mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	settings change + flashbang mode fixes
This commit is contained in:
		
							parent
							
								
									db04dd2781
								
							
						
					
					
						commit
						162ce53a82
					
				
					 37 changed files with 357 additions and 604 deletions
				
			
		| 
						 | 
				
			
			@ -53,7 +53,6 @@ object APIHolder {
 | 
			
		|||
            PelisflixProvider(),
 | 
			
		||||
            SeriesflixProvider(),
 | 
			
		||||
            IHaveNoTvProvider(), // Documentaries provider
 | 
			
		||||
            LookMovieProvider(), // RECAPTCHA (Please allow up to 5 seconds...)
 | 
			
		||||
            VMoveeProvider(),
 | 
			
		||||
            AllMoviesForYouProvider(),
 | 
			
		||||
            VidEmbedProvider(),
 | 
			
		||||
| 
						 | 
				
			
			@ -472,12 +471,6 @@ fun base64Encode(array: ByteArray): String {
 | 
			
		|||
 | 
			
		||||
class ErrorLoadingException(message: String? = null) : Exception(message)
 | 
			
		||||
 | 
			
		||||
fun parseRating(ratingString: String?): Int? {
 | 
			
		||||
    if (ratingString == null) return null
 | 
			
		||||
    val floatRating = ratingString.toFloatOrNull() ?: return null
 | 
			
		||||
    return (floatRating * 10).toInt()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun MainAPI.fixUrlNull(url: String?): String? {
 | 
			
		||||
    if (url.isNullOrEmpty()) {
 | 
			
		||||
        return null
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -137,6 +137,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
 | 
			
		|||
            R.id.navigation_settings_ui,
 | 
			
		||||
            R.id.navigation_settings_account,
 | 
			
		||||
            R.id.navigation_settings_lang,
 | 
			
		||||
            R.id.navigation_settings_general,
 | 
			
		||||
        ).contains(destination.id)
 | 
			
		||||
 | 
			
		||||
        val landscape = when (resources.configuration.orientation) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,298 +0,0 @@
 | 
			
		|||
package com.lagradost.cloudstream3.movieproviders
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty
 | 
			
		||||
import com.fasterxml.jackson.module.kotlin.readValue
 | 
			
		||||
import com.lagradost.cloudstream3.*
 | 
			
		||||
import com.lagradost.cloudstream3.APIHolder.unixTime
 | 
			
		||||
import com.lagradost.cloudstream3.extractors.M3u8Manifest
 | 
			
		||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
 | 
			
		||||
import com.lagradost.cloudstream3.utils.ExtractorLink
 | 
			
		||||
import com.lagradost.cloudstream3.utils.getQualityFromName
 | 
			
		||||
import org.jsoup.Jsoup
 | 
			
		||||
 | 
			
		||||
//BE AWARE THAT weboas.is is a clone of lookmovie
 | 
			
		||||
class LookMovieProvider : MainAPI() {
 | 
			
		||||
    override val hasQuickSearch = true
 | 
			
		||||
    override var name = "LookMovie"
 | 
			
		||||
    override var mainUrl = "https://lookmovie.io"
 | 
			
		||||
 | 
			
		||||
    override val supportedTypes = setOf(
 | 
			
		||||
        TvType.Movie,
 | 
			
		||||
        TvType.TvSeries,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LookMovieSearchResult(
 | 
			
		||||
        @JsonProperty("backdrop") val backdrop: String?,
 | 
			
		||||
        @JsonProperty("imdb_rating") val imdb_rating: String,
 | 
			
		||||
        @JsonProperty("poster") val poster: String?,
 | 
			
		||||
        @JsonProperty("slug") val slug: String,
 | 
			
		||||
        @JsonProperty("title") val title: String,
 | 
			
		||||
        @JsonProperty("year") val year: String?,
 | 
			
		||||
        //  @JsonProperty("flag_quality") val flag_quality: Int?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LookMovieTokenRoot(
 | 
			
		||||
        @JsonProperty("data") val data: LookMovieTokenResult?,
 | 
			
		||||
        @JsonProperty("success") val success: Boolean,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LookMovieTokenResult(
 | 
			
		||||
        @JsonProperty("accessToken") val accessToken: String,
 | 
			
		||||
        @JsonProperty("subtitles") val subtitles: List<LookMovieTokenSubtitle>?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LookMovieTokenSubtitle(
 | 
			
		||||
        @JsonProperty("language") val language: String,
 | 
			
		||||
        @JsonProperty("source") val source: String?,
 | 
			
		||||
        //@JsonProperty("source_id") val source_id: String,
 | 
			
		||||
        //@JsonProperty("kind") val kind: String,
 | 
			
		||||
        //@JsonProperty("id") val id: String,
 | 
			
		||||
        @JsonProperty("file") val file: String,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LookMovieSearchResultRoot(
 | 
			
		||||
        // @JsonProperty("per_page") val per_page: Int?,
 | 
			
		||||
        // @JsonProperty("total") val total: Int?,
 | 
			
		||||
        @JsonProperty("result") val result: List<LookMovieSearchResult>?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class LookMovieEpisode(
 | 
			
		||||
        @JsonProperty("title") var title: String,
 | 
			
		||||
        @JsonProperty("index") var index: String,
 | 
			
		||||
        @JsonProperty("episode") var episode: String,
 | 
			
		||||
        @JsonProperty("id_episode") var idEpisode: Int,
 | 
			
		||||
        @JsonProperty("season") var season: String,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    override suspend fun quickSearch(query: String): List<SearchResponse> {
 | 
			
		||||
        val movieUrl = "$mainUrl/api/v1/movies/search/?q=$query"
 | 
			
		||||
        val movieResponse = app.get(movieUrl).text
 | 
			
		||||
        val movies = mapper.readValue<LookMovieSearchResultRoot>(movieResponse).result
 | 
			
		||||
 | 
			
		||||
        val showsUrl = "$mainUrl/api/v1/shows/search/?q=$query"
 | 
			
		||||
        val showsResponse = app.get(showsUrl).text
 | 
			
		||||
        val shows = mapper.readValue<LookMovieSearchResultRoot>(showsResponse).result
 | 
			
		||||
 | 
			
		||||
        val returnValue = ArrayList<SearchResponse>()
 | 
			
		||||
        if (!movies.isNullOrEmpty()) {
 | 
			
		||||
            for (m in movies) {
 | 
			
		||||
                val url = "$mainUrl/movies/view/${m.slug}"
 | 
			
		||||
                returnValue.add(
 | 
			
		||||
                    MovieSearchResponse(
 | 
			
		||||
                        m.title,
 | 
			
		||||
                        url,
 | 
			
		||||
                        this.name,
 | 
			
		||||
                        TvType.Movie,
 | 
			
		||||
                        m.poster ?: m.backdrop,
 | 
			
		||||
                        m.year?.toIntOrNull()
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!shows.isNullOrEmpty()) {
 | 
			
		||||
            for (s in shows) {
 | 
			
		||||
                val url = "$mainUrl/shows/view/${s.slug}"
 | 
			
		||||
                returnValue.add(
 | 
			
		||||
                    MovieSearchResponse(
 | 
			
		||||
                        s.title,
 | 
			
		||||
                        url,
 | 
			
		||||
                        this.name,
 | 
			
		||||
                        TvType.TvSeries,
 | 
			
		||||
                        s.poster ?: s.backdrop,
 | 
			
		||||
                        s.year?.toIntOrNull()
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return returnValue
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun search(query: String): List<SearchResponse> {
 | 
			
		||||
        suspend fun search(query: String, isMovie: Boolean): List<SearchResponse> {
 | 
			
		||||
            val url = "$mainUrl/${if (isMovie) "movies" else "shows"}/search/?q=$query"
 | 
			
		||||
            val response = app.get(url).text
 | 
			
		||||
            val document = Jsoup.parse(response)
 | 
			
		||||
 | 
			
		||||
            val items = document.select("div.flex-wrap-movielist > div.movie-item-style-1")
 | 
			
		||||
            return items.map { item ->
 | 
			
		||||
                val titleHolder = item.selectFirst("> div.mv-item-infor > h6 > a")
 | 
			
		||||
                val href = fixUrl(titleHolder!!.attr("href"))
 | 
			
		||||
                val name = titleHolder.text()
 | 
			
		||||
                val posterHolder = item.selectFirst("> div.image__placeholder > a")
 | 
			
		||||
                val poster = posterHolder!!.selectFirst("> img")?.attr("data-src")
 | 
			
		||||
                val year = posterHolder.selectFirst("> p.year")?.text()?.toIntOrNull()
 | 
			
		||||
                if (isMovie) {
 | 
			
		||||
                    MovieSearchResponse(
 | 
			
		||||
                        name, href, this.name, TvType.Movie, poster, year
 | 
			
		||||
                    )
 | 
			
		||||
                } else
 | 
			
		||||
                    TvSeriesSearchResponse(
 | 
			
		||||
                        name, href, this.name, TvType.TvSeries, poster, year, null
 | 
			
		||||
                    )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val movieList = search(query, true).toMutableList()
 | 
			
		||||
        val seriesList = search(query, false)
 | 
			
		||||
        movieList.addAll(seriesList)
 | 
			
		||||
        return movieList
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data class LookMovieLinkLoad(val url: String, val extraUrl: String, val isMovie: Boolean)
 | 
			
		||||
 | 
			
		||||
    private fun addSubtitles(
 | 
			
		||||
        subs: List<LookMovieTokenSubtitle>?,
 | 
			
		||||
        subtitleCallback: (SubtitleFile) -> Unit
 | 
			
		||||
    ) {
 | 
			
		||||
        if (subs == null) return
 | 
			
		||||
        subs.forEach {
 | 
			
		||||
            if (it.file.endsWith(".vtt"))
 | 
			
		||||
                subtitleCallback.invoke(SubtitleFile(it.language, fixUrl(it.file)))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun loadCurrentLinks(url: String, callback: (ExtractorLink) -> Unit) {
 | 
			
		||||
        val response = app.get(url.replace("\$unixtime", unixTime.toString())).text
 | 
			
		||||
        M3u8Manifest.extractLinks(response).forEach {
 | 
			
		||||
            callback.invoke(
 | 
			
		||||
                ExtractorLink(
 | 
			
		||||
                    this.name,
 | 
			
		||||
                    "${this.name} - ${it.second}",
 | 
			
		||||
                    fixUrl(it.first),
 | 
			
		||||
                    "",
 | 
			
		||||
                    getQualityFromName(it.second),
 | 
			
		||||
                    true
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun loadLinks(
 | 
			
		||||
        data: String,
 | 
			
		||||
        isCasting: Boolean,
 | 
			
		||||
        subtitleCallback: (SubtitleFile) -> Unit,
 | 
			
		||||
        callback: (ExtractorLink) -> Unit
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        val localData: LookMovieLinkLoad = mapper.readValue(data)
 | 
			
		||||
 | 
			
		||||
        if (localData.isMovie) {
 | 
			
		||||
            val tokenResponse = app.get(localData.url).text
 | 
			
		||||
            val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse)
 | 
			
		||||
            val accessToken = root.data?.accessToken ?: return false
 | 
			
		||||
            addSubtitles(root.data.subtitles, subtitleCallback)
 | 
			
		||||
            loadCurrentLinks(localData.extraUrl.replace("\$accessToken", accessToken), callback)
 | 
			
		||||
            return true
 | 
			
		||||
        } else {
 | 
			
		||||
            loadCurrentLinks(localData.url, callback)
 | 
			
		||||
            val subResponse = app.get(localData.extraUrl).text
 | 
			
		||||
            val subs = mapper.readValue<List<LookMovieTokenSubtitle>>(subResponse)
 | 
			
		||||
            addSubtitles(subs, subtitleCallback)
 | 
			
		||||
        }
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun load(url: String): LoadResponse? {
 | 
			
		||||
        val response = app.get(url).text
 | 
			
		||||
        val document = Jsoup.parse(response)
 | 
			
		||||
        val isMovie = url.contains("/movies/")
 | 
			
		||||
 | 
			
		||||
        val watchHeader = document.selectFirst("div.watch-heading")
 | 
			
		||||
        val nameHeader = watchHeader!!.selectFirst("> h1.bd-hd")
 | 
			
		||||
        val year = nameHeader!!.selectFirst("> span")?.text()?.toIntOrNull()
 | 
			
		||||
        val title = nameHeader.ownText()
 | 
			
		||||
        val rating =
 | 
			
		||||
            parseRating(watchHeader.selectFirst("> div.movie-rate > div.rate > p > span")!!.text())
 | 
			
		||||
        val imgElement = document.selectFirst("div.movie-img > p.movie__poster")
 | 
			
		||||
        val img = imgElement?.attr("style")
 | 
			
		||||
        var poster = if (img.isNullOrEmpty()) null else "url\\((.*?)\\)".toRegex()
 | 
			
		||||
            .find(img)?.groupValues?.get(1)
 | 
			
		||||
        if (poster.isNullOrEmpty()) poster = imgElement?.attr("data-background-image")
 | 
			
		||||
        val descript = document.selectFirst("p.description-short")!!.text()
 | 
			
		||||
        val id = "${if (isMovie) "id_movie" else "id_show"}:(.*?),".toRegex()
 | 
			
		||||
            .find(response)?.groupValues?.get(1)
 | 
			
		||||
            ?.replace(" ", "")
 | 
			
		||||
            ?: return null
 | 
			
		||||
        val realSlug = url.replace("$mainUrl/${if (isMovie) "movies" else "shows"}/view/", "")
 | 
			
		||||
        val realUrl =
 | 
			
		||||
            "$mainUrl/api/v1/security/${if (isMovie) "movie" else "show"}-access?${if (isMovie) "id_movie=$id" else "slug=$realSlug"}&token=1&sk=&step=1"
 | 
			
		||||
 | 
			
		||||
        if (isMovie) {
 | 
			
		||||
            val localData =
 | 
			
		||||
                LookMovieLinkLoad(
 | 
			
		||||
                    realUrl,
 | 
			
		||||
                    "$mainUrl/manifests/movies/json/$id/\$unixtime/\$accessToken/master.m3u8",
 | 
			
		||||
                    true
 | 
			
		||||
                ).toJson()
 | 
			
		||||
 | 
			
		||||
            return MovieLoadResponse(
 | 
			
		||||
                title,
 | 
			
		||||
                url,
 | 
			
		||||
                this.name,
 | 
			
		||||
                TvType.Movie,
 | 
			
		||||
                localData,
 | 
			
		||||
                poster,
 | 
			
		||||
                year,
 | 
			
		||||
                descript,
 | 
			
		||||
                rating
 | 
			
		||||
            )
 | 
			
		||||
        } else {
 | 
			
		||||
            val tokenResponse = app.get(realUrl).text
 | 
			
		||||
            val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse)
 | 
			
		||||
            val accessToken = root.data?.accessToken ?: return null
 | 
			
		||||
 | 
			
		||||
            val window =
 | 
			
		||||
                "window\\['show_storage'] =((.|\\n)*?<)".toRegex().find(response)?.groupValues?.get(
 | 
			
		||||
                    1
 | 
			
		||||
                )
 | 
			
		||||
                    ?: return null
 | 
			
		||||
            // val id = "id_show:(.*?),".toRegex().find(response.text)?.groupValues?.get(1) ?: return null
 | 
			
		||||
            val season = "seasons:.*\\[((.|\\n)*?)]".toRegex().find(window)?.groupValues?.get(1)
 | 
			
		||||
                ?: return null
 | 
			
		||||
 | 
			
		||||
            fun String.fixSeasonJson(replace: String): String {
 | 
			
		||||
                return this.replace("$replace:", "\"$replace\":")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val json = season
 | 
			
		||||
                .replace("\'", "\"")
 | 
			
		||||
                .fixSeasonJson("title")
 | 
			
		||||
                .fixSeasonJson("id_episode")
 | 
			
		||||
                .fixSeasonJson("episode")
 | 
			
		||||
                .fixSeasonJson("index")
 | 
			
		||||
                .fixSeasonJson("season")
 | 
			
		||||
            val realJson = "[" + json.substring(0, json.lastIndexOf(',')) + "]"
 | 
			
		||||
 | 
			
		||||
            val episodes = mapper.readValue<List<LookMovieEpisode>>(realJson).map {
 | 
			
		||||
                val localData =
 | 
			
		||||
                    LookMovieLinkLoad(
 | 
			
		||||
                        "$mainUrl/manifests/shows/json/$accessToken/\$unixtime/${it.idEpisode}/master.m3u8",
 | 
			
		||||
                        "https://lookmovie.io/api/v1/shows/episode-subtitles/?id_episode=${it.idEpisode}",
 | 
			
		||||
                        false
 | 
			
		||||
                    ).toJson()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                Episode(
 | 
			
		||||
                    localData,
 | 
			
		||||
                    it.title,
 | 
			
		||||
                    it.season.toIntOrNull(),
 | 
			
		||||
                    it.episode.toIntOrNull(),
 | 
			
		||||
                )
 | 
			
		||||
            }.toList()
 | 
			
		||||
 | 
			
		||||
            return TvSeriesLoadResponse(
 | 
			
		||||
                title,
 | 
			
		||||
                url,
 | 
			
		||||
                this.name,
 | 
			
		||||
                TvType.TvSeries,
 | 
			
		||||
                ArrayList(episodes),
 | 
			
		||||
                poster,
 | 
			
		||||
                year,
 | 
			
		||||
                descript,
 | 
			
		||||
                null,
 | 
			
		||||
                rating
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1786,7 +1786,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
 | 
			
		|||
                        }
 | 
			
		||||
                        result_description.setOnClickListener {
 | 
			
		||||
                            val builder: AlertDialog.Builder =
 | 
			
		||||
                                AlertDialog.Builder(requireContext())
 | 
			
		||||
                                AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom)
 | 
			
		||||
                            builder.setMessage(d.plot)
 | 
			
		||||
                                .setTitle(if (d.type == TvType.Torrent) R.string.torrent_plot else R.string.result_plot)
 | 
			
		||||
                                .show()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,6 @@ import androidx.appcompat.app.AlertDialog
 | 
			
		|||
import androidx.core.view.isGone
 | 
			
		||||
import androidx.core.view.isVisible
 | 
			
		||||
import androidx.preference.PreferenceFragmentCompat
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import com.lagradost.cloudstream3.CommonActivity.showToast
 | 
			
		||||
import com.lagradost.cloudstream3.R
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +23,6 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSub
 | 
			
		|||
import com.lagradost.cloudstream3.syncproviders.AuthAPI
 | 
			
		||||
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
 | 
			
		||||
import com.lagradost.cloudstream3.syncproviders.OAuth2API
 | 
			
		||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.beneneCount
 | 
			
		||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
 | 
			
		||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
 | 
			
		||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +36,7 @@ import kotlinx.android.synthetic.main.add_account_input.*
 | 
			
		|||
class SettingsAccount : PreferenceFragmentCompat() {
 | 
			
		||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState)
 | 
			
		||||
        setUpToolbar(R.string.category_credits)
 | 
			
		||||
        setUpToolbar(R.string.category_account)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun showLoginInfo(api: AccountManager, info: AuthAPI.LoginInfo) {
 | 
			
		||||
| 
						 | 
				
			
			@ -179,16 +177,7 @@ class SettingsAccount : PreferenceFragmentCompat() {
 | 
			
		|||
 | 
			
		||||
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
 | 
			
		||||
        hideKeyboard()
 | 
			
		||||
        setPreferencesFromResource(R.xml.settings_credits_account, rootKey)
 | 
			
		||||
        val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
 | 
			
		||||
 | 
			
		||||
        getPref(R.string.legal_notice_key)?.setOnPreferenceClickListener {
 | 
			
		||||
            val builder: AlertDialog.Builder = AlertDialog.Builder(it.context)
 | 
			
		||||
            builder.setTitle(R.string.legal_notice)
 | 
			
		||||
            builder.setMessage(R.string.legal_notice_text)
 | 
			
		||||
            builder.show()
 | 
			
		||||
            return@setOnPreferenceClickListener true
 | 
			
		||||
        }
 | 
			
		||||
        setPreferencesFromResource(R.xml.settings_account, rootKey)
 | 
			
		||||
 | 
			
		||||
        val syncApis =
 | 
			
		||||
            listOf(
 | 
			
		||||
| 
						 | 
				
			
			@ -213,33 +202,5 @@ class SettingsAccount : PreferenceFragmentCompat() {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            beneneCount = settingsManager.getInt(getString(R.string.benene_count), 0)
 | 
			
		||||
            getPref(R.string.benene_count)?.let { pref ->
 | 
			
		||||
                pref.summary =
 | 
			
		||||
                    if (beneneCount <= 0) getString(R.string.benene_count_text_none) else getString(
 | 
			
		||||
                        R.string.benene_count_text
 | 
			
		||||
                    ).format(
 | 
			
		||||
                        beneneCount
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                pref.setOnPreferenceClickListener {
 | 
			
		||||
                    try {
 | 
			
		||||
                        beneneCount++
 | 
			
		||||
                        settingsManager.edit().putInt(getString(R.string.benene_count), beneneCount)
 | 
			
		||||
                            .apply()
 | 
			
		||||
                        it.summary = getString(R.string.benene_count_text).format(beneneCount)
 | 
			
		||||
                    } catch (e: Exception) {
 | 
			
		||||
                        logError(e)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return@setOnPreferenceClickListener true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            e.printStackTrace()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ class SettingsFragment : Fragment() {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun PreferenceFragmentCompat?.setUpToolbar(@StringRes title : Int) {
 | 
			
		||||
        fun PreferenceFragmentCompat?.setUpToolbar(@StringRes title: Int) {
 | 
			
		||||
            if (this == null) return
 | 
			
		||||
            settings_toolbar?.apply {
 | 
			
		||||
                setTitle(title)
 | 
			
		||||
| 
						 | 
				
			
			@ -131,6 +131,7 @@ class SettingsFragment : Fragment() {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        listOf(
 | 
			
		||||
            Pair(settings_general, R.id.action_navigation_settings_to_navigation_settings_general),
 | 
			
		||||
            Pair(settings_player, R.id.action_navigation_settings_to_navigation_settings_player),
 | 
			
		||||
            Pair(settings_credits, R.id.action_navigation_settings_to_navigation_settings_account),
 | 
			
		||||
            Pair(settings_ui, R.id.action_navigation_settings_to_navigation_settings_ui),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,182 @@
 | 
			
		|||
package com.lagradost.cloudstream3.ui.settings
 | 
			
		||||
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.os.Environment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import androidx.activity.result.contract.ActivityResultContracts
 | 
			
		||||
import androidx.appcompat.app.AlertDialog
 | 
			
		||||
import androidx.preference.PreferenceFragmentCompat
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import com.lagradost.cloudstream3.AcraApplication
 | 
			
		||||
import com.lagradost.cloudstream3.R
 | 
			
		||||
import com.lagradost.cloudstream3.app
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.logError
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
 | 
			
		||||
import com.lagradost.cloudstream3.network.initClient
 | 
			
		||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
 | 
			
		||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
 | 
			
		||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
 | 
			
		||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
 | 
			
		||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager
 | 
			
		||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
class SettingsGeneral : PreferenceFragmentCompat() {
 | 
			
		||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState)
 | 
			
		||||
        setUpToolbar(R.string.category_general)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Open file picker
 | 
			
		||||
    private val pathPicker =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
 | 
			
		||||
            // It lies, it can be null if file manager quits.
 | 
			
		||||
            if (uri == null) return@registerForActivityResult
 | 
			
		||||
            val context = context ?: AcraApplication.context ?: return@registerForActivityResult
 | 
			
		||||
            // RW perms for the path
 | 
			
		||||
            val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
 | 
			
		||||
                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
			
		||||
 | 
			
		||||
            context.contentResolver.takePersistableUriPermission(uri, flags)
 | 
			
		||||
 | 
			
		||||
            val file = UniFile.fromUri(context, uri)
 | 
			
		||||
            println("Selected URI path: $uri - Full path: ${file.filePath}")
 | 
			
		||||
 | 
			
		||||
            // Stores the real URI using download_path_key
 | 
			
		||||
            // Important that the URI is stored instead of filepath due to permissions.
 | 
			
		||||
            PreferenceManager.getDefaultSharedPreferences(context)
 | 
			
		||||
                .edit().putString(getString(R.string.download_path_key), uri.toString()).apply()
 | 
			
		||||
 | 
			
		||||
            // From URI -> File path
 | 
			
		||||
            // File path here is purely for cosmetic purposes in settings
 | 
			
		||||
            (file.filePath ?: uri.toString()).let {
 | 
			
		||||
                PreferenceManager.getDefaultSharedPreferences(context)
 | 
			
		||||
                    .edit().putString(getString(R.string.download_path_pref), it).apply()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
 | 
			
		||||
        hideKeyboard()
 | 
			
		||||
        setPreferencesFromResource(R.xml.settins_general, rootKey)
 | 
			
		||||
        val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
 | 
			
		||||
 | 
			
		||||
        getPref(R.string.legal_notice_key)?.setOnPreferenceClickListener {
 | 
			
		||||
            val builder: AlertDialog.Builder =
 | 
			
		||||
                AlertDialog.Builder(it.context, R.style.AlertDialogCustom)
 | 
			
		||||
            builder.setTitle(R.string.legal_notice)
 | 
			
		||||
            builder.setMessage(R.string.legal_notice_text)
 | 
			
		||||
            builder.show()
 | 
			
		||||
            return@setOnPreferenceClickListener true
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        getPref(R.string.dns_key)?.setOnPreferenceClickListener {
 | 
			
		||||
            val prefNames = resources.getStringArray(R.array.dns_pref)
 | 
			
		||||
            val prefValues = resources.getIntArray(R.array.dns_pref_values)
 | 
			
		||||
 | 
			
		||||
            val currentDns =
 | 
			
		||||
                settingsManager.getInt(getString(R.string.dns_pref), 0)
 | 
			
		||||
 | 
			
		||||
            activity?.showBottomDialog(
 | 
			
		||||
                prefNames.toList(),
 | 
			
		||||
                prefValues.indexOf(currentDns),
 | 
			
		||||
                getString(R.string.dns_pref),
 | 
			
		||||
                true,
 | 
			
		||||
                {}) {
 | 
			
		||||
                settingsManager.edit().putInt(getString(R.string.dns_pref), prefValues[it]).apply()
 | 
			
		||||
                (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) }
 | 
			
		||||
            }
 | 
			
		||||
            return@setOnPreferenceClickListener true
 | 
			
		||||
        }
 | 
			
		||||
        fun getDownloadDirs(): List<String> {
 | 
			
		||||
            return normalSafeApiCall {
 | 
			
		||||
                val defaultDir = VideoDownloadManager.getDownloadDir()?.filePath
 | 
			
		||||
 | 
			
		||||
                // app_name_download_path = Cloudstream and does not change depending on release.
 | 
			
		||||
                // DOES NOT WORK ON SCOPED STORAGE.
 | 
			
		||||
                val secondaryDir =
 | 
			
		||||
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath +
 | 
			
		||||
                            File.separator + resources.getString(R.string.app_name_download_path)
 | 
			
		||||
                val first = listOf(defaultDir, secondaryDir)
 | 
			
		||||
                (try {
 | 
			
		||||
                    val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second }
 | 
			
		||||
 | 
			
		||||
                    (first +
 | 
			
		||||
                            requireContext().getExternalFilesDirs("").mapNotNull { it.path } +
 | 
			
		||||
                            currentDir)
 | 
			
		||||
                } catch (e: Exception) {
 | 
			
		||||
                    first
 | 
			
		||||
                }).filterNotNull().distinct()
 | 
			
		||||
            } ?: emptyList()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getPref(R.string.download_path_key)?.setOnPreferenceClickListener {
 | 
			
		||||
            val dirs = getDownloadDirs()
 | 
			
		||||
 | 
			
		||||
            val currentDir =
 | 
			
		||||
                settingsManager.getString(getString(R.string.download_path_pref), null)
 | 
			
		||||
                    ?: VideoDownloadManager.getDownloadDir().toString()
 | 
			
		||||
 | 
			
		||||
            activity?.showBottomDialog(
 | 
			
		||||
                dirs + listOf("Custom"),
 | 
			
		||||
                dirs.indexOf(currentDir),
 | 
			
		||||
                getString(R.string.download_path_pref),
 | 
			
		||||
                true,
 | 
			
		||||
                {}) {
 | 
			
		||||
                // Last = custom
 | 
			
		||||
                if (it == dirs.size) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        pathPicker.launch(Uri.EMPTY)
 | 
			
		||||
                    } catch (e: Exception) {
 | 
			
		||||
                        logError(e)
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Sets both visual and actual paths.
 | 
			
		||||
                    // key = used path
 | 
			
		||||
                    // pref = visual path
 | 
			
		||||
                    settingsManager.edit()
 | 
			
		||||
                        .putString(getString(R.string.download_path_key), dirs[it]).apply()
 | 
			
		||||
                    settingsManager.edit()
 | 
			
		||||
                        .putString(getString(R.string.download_path_pref), dirs[it]).apply()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return@setOnPreferenceClickListener true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            SettingsFragment.beneneCount =
 | 
			
		||||
                settingsManager.getInt(getString(R.string.benene_count), 0)
 | 
			
		||||
            getPref(R.string.benene_count)?.let { pref ->
 | 
			
		||||
                pref.summary =
 | 
			
		||||
                    if (SettingsFragment.beneneCount <= 0) getString(R.string.benene_count_text_none) else getString(
 | 
			
		||||
                        R.string.benene_count_text
 | 
			
		||||
                    ).format(
 | 
			
		||||
                        SettingsFragment.beneneCount
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                pref.setOnPreferenceClickListener {
 | 
			
		||||
                    try {
 | 
			
		||||
                        SettingsFragment.beneneCount++
 | 
			
		||||
                        settingsManager.edit().putInt(
 | 
			
		||||
                            getString(R.string.benene_count),
 | 
			
		||||
                            SettingsFragment.beneneCount
 | 
			
		||||
                        )
 | 
			
		||||
                            .apply()
 | 
			
		||||
                        it.summary =
 | 
			
		||||
                            getString(R.string.benene_count_text).format(SettingsFragment.beneneCount)
 | 
			
		||||
                    } catch (e: Exception) {
 | 
			
		||||
                        logError(e)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return@setOnPreferenceClickListener true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            e.printStackTrace()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,21 +1,11 @@
 | 
			
		|||
package com.lagradost.cloudstream3.ui.settings
 | 
			
		||||
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.os.Environment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import androidx.activity.result.contract.ActivityResultContracts
 | 
			
		||||
import androidx.preference.PreferenceFragmentCompat
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import com.lagradost.cloudstream3.AcraApplication
 | 
			
		||||
import com.lagradost.cloudstream3.R
 | 
			
		||||
import com.lagradost.cloudstream3.app
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.logError
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
 | 
			
		||||
import com.lagradost.cloudstream3.network.initClient
 | 
			
		||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize
 | 
			
		||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
 | 
			
		||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
 | 
			
		||||
| 
						 | 
				
			
			@ -25,43 +15,12 @@ import com.lagradost.cloudstream3.utils.Qualities
 | 
			
		|||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
 | 
			
		||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
 | 
			
		||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
 | 
			
		||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager
 | 
			
		||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
class SettingsPlayer : PreferenceFragmentCompat() {
 | 
			
		||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState)
 | 
			
		||||
        setUpToolbar(R.string.category_player)
 | 
			
		||||
    }
 | 
			
		||||
    // Open file picker
 | 
			
		||||
    private val pathPicker =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
 | 
			
		||||
            // It lies, it can be null if file manager quits.
 | 
			
		||||
            if (uri == null) return@registerForActivityResult
 | 
			
		||||
            val context = context ?: AcraApplication.context ?: return@registerForActivityResult
 | 
			
		||||
            // RW perms for the path
 | 
			
		||||
            val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
 | 
			
		||||
                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
			
		||||
 | 
			
		||||
            context.contentResolver.takePersistableUriPermission(uri, flags)
 | 
			
		||||
 | 
			
		||||
            val file = UniFile.fromUri(context, uri)
 | 
			
		||||
            println("Selected URI path: $uri - Full path: ${file.filePath}")
 | 
			
		||||
 | 
			
		||||
            // Stores the real URI using download_path_key
 | 
			
		||||
            // Important that the URI is stored instead of filepath due to permissions.
 | 
			
		||||
            PreferenceManager.getDefaultSharedPreferences(context)
 | 
			
		||||
                .edit().putString(getString(R.string.download_path_key), uri.toString()).apply()
 | 
			
		||||
 | 
			
		||||
            // From URI -> File path
 | 
			
		||||
            // File path here is purely for cosmetic purposes in settings
 | 
			
		||||
            (file.filePath ?: uri.toString()).let {
 | 
			
		||||
                PreferenceManager.getDefaultSharedPreferences(context)
 | 
			
		||||
                    .edit().putString(getString(R.string.download_path_pref), it).apply()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
 | 
			
		||||
        hideKeyboard()
 | 
			
		||||
        setPreferencesFromResource(R.xml.settings_player, rootKey)
 | 
			
		||||
| 
						 | 
				
			
			@ -86,24 +45,6 @@ class SettingsPlayer : PreferenceFragmentCompat() {
 | 
			
		|||
            }
 | 
			
		||||
            return@setOnPreferenceClickListener true
 | 
			
		||||
        }
 | 
			
		||||
        getPref(R.string.dns_key)?.setOnPreferenceClickListener {
 | 
			
		||||
            val prefNames = resources.getStringArray(R.array.dns_pref)
 | 
			
		||||
            val prefValues = resources.getIntArray(R.array.dns_pref_values)
 | 
			
		||||
 | 
			
		||||
            val currentDns =
 | 
			
		||||
                settingsManager.getInt(getString(R.string.dns_pref), 0)
 | 
			
		||||
 | 
			
		||||
            activity?.showBottomDialog(
 | 
			
		||||
                prefNames.toList(),
 | 
			
		||||
                prefValues.indexOf(currentDns),
 | 
			
		||||
                getString(R.string.dns_pref),
 | 
			
		||||
                true,
 | 
			
		||||
                {}) {
 | 
			
		||||
                settingsManager.edit().putInt(getString(R.string.dns_pref), prefValues[it]).apply()
 | 
			
		||||
                (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) }
 | 
			
		||||
            }
 | 
			
		||||
            return@setOnPreferenceClickListener true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getPref(R.string.prefer_limit_title_key)?.setOnPreferenceClickListener {
 | 
			
		||||
            val prefNames = resources.getStringArray(R.array.limit_title_pref_names)
 | 
			
		||||
| 
						 | 
				
			
			@ -242,60 +183,6 @@ class SettingsPlayer : PreferenceFragmentCompat() {
 | 
			
		|||
                return@setOnPreferenceClickListener true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        fun getDownloadDirs(): List<String> {
 | 
			
		||||
            return normalSafeApiCall {
 | 
			
		||||
                val defaultDir = VideoDownloadManager.getDownloadDir()?.filePath
 | 
			
		||||
 | 
			
		||||
                // app_name_download_path = Cloudstream and does not change depending on release.
 | 
			
		||||
                // DOES NOT WORK ON SCOPED STORAGE.
 | 
			
		||||
                val secondaryDir =
 | 
			
		||||
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath +
 | 
			
		||||
                            File.separator + resources.getString(R.string.app_name_download_path)
 | 
			
		||||
                val first = listOf(defaultDir, secondaryDir)
 | 
			
		||||
                (try {
 | 
			
		||||
                    val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second }
 | 
			
		||||
 | 
			
		||||
                    (first +
 | 
			
		||||
                            requireContext().getExternalFilesDirs("").mapNotNull { it.path } +
 | 
			
		||||
                            currentDir)
 | 
			
		||||
                } catch (e: Exception) {
 | 
			
		||||
                    first
 | 
			
		||||
                }).filterNotNull().distinct()
 | 
			
		||||
            } ?: emptyList()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getPref(R.string.download_path_key)?.setOnPreferenceClickListener {
 | 
			
		||||
            val dirs = getDownloadDirs()
 | 
			
		||||
 | 
			
		||||
            val currentDir =
 | 
			
		||||
                settingsManager.getString(getString(R.string.download_path_pref), null)
 | 
			
		||||
                    ?: VideoDownloadManager.getDownloadDir().toString()
 | 
			
		||||
 | 
			
		||||
            activity?.showBottomDialog(
 | 
			
		||||
                dirs + listOf("Custom"),
 | 
			
		||||
                dirs.indexOf(currentDir),
 | 
			
		||||
                getString(R.string.download_path_pref),
 | 
			
		||||
                true,
 | 
			
		||||
                {}) {
 | 
			
		||||
                // Last = custom
 | 
			
		||||
                if (it == dirs.size) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        pathPicker.launch(Uri.EMPTY)
 | 
			
		||||
                    } catch (e: Exception) {
 | 
			
		||||
                        logError(e)
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Sets both visual and actual paths.
 | 
			
		||||
                    // key = used path
 | 
			
		||||
                    // pref = visual path
 | 
			
		||||
                    settingsManager.edit()
 | 
			
		||||
                        .putString(getString(R.string.download_path_key), dirs[it]).apply()
 | 
			
		||||
                    settingsManager.edit()
 | 
			
		||||
                        .putString(getString(R.string.download_path_pref), dirs[it]).apply()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return@setOnPreferenceClickListener true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +1,11 @@
 | 
			
		|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
        android:width="24dp"
 | 
			
		||||
        android:height="24dp"
 | 
			
		||||
        android:viewportWidth="24"
 | 
			
		||||
        android:viewportHeight="24"
 | 
			
		||||
        app:tint="?attr/white">
 | 
			
		||||
  <path
 | 
			
		||||
      android:fillColor="@android:color/white"
 | 
			
		||||
      android:pathData="M3,3v8h8L11,3L3,3zM9,9L5,9L5,5h4v4zM3,13v8h8v-8L3,13zM9,19L5,19v-4h4v4zM13,3v8h8L21,3h-8zM19,9h-4L15,5h4v4zM13,13v8h8v-8h-8zM19,19h-4v-4h4v4z"
 | 
			
		||||
      android:fillType="evenOdd"/>
 | 
			
		||||
        android:tint="?attr/white">
 | 
			
		||||
    <path
 | 
			
		||||
            android:fillColor="@android:color/white"
 | 
			
		||||
            android:pathData="M3,3v8h8L11,3L3,3zM9,9L5,9L5,5h4v4zM3,13v8h8v-8L3,13zM9,19L5,19v-4h4v4zM13,3v8h8L21,3h-8zM19,9h-4L15,5h4v4zM13,13v8h8v-8h-8zM19,19h-4v-4h4v4z"
 | 
			
		||||
            android:fillType="evenOdd" />
 | 
			
		||||
</vector>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,7 @@
 | 
			
		|||
            android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
        <EditText
 | 
			
		||||
                android:textColorHint="?attr/grayTextColor"
 | 
			
		||||
                android:hint="@string/example_username"
 | 
			
		||||
                android:autofillHints="username"
 | 
			
		||||
                android:id="@+id/login_username_input"
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +58,7 @@
 | 
			
		|||
                tools:ignore="LabelFor" />
 | 
			
		||||
 | 
			
		||||
        <EditText
 | 
			
		||||
                android:textColorHint="?attr/grayTextColor"
 | 
			
		||||
                android:autofillHints="emailAddress"
 | 
			
		||||
                android:hint="@string/example_email"
 | 
			
		||||
                android:id="@+id/login_email_input"
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +74,7 @@
 | 
			
		|||
                tools:ignore="LabelFor" />
 | 
			
		||||
 | 
			
		||||
        <EditText
 | 
			
		||||
                android:textColorHint="?attr/grayTextColor"
 | 
			
		||||
                android:hint="@string/example_ip"
 | 
			
		||||
                android:id="@+id/login_server_input"
 | 
			
		||||
                android:nextFocusRight="@id/cancel_btt"
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +88,7 @@
 | 
			
		|||
                tools:ignore="LabelFor" />
 | 
			
		||||
 | 
			
		||||
        <EditText
 | 
			
		||||
                android:textColorHint="?attr/grayTextColor"
 | 
			
		||||
                android:hint="@string/example_password"
 | 
			
		||||
                android:id="@+id/login_password_input"
 | 
			
		||||
                android:nextFocusRight="@id/cancel_btt"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,7 +117,7 @@
 | 
			
		|||
                android:layout_gravity="bottom"
 | 
			
		||||
                android:paddingBottom="5dp"
 | 
			
		||||
                android:paddingTop="5dp"
 | 
			
		||||
                android:textColor="@color/textColor"
 | 
			
		||||
                android:textColor="?attr/textColor"
 | 
			
		||||
                android:id="@+id/imageText"
 | 
			
		||||
                android:minLines="2"
 | 
			
		||||
                android:maxLines="2"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,8 +52,15 @@
 | 
			
		|||
                        android:layout_width="wrap_content"
 | 
			
		||||
                        android:layout_height="wrap_content" />
 | 
			
		||||
            </LinearLayout>
 | 
			
		||||
 | 
			
		||||
            <TextView
 | 
			
		||||
                    android:nextFocusDown="@id/settings_player"
 | 
			
		||||
 | 
			
		||||
                    android:id="@+id/settings_general"
 | 
			
		||||
                    style="@style/SettingsItem"
 | 
			
		||||
                    android:text="@string/category_general" />
 | 
			
		||||
            
 | 
			
		||||
            <TextView
 | 
			
		||||
                    android:nextFocusUp="@id/settings_general"
 | 
			
		||||
                    android:nextFocusDown="@id/settings_lang"
 | 
			
		||||
 | 
			
		||||
                    android:id="@+id/settings_player"
 | 
			
		||||
| 
						 | 
				
			
			@ -89,7 +96,7 @@
 | 
			
		|||
 | 
			
		||||
                    android:id="@+id/settings_credits"
 | 
			
		||||
                    style="@style/SettingsItem"
 | 
			
		||||
                    android:text="@string/category_credits" />
 | 
			
		||||
                    android:text="@string/category_account" />
 | 
			
		||||
 | 
			
		||||
            <TextView
 | 
			
		||||
                    android:padding="10dp"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,17 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
    android:orientation="vertical"
 | 
			
		||||
    android:layout_width="wrap_content"
 | 
			
		||||
    android:layout_height="wrap_content"
 | 
			
		||||
    android:background="?attr/primaryBlackBackground" >
 | 
			
		||||
    <ImageView
 | 
			
		||||
        android:id="@+id/imgPoster"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:orientation="vertical"
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:scaleType="fitCenter"
 | 
			
		||||
        android:adjustViewBounds="true"
 | 
			
		||||
        android:src="@drawable/default_cover"
 | 
			
		||||
        android:background="#fffff0"
 | 
			
		||||
        android:contentDescription="@string/poster_image"
 | 
			
		||||
        />
 | 
			
		||||
        android:background="?attr/primaryBlackBackground">
 | 
			
		||||
 | 
			
		||||
    <ImageView
 | 
			
		||||
            android:id="@+id/imgPoster"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:scaleType="fitCenter"
 | 
			
		||||
            android:adjustViewBounds="true"
 | 
			
		||||
            android:src="@drawable/default_cover"
 | 
			
		||||
            android:background="#fffff0"
 | 
			
		||||
            android:contentDescription="@string/poster_image" />
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +44,7 @@
 | 
			
		|||
                        app:tint="?attr/textColor" />
 | 
			
		||||
 | 
			
		||||
                <EditText
 | 
			
		||||
                        android:textColorHint="?attr/grayTextColor"
 | 
			
		||||
                        android:id="@+id/result_sync_current_episodes"
 | 
			
		||||
                        style="@style/AppEditStyle"
 | 
			
		||||
                        tools:hint="20"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,70 +11,70 @@
 | 
			
		|||
        android:clickable="true"
 | 
			
		||||
        android:id="@+id/search_result_root">
 | 
			
		||||
 | 
			
		||||
        <androidx.cardview.widget.CardView
 | 
			
		||||
                android:layout_margin="2dp"
 | 
			
		||||
    <androidx.cardview.widget.CardView
 | 
			
		||||
            android:layout_margin="2dp"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="match_parent"
 | 
			
		||||
            android:layout_marginBottom="2dp"
 | 
			
		||||
            android:elevation="10dp"
 | 
			
		||||
            app:cardCornerRadius="@dimen/rounded_image_radius"
 | 
			
		||||
            android:id="@+id/background_card"
 | 
			
		||||
            app:cardBackgroundColor="?attr/primaryGrayBackground">
 | 
			
		||||
 | 
			
		||||
        <ImageView
 | 
			
		||||
                android:id="@+id/imageView"
 | 
			
		||||
                tools:src="@drawable/example_poster"
 | 
			
		||||
 | 
			
		||||
                android:duplicateParentState="true"
 | 
			
		||||
                android:scaleType="centerCrop"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="match_parent"
 | 
			
		||||
                android:layout_marginBottom="2dp"
 | 
			
		||||
                android:elevation="10dp"
 | 
			
		||||
                app:cardCornerRadius="@dimen/rounded_image_radius"
 | 
			
		||||
                android:id="@+id/background_card"
 | 
			
		||||
                app:cardBackgroundColor="?attr/primaryGrayBackground">
 | 
			
		||||
 | 
			
		||||
            <ImageView
 | 
			
		||||
                    android:id="@+id/imageView"
 | 
			
		||||
                    tools:src="@drawable/example_poster"
 | 
			
		||||
 | 
			
		||||
                    android:duplicateParentState="true"
 | 
			
		||||
                    android:scaleType="centerCrop"
 | 
			
		||||
                    android:layout_width="match_parent"
 | 
			
		||||
                    android:layout_height="match_parent"
 | 
			
		||||
                    android:foreground="?android:attr/selectableItemBackgroundBorderless"
 | 
			
		||||
                    android:contentDescription="@string/search_poster_img_des" />
 | 
			
		||||
 | 
			
		||||
            <TextView
 | 
			
		||||
                    tools:text="@string/quality_hd"
 | 
			
		||||
                    android:id="@+id/text_quality"
 | 
			
		||||
                    android:textColor="@color/textColor"
 | 
			
		||||
                    style="@style/SearchBox"
 | 
			
		||||
                    android:background="@drawable/type_bg_color" />
 | 
			
		||||
 | 
			
		||||
            <LinearLayout
 | 
			
		||||
                    android:orientation="vertical"
 | 
			
		||||
                    android:layout_gravity="end"
 | 
			
		||||
                    android:layout_width="match_parent"
 | 
			
		||||
                    android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
                <TextView
 | 
			
		||||
                        android:text="@string/app_dubbed_text"
 | 
			
		||||
                        android:id="@+id/text_is_dub"
 | 
			
		||||
                        android:layout_gravity="end"
 | 
			
		||||
                        style="@style/SearchBox"
 | 
			
		||||
                        android:background="@drawable/dub_bg_color" />
 | 
			
		||||
 | 
			
		||||
                <TextView
 | 
			
		||||
                        android:id="@+id/text_is_sub"
 | 
			
		||||
                        android:text="@string/app_subbed_text"
 | 
			
		||||
                        android:layout_gravity="end"
 | 
			
		||||
                        style="@style/SearchBox"
 | 
			
		||||
                        android:background="@drawable/sub_bg_color" />
 | 
			
		||||
            </LinearLayout>
 | 
			
		||||
        </androidx.cardview.widget.CardView>
 | 
			
		||||
                android:foreground="?android:attr/selectableItemBackgroundBorderless"
 | 
			
		||||
                android:contentDescription="@string/search_poster_img_des" />
 | 
			
		||||
 | 
			
		||||
        <TextView
 | 
			
		||||
                tools:text="The Perfect Run\nThe Perfect Run"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:textSize="13sp"
 | 
			
		||||
                android:gravity="center"
 | 
			
		||||
                android:layout_gravity="bottom"
 | 
			
		||||
                android:paddingBottom="5dp"
 | 
			
		||||
                android:paddingTop="5dp"
 | 
			
		||||
                tools:text="@string/quality_hd"
 | 
			
		||||
                android:id="@+id/text_quality"
 | 
			
		||||
                android:textColor="@color/textColor"
 | 
			
		||||
                android:id="@+id/imageText"
 | 
			
		||||
                android:minLines="2"
 | 
			
		||||
                android:maxLines="2"
 | 
			
		||||
                android:paddingStart="5dp"
 | 
			
		||||
                android:paddingEnd="5dp"
 | 
			
		||||
                android:ellipsize="end" />
 | 
			
		||||
                style="@style/SearchBox"
 | 
			
		||||
                android:background="@drawable/type_bg_color" />
 | 
			
		||||
 | 
			
		||||
        <LinearLayout
 | 
			
		||||
                android:orientation="vertical"
 | 
			
		||||
                android:layout_gravity="end"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
            <TextView
 | 
			
		||||
                    android:text="@string/app_dubbed_text"
 | 
			
		||||
                    android:id="@+id/text_is_dub"
 | 
			
		||||
                    android:layout_gravity="end"
 | 
			
		||||
                    style="@style/SearchBox"
 | 
			
		||||
                    android:background="@drawable/dub_bg_color" />
 | 
			
		||||
 | 
			
		||||
            <TextView
 | 
			
		||||
                    android:id="@+id/text_is_sub"
 | 
			
		||||
                    android:text="@string/app_subbed_text"
 | 
			
		||||
                    android:layout_gravity="end"
 | 
			
		||||
                    style="@style/SearchBox"
 | 
			
		||||
                    android:background="@drawable/sub_bg_color" />
 | 
			
		||||
        </LinearLayout>
 | 
			
		||||
    </androidx.cardview.widget.CardView>
 | 
			
		||||
 | 
			
		||||
    <TextView
 | 
			
		||||
            tools:text="The Perfect Run\nThe Perfect Run"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:textSize="13sp"
 | 
			
		||||
            android:gravity="center"
 | 
			
		||||
            android:layout_gravity="bottom"
 | 
			
		||||
            android:paddingBottom="5dp"
 | 
			
		||||
            android:paddingTop="5dp"
 | 
			
		||||
            android:textColor="?attr/textColor"
 | 
			
		||||
            android:id="@+id/imageText"
 | 
			
		||||
            android:minLines="2"
 | 
			
		||||
            android:maxLines="2"
 | 
			
		||||
            android:paddingStart="5dp"
 | 
			
		||||
            android:paddingEnd="5dp"
 | 
			
		||||
            android:ellipsize="end" />
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +119,15 @@
 | 
			
		|||
            app:popEnterAnim="@anim/enter_anim"
 | 
			
		||||
            app:popExitAnim="@anim/exit_anim" />
 | 
			
		||||
 | 
			
		||||
    <fragment
 | 
			
		||||
            android:id="@+id/navigation_settings_general"
 | 
			
		||||
            android:label="@string/title_settings"
 | 
			
		||||
            android:name="com.lagradost.cloudstream3.ui.settings.SettingsGeneral"
 | 
			
		||||
            app:enterAnim="@anim/enter_anim"
 | 
			
		||||
            app:exitAnim="@anim/exit_anim"
 | 
			
		||||
            app:popEnterAnim="@anim/enter_anim"
 | 
			
		||||
            app:popExitAnim="@anim/exit_anim" />
 | 
			
		||||
 | 
			
		||||
    <fragment
 | 
			
		||||
            android:id="@+id/navigation_settings_lang"
 | 
			
		||||
            android:label="@string/title_settings"
 | 
			
		||||
| 
						 | 
				
			
			@ -290,6 +299,13 @@
 | 
			
		|||
                app:exitAnim="@anim/exit_anim"
 | 
			
		||||
                app:popEnterAnim="@anim/enter_anim"
 | 
			
		||||
                app:popExitAnim="@anim/exit_anim" />
 | 
			
		||||
        <action
 | 
			
		||||
                app:enterAnim="@anim/enter_anim"
 | 
			
		||||
                app:exitAnim="@anim/exit_anim"
 | 
			
		||||
                app:popEnterAnim="@anim/enter_anim"
 | 
			
		||||
                app:popExitAnim="@anim/exit_anim"
 | 
			
		||||
                android:id="@+id/action_navigation_settings_to_navigation_settings_general"
 | 
			
		||||
                app:destination="@id/navigation_settings_general" />
 | 
			
		||||
    </fragment>
 | 
			
		||||
 | 
			
		||||
    <fragment
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -246,7 +246,7 @@
 | 
			
		|||
    <string name="resize_fill">امتداد</string>
 | 
			
		||||
    <string name="resize_zoom">تكبير</string>
 | 
			
		||||
 | 
			
		||||
    <string name="general">عام</string>
 | 
			
		||||
    <string name="category_general">عام</string>
 | 
			
		||||
    <string name="provider_lang_settings">لغات الموفر</string>
 | 
			
		||||
    <string name="app_layout">واجهة التطبيق</string>	
 | 
			
		||||
    <string name="preferred_media_settings">النوع المفضل من المشاهدة</string>	
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -238,7 +238,7 @@
 | 
			
		|||
 
 | 
			
		||||
    <string name="search">Búsqueda</string> 
 | 
			
		||||
    <string name="category_nginx">Nginx</string> 
 | 
			
		||||
    <string name="category_credits">Créditos y cuenta</string> 
 | 
			
		||||
    <string name="category_account">Créditos y cuenta</string>
 | 
			
		||||
    <string name="category_updates">Actualizaciones y copia de seguridad</string> 
 | 
			
		||||
    <string name="nginx_credentials_title">Credencial Nginx</string> 
 | 
			
		||||
    <string name="nginx_credentials_summary">Tienes que usar el siguiente formato minombredeusuariogenial:micontraseñasegura123</string> 
 | 
			
		||||
| 
						 | 
				
			
			@ -408,7 +408,7 @@
 | 
			
		|||
        responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use 
 | 
			
		||||
        CloudStream 3 at your own risk. 
 | 
			
		||||
    </string> 
 | 
			
		||||
    <string name="general">General</string> 
 | 
			
		||||
    <string name="category_general">General</string>
 | 
			
		||||
    <string name="random_button_settings">Botón aleatorio</string> 
 | 
			
		||||
    <string name="random_button_settings_desc">Mostrar botón aleatorio en la página de inicio</string> 
 | 
			
		||||
    <string name="provider_lang_settings">Idiomas del proveedor</string> 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -193,7 +193,7 @@
 | 
			
		|||
    <string name="app_theme_settings">Thème de l\'application</string>
 | 
			
		||||
    <string name="player_speed_text_format" formatted="true">Vitesse (%.2fx)</string>
 | 
			
		||||
    <string name="use_system_brightness_settings">Utiliser la luminosité du système</string>
 | 
			
		||||
    <string name="general">Général</string>
 | 
			
		||||
    <string name="category_general">Général</string>
 | 
			
		||||
    <string name="dns_pref">DNS avec HTTPS</string>
 | 
			
		||||
    <string name="display_subbed_dubbed_settings">Afficher les animés en Anglais (Dub) / sous-titrés</string>
 | 
			
		||||
    <string name="phone_layout">Disposition en mode téléphone</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -188,7 +188,7 @@
 | 
			
		|||
 | 
			
		||||
    <string name="search">Cari</string>
 | 
			
		||||
    <string name="category_nginx">Nginx</string>
 | 
			
		||||
    <string name="category_credits">Kredit dan akun</string>
 | 
			
		||||
    <string name="category_account">Kredit dan akun</string>
 | 
			
		||||
    <string name="category_updates">Update dan cadangan</string>
 | 
			
		||||
    <string name="nginx_credentials_title">Kredensial Nginx</string> 
 | 
			
		||||
    <string name="nginx_credentials_summary">Kamu harus menggunakan format berikut namaku:passwordku123</string>
 | 
			
		||||
| 
						 | 
				
			
			@ -359,7 +359,7 @@
 | 
			
		|||
        responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use
 | 
			
		||||
        CloudStream 3 at your own risk.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="general">Umum</string>
 | 
			
		||||
    <string name="category_general">Umum</string>
 | 
			
		||||
    <string name="random_button_settings">Tombol Acak</string>
 | 
			
		||||
    <string name="random_button_settings_desc">Tampilkan tombol acak di Beranda</string>
 | 
			
		||||
    <string name="provider_lang_settings">Bahasa provider</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -181,7 +181,7 @@
 | 
			
		|||
 | 
			
		||||
    <string name="search">Cerca</string>
 | 
			
		||||
    <string name="category_nginx">Nginx</string>
 | 
			
		||||
    <string name="category_credits">Accounts e Crediti</string>
 | 
			
		||||
    <string name="category_account">Accounts e Crediti</string>
 | 
			
		||||
    <string name="category_updates">Aggiornamenti e Backup</string>
 | 
			
		||||
    <string name="nginx_credentials_title">Credenziali Nginx</string>
 | 
			
		||||
    <string name="nginx_credentials_summary">È necessario utilizzare il seguente formato mycoolusername:mysecurepassword123</string>
 | 
			
		||||
| 
						 | 
				
			
			@ -328,7 +328,7 @@
 | 
			
		|||
    <string name="resize_zoom">Zoom</string>
 | 
			
		||||
 | 
			
		||||
    <string name="legal_notice">Disclaimer</string>
 | 
			
		||||
    <string name="general">Generale</string>
 | 
			
		||||
    <string name="category_general">Generale</string>
 | 
			
		||||
    <string name="random_button_settings">Random</string>
 | 
			
		||||
    <string name="random_button_settings_desc">Mostra pulsante Random nella homepage</string>
 | 
			
		||||
    <string name="provider_lang_settings">Lingua provider</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -220,7 +220,7 @@
 | 
			
		|||
    <string name="resize_zoom">Зумирај</string>
 | 
			
		||||
 | 
			
		||||
    <string name="legal_notice">Disclaimer</string>
 | 
			
		||||
    <string name="general">Генерално</string>
 | 
			
		||||
    <string name="category_general">Генерално</string>
 | 
			
		||||
    <string name="provider_lang_settings">Јазици на провајдерите</string>
 | 
			
		||||
    <string name="app_layout">Распоред на апликацијата</string>
 | 
			
		||||
    <string name="preferred_media_settings">Претпочитани медиуми</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -188,7 +188,7 @@
 | 
			
		|||
    <string name="resize_zoom">uhuoh o   a ohahuhohoa hah</string>
 | 
			
		||||
    <string name="provider_lang_settings">ua hu ouo o aoau hah ah</string>
 | 
			
		||||
    <string name="legal_notice">ah huu  oouhhau aoaoaaohoo ha</string>
 | 
			
		||||
    <string name="general">a ahu uoo uoahuo   uo</string>
 | 
			
		||||
    <string name="category_general">a ahu uoo uoahuo   uo</string>
 | 
			
		||||
    <string name="app_layout">uo u ohouao</string>
 | 
			
		||||
    <string name="automatic">uuoouhh hhuhuuh ouhoaao hau aouo</string>
 | 
			
		||||
    <string name="tv_layout">uha uh  huo uooaah u</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -221,7 +221,7 @@
 | 
			
		|||
    <string name="resize_fit">Tilpass til skjermen</string>
 | 
			
		||||
    <string name="resize_fill">Tøye ut</string>
 | 
			
		||||
    <string name="resize_zoom">Zoome</string>
 | 
			
		||||
    <string name="general">Grunnleggende</string>
 | 
			
		||||
    <string name="category_general">Grunnleggende</string>
 | 
			
		||||
    <string name="provider_lang_settings">Leverandør Språk</string>
 | 
			
		||||
    <string name="app_layout">App Oppsett</string>
 | 
			
		||||
    <string name="automatic">Automatisk</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -270,7 +270,7 @@
 | 
			
		|||
    <string name="legal_notice">Zastrzeżenie</string>
 | 
			
		||||
 | 
			
		||||
    <string name="provider_lang_settings">Języki dostawców</string>
 | 
			
		||||
    <string name="general">Ogólne</string>
 | 
			
		||||
    <string name="category_general">Ogólne</string>
 | 
			
		||||
    <string name="app_layout">Układ aplikacji</string>
 | 
			
		||||
    <string name="preferred_media_settings">Preferowane media</string>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -258,7 +258,7 @@
 | 
			
		|||
       responsabilidade do usuário de evitar qualquer ação que possa violar as leis que regem sua localidade. Utilizar
 | 
			
		||||
       CloudStream 3 por sua própria conta e risco.
 | 
			
		||||
    </string>
 | 
			
		||||
	<string name="general">Geral</string>
 | 
			
		||||
	<string name="category_general">Geral</string>
 | 
			
		||||
    <string name="provider_lang_settings">Idiomas dos Origems</string>
 | 
			
		||||
    <string name="app_layout">Layout do App</string>
 | 
			
		||||
    <string name="preferred_media_settings">Mídia preferida</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -259,7 +259,7 @@
 | 
			
		|||
        responsabilitatea utilizatorului de a evita orice acțiune care ar putea încălca legile care guvernează localitatea sa. Utilizați
 | 
			
		||||
        CloudStream 3 pe propriul risc.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="general">Generală</string>
 | 
			
		||||
    <string name="category_general">Generală</string>
 | 
			
		||||
    <string name="provider_lang_settings">Limbile Furnizorului</string>
 | 
			
		||||
    <string name="app_layout">Aranjament-ul Aplicației</string>
 | 
			
		||||
    <string name="preferred_media_settings">Media Preferată</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -199,7 +199,7 @@
 | 
			
		|||
    <string name="resume">Återuppta</string>
 | 
			
		||||
    <string name="storage_error">Ett nerladdningsfel uppstod, kolla om appen har lagringsbehörigheter</string>
 | 
			
		||||
    <string name="watch_quality_pref">Föredragen videokvalitet</string>
 | 
			
		||||
    <string name="general">Allmänna Inställningar</string>
 | 
			
		||||
    <string name="category_general">Allmänna Inställningar</string>
 | 
			
		||||
    <string name="subs_font_size">Textstorlek</string>
 | 
			
		||||
    <string name="use_system_brightness_settings">Använd system ljusstyrka</string>
 | 
			
		||||
    <string name="use_system_brightness_settings_des">Använder systemets ljusstyrka instället för en svart överlaga</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -231,7 +231,7 @@
 | 
			
		|||
    <string name="resize_fill">Punan ang buong screen</string>
 | 
			
		||||
    <string name="resize_zoom">Zoom</string>
 | 
			
		||||
 | 
			
		||||
    <string name="general">Pangkalahatan</string>
 | 
			
		||||
    <string name="category_general">Pangkalahatan</string>
 | 
			
		||||
    <string name="provider_lang_settings">Lenggwahe ng Provider</string>
 | 
			
		||||
    <string name="app_layout">App Layout</string>
 | 
			
		||||
    <string name="automatic">Awtomatik</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -344,7 +344,7 @@
 | 
			
		|||
        responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use
 | 
			
		||||
        CloudStream 3 at your own risk.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="general">Genel</string>
 | 
			
		||||
    <string name="category_general">Genel</string>
 | 
			
		||||
    <string name="provider_lang_settings">Sağlayıcı Dilleri</string>
 | 
			
		||||
    <string name="app_layout">Uygulama Düzeni</string>
 | 
			
		||||
    <string name="preferred_media_settings">Tercih Edilen Medya</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -243,7 +243,7 @@
 | 
			
		|||
    <string name="resize_fill">Kéo dãn</string>
 | 
			
		||||
    <string name="resize_zoom">Phóng to</string>
 | 
			
		||||
	
 | 
			
		||||
    <string name="general">Tổng quan</string>
 | 
			
		||||
    <string name="category_general">Tổng quan</string>
 | 
			
		||||
    <string name="provider_lang_settings">Ngôn ngữ nhà cung cấp</string>
 | 
			
		||||
    <string name="app_layout">Giao diện App</string>
 | 
			
		||||
    <string name="preferred_media_settings">Thể loại ưu tiên</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -306,7 +306,7 @@
 | 
			
		|||
        responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use
 | 
			
		||||
        CloudStream 3 at your own risk.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="general">全局</string>
 | 
			
		||||
    <string name="category_general">全局</string>
 | 
			
		||||
    <string name="provider_lang_settings">内容提供者语言</string>
 | 
			
		||||
    <string name="app_layout">应用布局</string>
 | 
			
		||||
    <string name="preferred_media_settings">首选媒体</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,9 +35,6 @@
 | 
			
		|||
    <string name="provider_lang_key" translatable="false">provider_lang_key</string>
 | 
			
		||||
    <string name="dns_key" translatable="false">dns_key</string>
 | 
			
		||||
    <string name="download_path_key" translatable="false">download_path_key</string>
 | 
			
		||||
    <string name="nginx_url_key" translatable="false">nginx_url_key</string>
 | 
			
		||||
    <string name="nginx_credentials" translatable="false">nginx_credentials</string>
 | 
			
		||||
    <string name="nginx_info" translatable="false">nginx_info</string>
 | 
			
		||||
    <string name="app_name_download_path" translatable="false">Cloudstream</string>
 | 
			
		||||
    <string name="app_layout_key" translatable="false">app_layout_key</string>
 | 
			
		||||
    <string name="primary_color_key" translatable="false">primary_color_key</string>
 | 
			
		||||
| 
						 | 
				
			
			@ -241,11 +238,8 @@
 | 
			
		|||
    <string name="backup_failed_error_format">Error backing up %s</string>
 | 
			
		||||
 | 
			
		||||
    <string name="search">Search</string>
 | 
			
		||||
    <string name="category_nginx">Nginx</string>
 | 
			
		||||
    <string name="category_credits">Credits and account</string>
 | 
			
		||||
    <string name="category_account">Accounts</string>
 | 
			
		||||
    <string name="category_updates">Updates and backup</string>
 | 
			
		||||
    <string name="nginx_credentials_title">Nginx Credential</string>
 | 
			
		||||
    <string name="nginx_credentials_summary">You have to use the following format mycoolusername:mysecurepassword123</string>
 | 
			
		||||
    <string name="nginx_info_title">What is Nginx ?</string>
 | 
			
		||||
    <string name="nginx_info_summary">Nginx is a software that can be used to display files from a server that you own. Click to see a Nginx setup guide</string>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -414,15 +408,15 @@
 | 
			
		|||
        responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use
 | 
			
		||||
        CloudStream 3 at your own risk.
 | 
			
		||||
    </string>
 | 
			
		||||
    <string name="general">General</string>
 | 
			
		||||
    <string name="category_general">General</string>
 | 
			
		||||
    <string name="random_button_settings">Random Button</string>
 | 
			
		||||
    <string name="random_button_settings_desc">Show random button on Homepage</string>
 | 
			
		||||
    <string name="provider_lang_settings">Provider languages</string>
 | 
			
		||||
    <string name="app_layout">App Layout</string>
 | 
			
		||||
    <string name="preferred_media_settings">Preferred media</string>
 | 
			
		||||
    <string name="subtitles_encoding">Subtitle encoding</string>
 | 
			
		||||
    <string name="category_preferred_media_and_lang">Preferred media and language</string>
 | 
			
		||||
    <string name="category_ui">User interface</string>
 | 
			
		||||
    <string name="category_preferred_media_and_lang">Language</string>
 | 
			
		||||
    <string name="category_ui">Layout</string>
 | 
			
		||||
 | 
			
		||||
    <string name="automatic">Auto</string>
 | 
			
		||||
    <string name="tv_layout">TV layout</string>
 | 
			
		||||
| 
						 | 
				
			
			@ -535,4 +529,5 @@
 | 
			
		|||
    <string name="error_invalid_id">Invalid id</string>
 | 
			
		||||
    <string name="subtitles_remove_captions">Remove closed captions from subtitles</string>
 | 
			
		||||
    <string name="subtitles_remove_bloat">Remove bloat from subtitles</string>
 | 
			
		||||
    <string name="extras">Extras</string>
 | 
			
		||||
</resources>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										27
									
								
								app/src/main/res/xml/settings_account.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/src/main/res/xml/settings_account.xml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/mal_key"
 | 
			
		||||
            android:icon="@drawable/mal_logo" />
 | 
			
		||||
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/anilist_key"
 | 
			
		||||
            android:icon="@drawable/ic_anilist_icon" />
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/opensubtitles_key"
 | 
			
		||||
            android:icon="@drawable/open_subtitles_icon" />
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/nginx_key"
 | 
			
		||||
            android:icon="@drawable/nginx" />
 | 
			
		||||
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:title="@string/nginx_info_title"
 | 
			
		||||
            android:icon="@drawable/nginx_question"
 | 
			
		||||
            android:summary="@string/nginx_info_summary">
 | 
			
		||||
        <intent
 | 
			
		||||
                android:action="android.intent.action.VIEW"
 | 
			
		||||
                android:data="https://www.sarlays.com/use-nginx-with-cloudstream/" />
 | 
			
		||||
    </Preference>
 | 
			
		||||
 | 
			
		||||
</PreferenceScreen>
 | 
			
		||||
| 
						 | 
				
			
			@ -12,16 +12,6 @@
 | 
			
		|||
            android:icon="@drawable/ic_outline_subtitles_24"
 | 
			
		||||
            app:summary="@string/chromecast_subtitles_settings_des" />
 | 
			
		||||
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/dns_key"
 | 
			
		||||
            android:title="@string/dns_pref"
 | 
			
		||||
            android:summary="@string/dns_pref_summary"
 | 
			
		||||
            android:icon="@drawable/ic_baseline_dns_24" />
 | 
			
		||||
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/download_path_key"
 | 
			
		||||
            android:title="@string/download_path_pref"
 | 
			
		||||
            android:icon="@drawable/netflix_download" />
 | 
			
		||||
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/quality_pref_key"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,29 +1,17 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/mal_key"
 | 
			
		||||
            android:icon="@drawable/mal_logo" />
 | 
			
		||||
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/anilist_key"
 | 
			
		||||
            android:icon="@drawable/ic_anilist_icon" />
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/opensubtitles_key"
 | 
			
		||||
            android:icon="@drawable/open_subtitles_icon" />
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/nginx_key"
 | 
			
		||||
            android:icon="@drawable/nginx" />
 | 
			
		||||
            android:key="@string/dns_key"
 | 
			
		||||
            android:title="@string/dns_pref"
 | 
			
		||||
            android:summary="@string/dns_pref_summary"
 | 
			
		||||
            android:icon="@drawable/ic_baseline_dns_24" />
 | 
			
		||||
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/nginx_info"
 | 
			
		||||
            android:title="@string/nginx_info_title"
 | 
			
		||||
            android:icon="@drawable/nginx_question"
 | 
			
		||||
            android:summary="@string/nginx_info_summary">
 | 
			
		||||
        <intent
 | 
			
		||||
                android:action="android.intent.action.VIEW"
 | 
			
		||||
                android:data="https://www.sarlays.com/use-nginx-with-cloudstream/" />
 | 
			
		||||
    </Preference>
 | 
			
		||||
            android:key="@string/download_path_key"
 | 
			
		||||
            android:title="@string/download_path_pref"
 | 
			
		||||
            android:icon="@drawable/netflix_download" />
 | 
			
		||||
 | 
			
		||||
    <Preference
 | 
			
		||||
            android:key="@string/legal_notice_key"
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue