mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	sync fixes + UI
This commit is contained in:
		
							parent
							
								
									a933aa8493
								
							
						
					
					
						commit
						4447196ebc
					
				
					 13 changed files with 556 additions and 372 deletions
				
			
		| 
						 | 
				
			
			@ -693,13 +693,13 @@ interface LoadResponse {
 | 
			
		|||
    val url: String
 | 
			
		||||
    val apiName: String
 | 
			
		||||
    val type: TvType
 | 
			
		||||
    val posterUrl: String?
 | 
			
		||||
    var posterUrl: String?
 | 
			
		||||
    val year: Int?
 | 
			
		||||
    val plot: String?
 | 
			
		||||
    val rating: Int? // 1-1000
 | 
			
		||||
    val tags: List<String>?
 | 
			
		||||
    var plot: String?
 | 
			
		||||
    var rating: Int? // 1-1000
 | 
			
		||||
    var tags: List<String>?
 | 
			
		||||
    var duration: Int? // in minutes
 | 
			
		||||
    val trailerUrl: String?
 | 
			
		||||
    var trailerUrl: String?
 | 
			
		||||
    var recommendations: List<SearchResponse>?
 | 
			
		||||
    var actors: List<ActorData>?
 | 
			
		||||
    var comingSoon: Boolean
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,6 +73,8 @@ interface SyncAPI : OAuth2API {
 | 
			
		|||
        var synonyms: List<String>? = null,
 | 
			
		||||
        var trailerUrl: String? = null,
 | 
			
		||||
        var isAdult : Boolean? = null,
 | 
			
		||||
        var posterUrl: String? = null,
 | 
			
		||||
        var backgroundPosterUrl : String? = null,
 | 
			
		||||
 | 
			
		||||
        /** In unixtime */
 | 
			
		||||
        var startDate: Long? = null,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall
 | 
			
		|||
class SyncRepo(private val repo: SyncAPI) {
 | 
			
		||||
    val idPrefix = repo.idPrefix
 | 
			
		||||
    val name = repo.name
 | 
			
		||||
    val icon = repo.icon
 | 
			
		||||
 | 
			
		||||
    suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource<Boolean> {
 | 
			
		||||
        return safeApiCall { repo.score(id, status) }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
 | 
			
		|||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
 | 
			
		||||
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
 | 
			
		||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
 | 
			
		||||
import com.lagradost.cloudstream3.ErrorLoadingException
 | 
			
		||||
import com.lagradost.cloudstream3.R
 | 
			
		||||
import com.lagradost.cloudstream3.app
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.logError
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +21,7 @@ import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.unixTime
 | 
			
		|||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
 | 
			
		||||
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
 | 
			
		||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
 | 
			
		||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
 | 
			
		||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
 | 
			
		||||
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
 | 
			
		||||
import java.net.URL
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +88,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
 | 
			
		||||
    override suspend fun getResult(id: String): SyncAPI.SyncResult? {
 | 
			
		||||
        val internalId = id.toIntOrNull() ?: return null
 | 
			
		||||
        val season = getSeason(internalId)?.data?.Media ?: return null
 | 
			
		||||
        val season = getSeason(internalId).data?.Media ?: throw ErrorLoadingException("No media")
 | 
			
		||||
 | 
			
		||||
        return SyncAPI.SyncResult(
 | 
			
		||||
            season.id.toString(),
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +102,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
            synonyms = season.synonyms,
 | 
			
		||||
            isAdult = season.isAdult,
 | 
			
		||||
            totalEpisodes = season.episodes,
 | 
			
		||||
            //synopsis = season.
 | 
			
		||||
            synopsis = season.description,
 | 
			
		||||
 | 
			
		||||
            //TODO REST
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -295,28 +298,42 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
            return fromIntToAnimeStatus(aniListStatusString.indexOf(string))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        private suspend fun getSeason(id: Int): SeasonResponse? {
 | 
			
		||||
        private suspend fun getSeason(id: Int): SeasonResponse {
 | 
			
		||||
            val q = """
 | 
			
		||||
               query (${'$'}id: Int = $id) {
 | 
			
		||||
                   Media (id: ${'$'}id, type: ANIME) {
 | 
			
		||||
                       id
 | 
			
		||||
                       idMal
 | 
			
		||||
                       coverImage
 | 
			
		||||
                       coverImage {
 | 
			
		||||
                           extraLarge
 | 
			
		||||
                           large
 | 
			
		||||
                           medium
 | 
			
		||||
                           color
 | 
			
		||||
                       }
 | 
			
		||||
                       duration
 | 
			
		||||
                       episodes
 | 
			
		||||
                       genres
 | 
			
		||||
                       synonyms
 | 
			
		||||
                       averageScore
 | 
			
		||||
                       isAdult
 | 
			
		||||
                       trailer
 | 
			
		||||
                       description(asHtml: false)
 | 
			
		||||
                       trailer {
 | 
			
		||||
                           id
 | 
			
		||||
                           site
 | 
			
		||||
                           thumbnail
 | 
			
		||||
                       }
 | 
			
		||||
                       relations {
 | 
			
		||||
                            edges {
 | 
			
		||||
                                 id
 | 
			
		||||
                                 relationType(version: 2)
 | 
			
		||||
                                 node {
 | 
			
		||||
                                      id
 | 
			
		||||
                                      coverImage
 | 
			
		||||
                                      coverImage {
 | 
			
		||||
                                          extraLarge
 | 
			
		||||
                                          large
 | 
			
		||||
                                          medium
 | 
			
		||||
                                          color
 | 
			
		||||
                                      }
 | 
			
		||||
                                 }
 | 
			
		||||
                            }
 | 
			
		||||
                       }
 | 
			
		||||
| 
						 | 
				
			
			@ -328,19 +345,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
                   }
 | 
			
		||||
               }
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
            val data = app.post(
 | 
			
		||||
                "https://graphql.anilist.co",
 | 
			
		||||
                data = mapOf("query" to q),
 | 
			
		||||
                cacheTime = 0,
 | 
			
		||||
            ).text
 | 
			
		||||
            if (data == "") return null
 | 
			
		||||
            return try {
 | 
			
		||||
                mapper.readValue(data)
 | 
			
		||||
            } catch (e: Exception) {
 | 
			
		||||
                logError(e)
 | 
			
		||||
                null
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return tryParseJson(data) ?: throw ErrorLoadingException("Error parsing $data")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -661,15 +672,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
        val seasons = mutableListOf<SeasonResponse?>()
 | 
			
		||||
        suspend fun getSeasonRecursive(id: Int) {
 | 
			
		||||
            val season = getSeason(id)
 | 
			
		||||
            if (season != null) {
 | 
			
		||||
                seasons.add(season)
 | 
			
		||||
                if (season.data?.Media?.format?.startsWith("TV") == true) {
 | 
			
		||||
                    season.data.Media.relations?.edges?.forEach {
 | 
			
		||||
                        if (it.node?.format != null) {
 | 
			
		||||
                            if (it.relationType == "SEQUEL" && it.node.format.startsWith("TV")) {
 | 
			
		||||
                                getSeasonRecursive(it.node.id)
 | 
			
		||||
                                return@forEach
 | 
			
		||||
                            }
 | 
			
		||||
            seasons.add(season)
 | 
			
		||||
            if (season.data?.Media?.format?.startsWith("TV") == true) {
 | 
			
		||||
                season.data.Media.relations?.edges?.forEach {
 | 
			
		||||
                    if (it.node?.format != null) {
 | 
			
		||||
                        if (it.relationType == "SEQUEL" && it.node.format.startsWith("TV")) {
 | 
			
		||||
                            getSeasonRecursive(it.node.id)
 | 
			
		||||
                            return@forEach
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -701,8 +710,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
 | 
			
		|||
        @JsonProperty("averageScore") val averageScore: Int?,
 | 
			
		||||
        @JsonProperty("isAdult") val isAdult: Boolean?,
 | 
			
		||||
        @JsonProperty("trailer") val trailer: MediaTrailer?,
 | 
			
		||||
 | 
			
		||||
        )
 | 
			
		||||
        @JsonProperty("description") val description: String?,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data class MediaTrailer(
 | 
			
		||||
        @JsonProperty("id") val id: String?,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
package com.lagradost.cloudstream3.ui.result
 | 
			
		||||
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.ImageView
 | 
			
		||||
import androidx.recyclerview.widget.DiffUtil
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
class ImageAdapter(context: Context, val resource: Int) : ArrayAdapter<Int>(context, resource) {
 | 
			
		||||
    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
 | 
			
		||||
        val newConvertView = convertView ?: run {
 | 
			
		||||
            val mInflater = context
 | 
			
		||||
                .getSystemService(Activity.LAYOUT_INFLATER_SERVICE) as LayoutInflater
 | 
			
		||||
            mInflater.inflate(resource, null)
 | 
			
		||||
        }
 | 
			
		||||
        getItem(position)?.let { (newConvertView as? ImageView?)?.setImageResource(it) }
 | 
			
		||||
        return newConvertView
 | 
			
		||||
    }
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
class ImageAdapter(
 | 
			
		||||
    val layout: Int,
 | 
			
		||||
) :
 | 
			
		||||
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {
 | 
			
		||||
    private val images: MutableList<Int> = mutableListOf()
 | 
			
		||||
 | 
			
		||||
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
 | 
			
		||||
        return ImageViewHolder(
 | 
			
		||||
            LayoutInflater.from(parent.context).inflate(layout, parent, false)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
 | 
			
		||||
        when (holder) {
 | 
			
		||||
            is ImageViewHolder -> {
 | 
			
		||||
                holder.bind(images[position])
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getItemCount(): Int {
 | 
			
		||||
        return images.size
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getItemId(position: Int): Long {
 | 
			
		||||
        return images[position].toLong()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateList(newList: List<Int>) {
 | 
			
		||||
        val diffResult = DiffUtil.calculateDiff(
 | 
			
		||||
            DiffCallback(this.images, newList)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        images.clear()
 | 
			
		||||
        images.addAll(newList)
 | 
			
		||||
 | 
			
		||||
        diffResult.dispatchUpdatesTo(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class ImageViewHolder
 | 
			
		||||
    constructor(itemView: View) :
 | 
			
		||||
        RecyclerView.ViewHolder(itemView) {
 | 
			
		||||
        fun bind(img: Int) {
 | 
			
		||||
            (itemView as? ImageView?)?.setImageResource(img)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DiffCallback<T>(private val oldList: List<T>, private val newList: List<T>) :
 | 
			
		||||
    DiffUtil.Callback() {
 | 
			
		||||
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
 | 
			
		||||
        oldList[oldItemPosition] == newList[newItemPosition]
 | 
			
		||||
 | 
			
		||||
    override fun getOldListSize() = oldList.size
 | 
			
		||||
 | 
			
		||||
    override fun getNewListSize() = newList.size
 | 
			
		||||
 | 
			
		||||
    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
 | 
			
		||||
        oldList[oldItemPosition] == newList[newItemPosition]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -636,7 +636,6 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
 | 
			
		|||
        val apiName = arguments?.getString("apiName") ?: return
 | 
			
		||||
        startAction = arguments?.getInt("startAction") ?: START_ACTION_NORMAL
 | 
			
		||||
        startValue = arguments?.getInt("startValue") ?: START_VALUE_NORMAL
 | 
			
		||||
 | 
			
		||||
        syncModel.addFromUrl(url)
 | 
			
		||||
 | 
			
		||||
        val api = getApiFromName(apiName)
 | 
			
		||||
| 
						 | 
				
			
			@ -1198,17 +1197,26 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        val imgAdapter = ImageAdapter(R.layout.result_mini_image)
 | 
			
		||||
        result_mini_sync?.adapter = imgAdapter
 | 
			
		||||
 | 
			
		||||
        observe(syncModel.synced) { list ->
 | 
			
		||||
            result_sync_names?.text =
 | 
			
		||||
                list.filter { it.isSynced && it.hasAccount }.joinToString { it.name }
 | 
			
		||||
 | 
			
		||||
            val newList = list.filter { it.isSynced }
 | 
			
		||||
            result_mini_sync?.isVisible = newList.isNotEmpty()
 | 
			
		||||
            (result_mini_sync?.adapter as? ImageAdapter?)?.updateList(newList.map { it.icon })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var currentSyncProgress = 0
 | 
			
		||||
        observe(syncModel.metadata) { meta ->
 | 
			
		||||
            when (meta) {
 | 
			
		||||
                is Resource.Success -> {
 | 
			
		||||
                    val d = meta.value
 | 
			
		||||
                    result_sync_episodes?.max = (d.totalEpisodes ?: 0) * 1000
 | 
			
		||||
                    result_sync_episodes?.progress = currentSyncProgress * 1000
 | 
			
		||||
 | 
			
		||||
                    normalSafeApiCall {
 | 
			
		||||
                        val ctx = result_sync_max_episodes?.context
 | 
			
		||||
                        result_sync_max_episodes?.text =
 | 
			
		||||
| 
						 | 
				
			
			@ -1218,6 +1226,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
 | 
			
		|||
                                ctx?.getString(R.string.sync_total_episodes_none)
 | 
			
		||||
                            }
 | 
			
		||||
                    }
 | 
			
		||||
                    viewModel.setMeta(d)
 | 
			
		||||
                }
 | 
			
		||||
                is Resource.Loading -> {
 | 
			
		||||
                    result_sync_max_episodes?.text =
 | 
			
		||||
| 
						 | 
				
			
			@ -1249,6 +1258,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
 | 
			
		|||
                    result_sync_rating?.value = d.score?.toFloat() ?: 0.0f
 | 
			
		||||
                    result_sync_check?.setItemChecked(d.status + 1, true)
 | 
			
		||||
                    val watchedEpisodes = d.watchedEpisodes ?: 0
 | 
			
		||||
                    currentSyncProgress = watchedEpisodes
 | 
			
		||||
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
 | 
			
		||||
                        result_sync_episodes?.setProgress(watchedEpisodes * 1000, true)
 | 
			
		||||
                    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1447,372 +1457,369 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
 | 
			
		|||
            currentId = it
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        observe(viewModel.resultResponse) { data ->
 | 
			
		||||
        observe(viewModel.result) { data ->
 | 
			
		||||
            when (data) {
 | 
			
		||||
                is Resource.Success -> {
 | 
			
		||||
                    val d = data.value
 | 
			
		||||
                    if (d is LoadResponse) {
 | 
			
		||||
                        if (d !is AnimeLoadResponse && result_episode_loading.isVisible) { // no episode loading when not anime
 | 
			
		||||
                            result_episode_loading.isVisible = false
 | 
			
		||||
                    if (d !is AnimeLoadResponse && result_episode_loading.isVisible) { // no episode loading when not anime
 | 
			
		||||
                        result_episode_loading.isVisible = false
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    updateVisStatus(2)
 | 
			
		||||
 | 
			
		||||
                    result_vpn?.text = when (api.vpnStatus) {
 | 
			
		||||
                        VPNStatus.MightBeNeeded -> getString(R.string.vpn_might_be_needed)
 | 
			
		||||
                        VPNStatus.Torrent -> getString(R.string.vpn_torrent)
 | 
			
		||||
                        else -> ""
 | 
			
		||||
                    }
 | 
			
		||||
                    result_vpn?.isGone = api.vpnStatus == VPNStatus.None
 | 
			
		||||
 | 
			
		||||
                    result_info?.text = when (api.providerType) {
 | 
			
		||||
                        ProviderType.MetaProvider -> getString(R.string.provider_info_meta)
 | 
			
		||||
                        else -> ""
 | 
			
		||||
                    }
 | 
			
		||||
                    result_info?.isVisible = api.providerType == ProviderType.MetaProvider
 | 
			
		||||
 | 
			
		||||
                    if (d.type.isEpisodeBased()) {
 | 
			
		||||
                        val ep = d as? TvSeriesLoadResponse
 | 
			
		||||
                        val epCount = ep?.episodes?.size ?: 1
 | 
			
		||||
                        if (epCount < 1) {
 | 
			
		||||
                            result_info?.text = getString(R.string.no_episodes_found)
 | 
			
		||||
                            result_info?.isVisible = true
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                        updateVisStatus(2)
 | 
			
		||||
                    currentHeaderName = d.name
 | 
			
		||||
                    currentType = d.type
 | 
			
		||||
 | 
			
		||||
                        result_vpn?.text = when (api.vpnStatus) {
 | 
			
		||||
                            VPNStatus.MightBeNeeded -> getString(R.string.vpn_might_be_needed)
 | 
			
		||||
                            VPNStatus.Torrent -> getString(R.string.vpn_torrent)
 | 
			
		||||
                            else -> ""
 | 
			
		||||
                    currentPoster = d.posterUrl
 | 
			
		||||
                    currentIsMovie = !d.isEpisodeBased()
 | 
			
		||||
 | 
			
		||||
                    result_open_in_browser?.setOnClickListener {
 | 
			
		||||
                        val i = Intent(ACTION_VIEW)
 | 
			
		||||
                        i.data = Uri.parse(d.url)
 | 
			
		||||
                        try {
 | 
			
		||||
                            startActivity(i)
 | 
			
		||||
                        } catch (e: Exception) {
 | 
			
		||||
                            logError(e)
 | 
			
		||||
                        }
 | 
			
		||||
                        result_vpn?.isGone = api.vpnStatus == VPNStatus.None
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                        result_info?.text = when (api.providerType) {
 | 
			
		||||
                            ProviderType.MetaProvider -> getString(R.string.provider_info_meta)
 | 
			
		||||
                            else -> ""
 | 
			
		||||
                    result_search?.setOnClickListener {
 | 
			
		||||
                        QuickSearchFragment.pushSearch(activity, d.name)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    result_share?.setOnClickListener {
 | 
			
		||||
                        try {
 | 
			
		||||
                            val i = Intent(ACTION_SEND)
 | 
			
		||||
                            i.type = "text/plain"
 | 
			
		||||
                            i.putExtra(EXTRA_SUBJECT, d.name)
 | 
			
		||||
                            i.putExtra(EXTRA_TEXT, d.url)
 | 
			
		||||
                            startActivity(createChooser(i, d.name))
 | 
			
		||||
                        } catch (e: Exception) {
 | 
			
		||||
                            logError(e)
 | 
			
		||||
                        }
 | 
			
		||||
                        result_info?.isVisible = api.providerType == ProviderType.MetaProvider
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                        if (d.type.isEpisodeBased()) {
 | 
			
		||||
                            val ep = d as? TvSeriesLoadResponse
 | 
			
		||||
                            val epCount = ep?.episodes?.size ?: 1
 | 
			
		||||
                            if (epCount < 1) {
 | 
			
		||||
                                result_info?.text = getString(R.string.no_episodes_found)
 | 
			
		||||
                                result_info?.isVisible = true
 | 
			
		||||
                    val showStatus = when (d) {
 | 
			
		||||
                        is TvSeriesLoadResponse -> d.showStatus
 | 
			
		||||
                        is AnimeLoadResponse -> d.showStatus
 | 
			
		||||
                        else -> null
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    setShow(showStatus)
 | 
			
		||||
                    setDuration(d.duration)
 | 
			
		||||
                    setYear(d.year)
 | 
			
		||||
                    setRating(d.rating)
 | 
			
		||||
                    setRecommendations(d.recommendations)
 | 
			
		||||
                    setActors(d.actors)
 | 
			
		||||
 | 
			
		||||
                    if (SettingsFragment.accountEnabled)
 | 
			
		||||
                        if (d is AnimeLoadResponse) {
 | 
			
		||||
                            if (
 | 
			
		||||
                                setMalSync(d.malId)
 | 
			
		||||
                                ||
 | 
			
		||||
                                setAniListSync(d.anilistId)
 | 
			
		||||
                            ) {
 | 
			
		||||
                                syncModel.updateMetaAndUser()
 | 
			
		||||
                            } else {
 | 
			
		||||
                                syncModel.addFromUrl(d.url)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        currentHeaderName = d.name
 | 
			
		||||
                        currentType = d.type
 | 
			
		||||
                    result_meta_site?.text = d.apiName
 | 
			
		||||
 | 
			
		||||
                        currentPoster = d.posterUrl
 | 
			
		||||
                        currentIsMovie = !d.isEpisodeBased()
 | 
			
		||||
 | 
			
		||||
                        result_open_in_browser?.setOnClickListener {
 | 
			
		||||
                            val i = Intent(ACTION_VIEW)
 | 
			
		||||
                            i.data = Uri.parse(d.url)
 | 
			
		||||
                    val posterImageLink = d.posterUrl
 | 
			
		||||
                    if (!posterImageLink.isNullOrEmpty()) {
 | 
			
		||||
                        result_poster?.setImage(posterImageLink)
 | 
			
		||||
                        result_poster_blur?.setImageBlur(posterImageLink, 10, 3)
 | 
			
		||||
                        //Full screen view of Poster image
 | 
			
		||||
                        result_poster_holder?.setOnClickListener {
 | 
			
		||||
                            try {
 | 
			
		||||
                                startActivity(i)
 | 
			
		||||
                            } catch (e: Exception) {
 | 
			
		||||
                                logError(e)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                                context?.let { ctx ->
 | 
			
		||||
                                    val bitmap = result_poster.drawable.toBitmap()
 | 
			
		||||
                                    val sourceBuilder = AlertDialog.Builder(ctx)
 | 
			
		||||
                                    sourceBuilder.setView(R.layout.result_poster)
 | 
			
		||||
 | 
			
		||||
                        result_search?.setOnClickListener {
 | 
			
		||||
                            QuickSearchFragment.pushSearch(activity, d.name)
 | 
			
		||||
                        }
 | 
			
		||||
                                    val sourceDialog = sourceBuilder.create()
 | 
			
		||||
                                    sourceDialog.show()
 | 
			
		||||
 | 
			
		||||
                        result_share?.setOnClickListener {
 | 
			
		||||
                            try {
 | 
			
		||||
                                val i = Intent(ACTION_SEND)
 | 
			
		||||
                                i.type = "text/plain"
 | 
			
		||||
                                i.putExtra(EXTRA_SUBJECT, d.name)
 | 
			
		||||
                                i.putExtra(EXTRA_TEXT, d.url)
 | 
			
		||||
                                startActivity(createChooser(i, d.name))
 | 
			
		||||
                            } catch (e: Exception) {
 | 
			
		||||
                                logError(e)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        val showStatus = when (d) {
 | 
			
		||||
                            is TvSeriesLoadResponse -> d.showStatus
 | 
			
		||||
                            is AnimeLoadResponse -> d.showStatus
 | 
			
		||||
                            else -> null
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        setShow(showStatus)
 | 
			
		||||
                        setDuration(d.duration)
 | 
			
		||||
                        setYear(d.year)
 | 
			
		||||
                        setRating(d.rating)
 | 
			
		||||
                        setRecommendations(d.recommendations)
 | 
			
		||||
                        setActors(d.actors)
 | 
			
		||||
 | 
			
		||||
                        if (SettingsFragment.accountEnabled)
 | 
			
		||||
                            if (d is AnimeLoadResponse) {
 | 
			
		||||
                                if (
 | 
			
		||||
                                    setMalSync(d.malId)
 | 
			
		||||
                                    ||
 | 
			
		||||
                                    setAniListSync(d.anilistId)
 | 
			
		||||
                                ) {
 | 
			
		||||
                                    syncModel.updateMetaAndUser()
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                        result_meta_site?.text = d.apiName
 | 
			
		||||
 | 
			
		||||
                        val posterImageLink = d.posterUrl
 | 
			
		||||
                        if (!posterImageLink.isNullOrEmpty()) {
 | 
			
		||||
                            result_poster?.setImage(posterImageLink)
 | 
			
		||||
                            result_poster_blur?.setImageBlur(posterImageLink, 10, 3)
 | 
			
		||||
                            //Full screen view of Poster image
 | 
			
		||||
                            result_poster_holder?.setOnClickListener {
 | 
			
		||||
                                try {
 | 
			
		||||
                                    context?.let { ctx ->
 | 
			
		||||
                                        val bitmap = result_poster.drawable.toBitmap()
 | 
			
		||||
                                        val sourceBuilder = AlertDialog.Builder(ctx)
 | 
			
		||||
                                        sourceBuilder.setView(R.layout.result_poster)
 | 
			
		||||
 | 
			
		||||
                                        val sourceDialog = sourceBuilder.create()
 | 
			
		||||
                                        sourceDialog.show()
 | 
			
		||||
 | 
			
		||||
                                        sourceDialog.findViewById<ImageView?>(R.id.imgPoster)
 | 
			
		||||
                                            ?.apply {
 | 
			
		||||
                                                setImageBitmap(bitmap)
 | 
			
		||||
                                                setOnClickListener {
 | 
			
		||||
                                                    sourceDialog.dismissSafe()
 | 
			
		||||
                                                }
 | 
			
		||||
                                    sourceDialog.findViewById<ImageView?>(R.id.imgPoster)
 | 
			
		||||
                                        ?.apply {
 | 
			
		||||
                                            setImageBitmap(bitmap)
 | 
			
		||||
                                            setOnClickListener {
 | 
			
		||||
                                                sourceDialog.dismissSafe()
 | 
			
		||||
                                            }
 | 
			
		||||
                                    }
 | 
			
		||||
                                } catch (e: Exception) {
 | 
			
		||||
                                    logError(e)
 | 
			
		||||
                                        }
 | 
			
		||||
                                }
 | 
			
		||||
                            } catch (e: Exception) {
 | 
			
		||||
                                logError(e)
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            result_poster?.setImageResource(R.drawable.default_cover)
 | 
			
		||||
                            result_poster_blur?.setImageResource(R.drawable.default_cover)
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        result_poster?.setImageResource(R.drawable.default_cover)
 | 
			
		||||
                        result_poster_blur?.setImageResource(R.drawable.default_cover)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                        result_poster_holder?.visibility = VISIBLE
 | 
			
		||||
                    result_poster_holder?.visibility = VISIBLE
 | 
			
		||||
 | 
			
		||||
                        /*result_play_movie?.text =
 | 
			
		||||
                            if (d.type == TvType.Torrent) getString(R.string.play_torrent_button) else getString(
 | 
			
		||||
                                R.string.play_movie_button
 | 
			
		||||
                            )*/
 | 
			
		||||
                        //result_plot_header?.text =
 | 
			
		||||
                        //    if (d.type == TvType.Torrent) getString(R.string.torrent_plot) else getString(R.string.result_plot)
 | 
			
		||||
                        if (!d.plot.isNullOrEmpty()) {
 | 
			
		||||
                            var syno = d.plot!!
 | 
			
		||||
                            if (syno.length > MAX_SYNO_LENGH) {
 | 
			
		||||
                                syno = syno.substring(0, MAX_SYNO_LENGH) + "..."
 | 
			
		||||
                            }
 | 
			
		||||
                            result_descript.setOnClickListener {
 | 
			
		||||
                                val builder: AlertDialog.Builder =
 | 
			
		||||
                                    AlertDialog.Builder(requireContext())
 | 
			
		||||
                                builder.setMessage(d.plot)
 | 
			
		||||
                                    .setTitle(if (d.type == TvType.Torrent) R.string.torrent_plot else R.string.result_plot)
 | 
			
		||||
                                    .show()
 | 
			
		||||
                            }
 | 
			
		||||
                            result_descript.text = syno
 | 
			
		||||
                        } else {
 | 
			
		||||
                            result_descript.text =
 | 
			
		||||
                                if (d.type == TvType.Torrent) getString(R.string.torrent_no_plot) else getString(
 | 
			
		||||
                                    R.string.normal_no_plot
 | 
			
		||||
                                )
 | 
			
		||||
                    /*result_play_movie?.text =
 | 
			
		||||
                        if (d.type == TvType.Torrent) getString(R.string.play_torrent_button) else getString(
 | 
			
		||||
                            R.string.play_movie_button
 | 
			
		||||
                        )*/
 | 
			
		||||
                    //result_plot_header?.text =
 | 
			
		||||
                    //    if (d.type == TvType.Torrent) getString(R.string.torrent_plot) else getString(R.string.result_plot)
 | 
			
		||||
                    if (!d.plot.isNullOrEmpty()) {
 | 
			
		||||
                        var syno = d.plot!!
 | 
			
		||||
                        if (syno.length > MAX_SYNO_LENGH) {
 | 
			
		||||
                            syno = syno.substring(0, MAX_SYNO_LENGH) + "..."
 | 
			
		||||
                        }
 | 
			
		||||
                        result_descript.setOnClickListener {
 | 
			
		||||
                            val builder: AlertDialog.Builder =
 | 
			
		||||
                                AlertDialog.Builder(requireContext())
 | 
			
		||||
                            builder.setMessage(d.plot)
 | 
			
		||||
                                .setTitle(if (d.type == TvType.Torrent) R.string.torrent_plot else R.string.result_plot)
 | 
			
		||||
                                .show()
 | 
			
		||||
                        }
 | 
			
		||||
                        result_descript.text = syno
 | 
			
		||||
                    } else {
 | 
			
		||||
                        result_descript.text =
 | 
			
		||||
                            if (d.type == TvType.Torrent) getString(R.string.torrent_no_plot) else getString(
 | 
			
		||||
                                R.string.normal_no_plot
 | 
			
		||||
                            )
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                        result_tag?.removeAllViews()
 | 
			
		||||
                    result_tag?.removeAllViews()
 | 
			
		||||
                    //result_tag_holder?.visibility = GONE
 | 
			
		||||
                    // result_status.visibility = GONE
 | 
			
		||||
 | 
			
		||||
                    d.comingSoon.let { soon ->
 | 
			
		||||
                        result_coming_soon?.isVisible = soon
 | 
			
		||||
                        result_data_holder?.isGone = soon
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    val tags = d.tags
 | 
			
		||||
                    if (tags.isNullOrEmpty()) {
 | 
			
		||||
                        //result_tag_holder?.visibility = GONE
 | 
			
		||||
                        // result_status.visibility = GONE
 | 
			
		||||
                    } else {
 | 
			
		||||
                        //result_tag_holder?.visibility = VISIBLE
 | 
			
		||||
 | 
			
		||||
                        d.comingSoon.let { soon ->
 | 
			
		||||
                            result_coming_soon?.isVisible = soon
 | 
			
		||||
                            result_data_holder?.isGone = soon
 | 
			
		||||
                        for ((index, tag) in tags.withIndex()) {
 | 
			
		||||
                            val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
 | 
			
		||||
                            val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
 | 
			
		||||
                            btt.text = tag
 | 
			
		||||
 | 
			
		||||
                            result_tag?.addView(viewBtt, index)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (d.type.isMovieType()) {
 | 
			
		||||
                        val hasDownloadSupport = api.hasDownloadSupport
 | 
			
		||||
                        lateFixDownloadButton(true)
 | 
			
		||||
 | 
			
		||||
                        result_play_movie?.setOnClickListener {
 | 
			
		||||
                            val card =
 | 
			
		||||
                                currentEpisodes?.firstOrNull() ?: return@setOnClickListener
 | 
			
		||||
                            handleAction(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        val tags = d.tags
 | 
			
		||||
                        if (tags.isNullOrEmpty()) {
 | 
			
		||||
                            //result_tag_holder?.visibility = GONE
 | 
			
		||||
                        } else {
 | 
			
		||||
                            //result_tag_holder?.visibility = VISIBLE
 | 
			
		||||
 | 
			
		||||
                            for ((index, tag) in tags.withIndex()) {
 | 
			
		||||
                                val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
 | 
			
		||||
                                val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
 | 
			
		||||
                                btt.text = tag
 | 
			
		||||
 | 
			
		||||
                                result_tag?.addView(viewBtt, index)
 | 
			
		||||
                            }
 | 
			
		||||
                        result_play_movie?.setOnLongClickListener {
 | 
			
		||||
                            val card = currentEpisodes?.firstOrNull()
 | 
			
		||||
                                ?: return@setOnLongClickListener true
 | 
			
		||||
                            handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
 | 
			
		||||
                            return@setOnLongClickListener true
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (d.type.isMovieType()) {
 | 
			
		||||
                            val hasDownloadSupport = api.hasDownloadSupport
 | 
			
		||||
                            lateFixDownloadButton(true)
 | 
			
		||||
 | 
			
		||||
                            result_play_movie?.setOnClickListener {
 | 
			
		||||
                                val card =
 | 
			
		||||
                                    currentEpisodes?.firstOrNull() ?: return@setOnClickListener
 | 
			
		||||
                                handleAction(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            result_play_movie?.setOnLongClickListener {
 | 
			
		||||
                                val card = currentEpisodes?.firstOrNull()
 | 
			
		||||
                                    ?: return@setOnLongClickListener true
 | 
			
		||||
                                handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
 | 
			
		||||
                                return@setOnLongClickListener true
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            result_download_movie?.setOnLongClickListener {
 | 
			
		||||
                                val card = currentEpisodes?.firstOrNull()
 | 
			
		||||
                                    ?: return@setOnLongClickListener true
 | 
			
		||||
                                handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
 | 
			
		||||
                                return@setOnLongClickListener true
 | 
			
		||||
                            }
 | 
			
		||||
                        result_download_movie?.setOnLongClickListener {
 | 
			
		||||
                            val card = currentEpisodes?.firstOrNull()
 | 
			
		||||
                                ?: return@setOnLongClickListener true
 | 
			
		||||
                            handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
 | 
			
		||||
                            return@setOnLongClickListener true
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
//                            result_options.setOnClickListener {
 | 
			
		||||
//                                val card = currentEpisodes?.first() ?: return@setOnClickListener
 | 
			
		||||
//                                handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
 | 
			
		||||
//                            }
 | 
			
		||||
 | 
			
		||||
                            result_download_movie?.visibility =
 | 
			
		||||
                                if (hasDownloadSupport) VISIBLE else GONE
 | 
			
		||||
                            if (hasDownloadSupport) {
 | 
			
		||||
                                val localId = d.getId()
 | 
			
		||||
                                val file =
 | 
			
		||||
                                    VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
 | 
			
		||||
                                        requireContext(),
 | 
			
		||||
                                        localId
 | 
			
		||||
                                    )
 | 
			
		||||
                                downloadButton?.dispose()
 | 
			
		||||
                                downloadButton = EasyDownloadButton()
 | 
			
		||||
                                downloadButton?.setUpMoreButton(
 | 
			
		||||
                                    file?.fileLength,
 | 
			
		||||
                                    file?.totalBytes,
 | 
			
		||||
                                    result_movie_progress_downloaded,
 | 
			
		||||
                                    result_movie_download_icon,
 | 
			
		||||
                                    result_movie_download_text,
 | 
			
		||||
                                    result_movie_download_text_precentage,
 | 
			
		||||
                                    result_download_movie,
 | 
			
		||||
                                    true,
 | 
			
		||||
                                    VideoDownloadHelper.DownloadEpisodeCached(
 | 
			
		||||
                                        d.name,
 | 
			
		||||
                                        d.posterUrl,
 | 
			
		||||
                                        0,
 | 
			
		||||
                                        null,
 | 
			
		||||
                                        localId,
 | 
			
		||||
                                        localId,
 | 
			
		||||
                                        d.rating,
 | 
			
		||||
                                        d.plot,
 | 
			
		||||
                                        System.currentTimeMillis(),
 | 
			
		||||
                                    )
 | 
			
		||||
                                ) { downloadClickEvent ->
 | 
			
		||||
                                    if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
 | 
			
		||||
                                        currentEpisodes?.firstOrNull()?.let { episode ->
 | 
			
		||||
                                            handleAction(
 | 
			
		||||
                                                EpisodeClickEvent(
 | 
			
		||||
                                                    ACTION_DOWNLOAD_EPISODE,
 | 
			
		||||
                                                    ResultEpisode(
 | 
			
		||||
                                                        d.name,
 | 
			
		||||
                                                        d.name,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        0,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        episode.data,
 | 
			
		||||
                                                        d.apiName,
 | 
			
		||||
                                                        localId,
 | 
			
		||||
                                                        0,
 | 
			
		||||
                                                        0L,
 | 
			
		||||
                                                        0L,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        d.type,
 | 
			
		||||
                                                        localId,
 | 
			
		||||
                                                    )
 | 
			
		||||
                        result_download_movie?.visibility =
 | 
			
		||||
                            if (hasDownloadSupport) VISIBLE else GONE
 | 
			
		||||
                        if (hasDownloadSupport) {
 | 
			
		||||
                            val localId = d.getId()
 | 
			
		||||
                            val file =
 | 
			
		||||
                                VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
 | 
			
		||||
                                    requireContext(),
 | 
			
		||||
                                    localId
 | 
			
		||||
                                )
 | 
			
		||||
                            downloadButton?.dispose()
 | 
			
		||||
                            downloadButton = EasyDownloadButton()
 | 
			
		||||
                            downloadButton?.setUpMoreButton(
 | 
			
		||||
                                file?.fileLength,
 | 
			
		||||
                                file?.totalBytes,
 | 
			
		||||
                                result_movie_progress_downloaded,
 | 
			
		||||
                                result_movie_download_icon,
 | 
			
		||||
                                result_movie_download_text,
 | 
			
		||||
                                result_movie_download_text_precentage,
 | 
			
		||||
                                result_download_movie,
 | 
			
		||||
                                true,
 | 
			
		||||
                                VideoDownloadHelper.DownloadEpisodeCached(
 | 
			
		||||
                                    d.name,
 | 
			
		||||
                                    d.posterUrl,
 | 
			
		||||
                                    0,
 | 
			
		||||
                                    null,
 | 
			
		||||
                                    localId,
 | 
			
		||||
                                    localId,
 | 
			
		||||
                                    d.rating,
 | 
			
		||||
                                    d.plot,
 | 
			
		||||
                                    System.currentTimeMillis(),
 | 
			
		||||
                                )
 | 
			
		||||
                            ) { downloadClickEvent ->
 | 
			
		||||
                                if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
 | 
			
		||||
                                    currentEpisodes?.firstOrNull()?.let { episode ->
 | 
			
		||||
                                        handleAction(
 | 
			
		||||
                                            EpisodeClickEvent(
 | 
			
		||||
                                                ACTION_DOWNLOAD_EPISODE,
 | 
			
		||||
                                                ResultEpisode(
 | 
			
		||||
                                                    d.name,
 | 
			
		||||
                                                    d.name,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    0,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    episode.data,
 | 
			
		||||
                                                    d.apiName,
 | 
			
		||||
                                                    localId,
 | 
			
		||||
                                                    0,
 | 
			
		||||
                                                    0L,
 | 
			
		||||
                                                    0L,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    d.type,
 | 
			
		||||
                                                    localId,
 | 
			
		||||
                                                )
 | 
			
		||||
                                            )
 | 
			
		||||
                                        }
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        handleDownloadClick(
 | 
			
		||||
                                            activity,
 | 
			
		||||
                                            currentHeaderName,
 | 
			
		||||
                                            downloadClickEvent
 | 
			
		||||
                                        )
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                result_download_movie?.setOnLongClickListener {
 | 
			
		||||
                                    val card =
 | 
			
		||||
                                        currentEpisodes?.firstOrNull()
 | 
			
		||||
                                            ?: return@setOnLongClickListener false
 | 
			
		||||
                                    handleAction(EpisodeClickEvent(ACTION_DOWNLOAD_MIRROR, card))
 | 
			
		||||
                                    return@setOnLongClickListener true
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                /*downloadButton?.setUpMaterialButton(
 | 
			
		||||
                                    file?.fileLength,
 | 
			
		||||
                                    file?.totalBytes,
 | 
			
		||||
                                    result_movie_progress_downloaded,
 | 
			
		||||
                                    result_download_movie,
 | 
			
		||||
                                    null, //result_movie_text_progress
 | 
			
		||||
                                    VideoDownloadHelper.DownloadEpisodeCached(
 | 
			
		||||
                                        d.name,
 | 
			
		||||
                                        d.posterUrl,
 | 
			
		||||
                                        0,
 | 
			
		||||
                                        null,
 | 
			
		||||
                                        localId,
 | 
			
		||||
                                        localId,
 | 
			
		||||
                                        d.rating,
 | 
			
		||||
                                        d.plot,
 | 
			
		||||
                                        System.currentTimeMillis(),
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    handleDownloadClick(
 | 
			
		||||
                                        activity,
 | 
			
		||||
                                        currentHeaderName,
 | 
			
		||||
                                        downloadClickEvent
 | 
			
		||||
                                    )
 | 
			
		||||
                                ) { downloadClickEvent ->
 | 
			
		||||
                                    if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
 | 
			
		||||
                                        currentEpisodes?.firstOrNull()?.let { episode ->
 | 
			
		||||
                                            handleAction(
 | 
			
		||||
                                                EpisodeClickEvent(
 | 
			
		||||
                                                    ACTION_DOWNLOAD_EPISODE,
 | 
			
		||||
                                                    ResultEpisode(
 | 
			
		||||
                                                        d.name,
 | 
			
		||||
                                                        d.name,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        0,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        episode.data,
 | 
			
		||||
                                                        d.apiName,
 | 
			
		||||
                                                        localId,
 | 
			
		||||
                                                        0,
 | 
			
		||||
                                                        0L,
 | 
			
		||||
                                                        0L,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        null,
 | 
			
		||||
                                                        d.type,
 | 
			
		||||
                                                        localId,
 | 
			
		||||
                                                    )
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            result_download_movie?.setOnLongClickListener {
 | 
			
		||||
                                val card =
 | 
			
		||||
                                    currentEpisodes?.firstOrNull()
 | 
			
		||||
                                        ?: return@setOnLongClickListener false
 | 
			
		||||
                                handleAction(EpisodeClickEvent(ACTION_DOWNLOAD_MIRROR, card))
 | 
			
		||||
                                return@setOnLongClickListener true
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            /*downloadButton?.setUpMaterialButton(
 | 
			
		||||
                                file?.fileLength,
 | 
			
		||||
                                file?.totalBytes,
 | 
			
		||||
                                result_movie_progress_downloaded,
 | 
			
		||||
                                result_download_movie,
 | 
			
		||||
                                null, //result_movie_text_progress
 | 
			
		||||
                                VideoDownloadHelper.DownloadEpisodeCached(
 | 
			
		||||
                                    d.name,
 | 
			
		||||
                                    d.posterUrl,
 | 
			
		||||
                                    0,
 | 
			
		||||
                                    null,
 | 
			
		||||
                                    localId,
 | 
			
		||||
                                    localId,
 | 
			
		||||
                                    d.rating,
 | 
			
		||||
                                    d.plot,
 | 
			
		||||
                                    System.currentTimeMillis(),
 | 
			
		||||
                                )
 | 
			
		||||
                            ) { downloadClickEvent ->
 | 
			
		||||
                                if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
 | 
			
		||||
                                    currentEpisodes?.firstOrNull()?.let { episode ->
 | 
			
		||||
                                        handleAction(
 | 
			
		||||
                                            EpisodeClickEvent(
 | 
			
		||||
                                                ACTION_DOWNLOAD_EPISODE,
 | 
			
		||||
                                                ResultEpisode(
 | 
			
		||||
                                                    d.name,
 | 
			
		||||
                                                    d.name,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    0,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    episode.data,
 | 
			
		||||
                                                    d.apiName,
 | 
			
		||||
                                                    localId,
 | 
			
		||||
                                                    0,
 | 
			
		||||
                                                    0L,
 | 
			
		||||
                                                    0L,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    null,
 | 
			
		||||
                                                    d.type,
 | 
			
		||||
                                                    localId,
 | 
			
		||||
                                                )
 | 
			
		||||
                                            )
 | 
			
		||||
                                        }
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        handleDownloadClick(
 | 
			
		||||
                                            activity,
 | 
			
		||||
                                            currentHeaderName,
 | 
			
		||||
                                            downloadClickEvent
 | 
			
		||||
                                        )
 | 
			
		||||
                                    }
 | 
			
		||||
                                }*/
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            lateFixDownloadButton(false)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        context?.getString(
 | 
			
		||||
                            when (d.type) {
 | 
			
		||||
                                TvType.TvSeries -> R.string.tv_series_singular
 | 
			
		||||
                                TvType.Anime -> R.string.anime_singular
 | 
			
		||||
                                TvType.OVA -> R.string.ova_singular
 | 
			
		||||
                                TvType.AnimeMovie -> R.string.movies_singular
 | 
			
		||||
                                TvType.Cartoon -> R.string.cartoons_singular
 | 
			
		||||
                                TvType.Documentary -> R.string.documentaries_singular
 | 
			
		||||
                                TvType.Movie -> R.string.movies_singular
 | 
			
		||||
                                TvType.Torrent -> R.string.torrent_singular
 | 
			
		||||
                                TvType.AsianDrama -> R.string.asian_drama_singular
 | 
			
		||||
                            }
 | 
			
		||||
                        )?.let {
 | 
			
		||||
                            result_meta_type?.text = it
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        when (d) {
 | 
			
		||||
                            is AnimeLoadResponse -> {
 | 
			
		||||
 | 
			
		||||
                                // val preferEnglish = true
 | 
			
		||||
                                //val titleName = (if (preferEnglish) d.engName else d.japName) ?: d.name
 | 
			
		||||
                                val titleName = d.name
 | 
			
		||||
                                result_title.text = titleName
 | 
			
		||||
                                //result_toolbar.title = titleName
 | 
			
		||||
                            }
 | 
			
		||||
                            else -> result_title.text = d.name
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    handleDownloadClick(
 | 
			
		||||
                                        activity,
 | 
			
		||||
                                        currentHeaderName,
 | 
			
		||||
                                        downloadClickEvent
 | 
			
		||||
                                    )
 | 
			
		||||
                                }
 | 
			
		||||
                            }*/
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        updateVisStatus(1)
 | 
			
		||||
                        lateFixDownloadButton(false)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    context?.getString(
 | 
			
		||||
                        when (d.type) {
 | 
			
		||||
                            TvType.TvSeries -> R.string.tv_series_singular
 | 
			
		||||
                            TvType.Anime -> R.string.anime_singular
 | 
			
		||||
                            TvType.OVA -> R.string.ova_singular
 | 
			
		||||
                            TvType.AnimeMovie -> R.string.movies_singular
 | 
			
		||||
                            TvType.Cartoon -> R.string.cartoons_singular
 | 
			
		||||
                            TvType.Documentary -> R.string.documentaries_singular
 | 
			
		||||
                            TvType.Movie -> R.string.movies_singular
 | 
			
		||||
                            TvType.Torrent -> R.string.torrent_singular
 | 
			
		||||
                            TvType.AsianDrama -> R.string.asian_drama_singular
 | 
			
		||||
                        }
 | 
			
		||||
                    )?.let {
 | 
			
		||||
                        result_meta_type?.text = it
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    when (d) {
 | 
			
		||||
                        is AnimeLoadResponse -> {
 | 
			
		||||
 | 
			
		||||
                            // val preferEnglish = true
 | 
			
		||||
                            //val titleName = (if (preferEnglish) d.engName else d.japName) ?: d.name
 | 
			
		||||
                            val titleName = d.name
 | 
			
		||||
                            result_title.text = titleName
 | 
			
		||||
                            //result_toolbar.title = titleName
 | 
			
		||||
                        }
 | 
			
		||||
                        else -> result_title.text = d.name
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is Resource.Failure -> {
 | 
			
		||||
| 
						 | 
				
			
			@ -1873,7 +1880,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
 | 
			
		|||
                    it.context?.openBrowser(tempUrl)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (restart || viewModel.resultResponse.value == null) {
 | 
			
		||||
                if (restart || viewModel.result.value == null) {
 | 
			
		||||
                    //viewModel.clear()
 | 
			
		||||
                    viewModel.load(tempUrl, apiName, showFillers)
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
package com.lagradost.cloudstream3.ui.result
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import androidx.lifecycle.LiveData
 | 
			
		||||
import androidx.lifecycle.MutableLiveData
 | 
			
		||||
import androidx.lifecycle.ViewModel
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.APIHolder.getId
 | 
			
		|||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.Resource
 | 
			
		||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
 | 
			
		||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
 | 
			
		||||
import com.lagradost.cloudstream3.ui.APIRepository
 | 
			
		||||
import com.lagradost.cloudstream3.ui.WatchType
 | 
			
		||||
import com.lagradost.cloudstream3.ui.player.IGenerator
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +44,7 @@ class ResultViewModel : ViewModel() {
 | 
			
		|||
    private var repo: APIRepository? = null
 | 
			
		||||
    private var generator: IGenerator? = null
 | 
			
		||||
 | 
			
		||||
    private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
 | 
			
		||||
    private val _resultResponse: MutableLiveData<Resource<LoadResponse>> = MutableLiveData()
 | 
			
		||||
    private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
 | 
			
		||||
    private val episodeById: MutableLiveData<HashMap<Int, Int>> =
 | 
			
		||||
        MutableLiveData() // lookup by ID to get Index
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +56,8 @@ class ResultViewModel : ViewModel() {
 | 
			
		|||
    private val selectedRangeInt: MutableLiveData<Int> = MutableLiveData()
 | 
			
		||||
    val rangeOptions: LiveData<List<String>> = _rangeOptions
 | 
			
		||||
 | 
			
		||||
    val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
 | 
			
		||||
    val result: LiveData<Resource<LoadResponse>> get() = _resultResponse
 | 
			
		||||
 | 
			
		||||
    val episodes: LiveData<List<ResultEpisode>> get() = _episodes
 | 
			
		||||
    val publicEpisodes: LiveData<Resource<List<ResultEpisode>>> get() = _publicEpisodes
 | 
			
		||||
    val publicEpisodesCount: LiveData<Int> get() = _publicEpisodesCount
 | 
			
		||||
| 
						 | 
				
			
			@ -106,6 +108,41 @@ class ResultViewModel : ViewModel() {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val TAG = "RVM"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var lastMeta: SyncAPI.SyncResult? = null
 | 
			
		||||
    private fun applyMeta(resp: LoadResponse, meta: SyncAPI.SyncResult?): LoadResponse {
 | 
			
		||||
        if (meta == null) return resp
 | 
			
		||||
        lastMeta = meta
 | 
			
		||||
        return resp.apply {
 | 
			
		||||
            Log.i(TAG, "applyMeta")
 | 
			
		||||
 | 
			
		||||
            duration = duration ?: meta.duration
 | 
			
		||||
            rating = rating ?: meta.publicScore
 | 
			
		||||
            tags = tags ?: meta.genres
 | 
			
		||||
            plot = if (plot.isNullOrBlank()) meta.synopsis else plot
 | 
			
		||||
            trailerUrl = trailerUrl ?: meta.trailerUrl
 | 
			
		||||
            posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl
 | 
			
		||||
            actors = actors ?: meta.actors?.map {
 | 
			
		||||
                ActorData(
 | 
			
		||||
                    Actor(
 | 
			
		||||
                        name = it.name,
 | 
			
		||||
                        image = it.posterUrl
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setMeta(meta: SyncAPI.SyncResult) {
 | 
			
		||||
        Log.i(TAG, "setMeta")
 | 
			
		||||
        (result.value as? Resource.Success<LoadResponse>?)?.value?.let { resp ->
 | 
			
		||||
            _resultResponse.postValue(Resource.Success(applyMeta(resp, meta)))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun loadWatchStatus(localId: Int? = null) {
 | 
			
		||||
        val currentId = localId ?: id.value ?: return
 | 
			
		||||
        val currentWatch = getResultWatchState(currentId)
 | 
			
		||||
| 
						 | 
				
			
			@ -289,7 +326,7 @@ class ResultViewModel : ViewModel() {
 | 
			
		|||
 | 
			
		||||
        when (data) {
 | 
			
		||||
            is Resource.Success -> {
 | 
			
		||||
                val d = data.value
 | 
			
		||||
                val d = applyMeta(data.value, lastMeta)
 | 
			
		||||
                page.postValue(d)
 | 
			
		||||
                val mainId = d.getId()
 | 
			
		||||
                id.postValue(mainId)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@ data class CurrentSynced(
 | 
			
		|||
    val idPrefix: String,
 | 
			
		||||
    val isSynced: Boolean,
 | 
			
		||||
    val hasAccount: Boolean,
 | 
			
		||||
    val icon : Int,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
class SyncViewModel : ViewModel() {
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +49,8 @@ class SyncViewModel : ViewModel() {
 | 
			
		|||
                it.name,
 | 
			
		||||
                it.idPrefix,
 | 
			
		||||
                syncIds.containsKey(it.idPrefix),
 | 
			
		||||
                it.hasAccount()
 | 
			
		||||
                it.hasAccount(),
 | 
			
		||||
                it.icon,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -67,8 +69,13 @@ class SyncViewModel : ViewModel() {
 | 
			
		|||
        updateSynced()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var hasAddedFromUrl : HashSet<String> = hashSetOf()
 | 
			
		||||
 | 
			
		||||
    fun addFromUrl(url: String?) = viewModelScope.launch {
 | 
			
		||||
        if(url == null || hasAddedFromUrl.contains(url)) return@launch
 | 
			
		||||
        SyncUtil.getIdsFromUrl(url)?.let { (malId, aniListId) ->
 | 
			
		||||
            hasAddedFromUrl.add(url)
 | 
			
		||||
 | 
			
		||||
            setMalId(malId)
 | 
			
		||||
            setAniListId(aniListId)
 | 
			
		||||
            if (malId != null || aniListId != null) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
package com.lagradost.cloudstream3.utils
 | 
			
		||||
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty
 | 
			
		||||
import com.fasterxml.jackson.module.kotlin.readValue
 | 
			
		||||
import com.lagradost.cloudstream3.app
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +15,8 @@ object SyncUtil {
 | 
			
		|||
        Regex("""(twist\.moe)/a/([^/?]*)"""),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private const val TAG = "SNC"
 | 
			
		||||
 | 
			
		||||
    private const val GOGOANIME = "Gogoanime"
 | 
			
		||||
    private const val NINE_ANIME = "9anime"
 | 
			
		||||
    private const val TWIST_MOE = "Twistmoe"
 | 
			
		||||
| 
						 | 
				
			
			@ -28,6 +31,7 @@ object SyncUtil {
 | 
			
		|||
 | 
			
		||||
    suspend fun getIdsFromUrl(url: String?): Pair<String?, String?>? {
 | 
			
		||||
        if (url == null) return null
 | 
			
		||||
        Log.i(TAG, "getIdsFromUrl $url")
 | 
			
		||||
 | 
			
		||||
        for (regex in regexs) {
 | 
			
		||||
            regex.find(url)?.let { match ->
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +55,7 @@ object SyncUtil {
 | 
			
		|||
        slug: String,
 | 
			
		||||
        site: String = "GogoanimeGogoanime"
 | 
			
		||||
    ): Pair<String?, String?>? {
 | 
			
		||||
        Log.i(TAG, "getIdsFromSlug $slug $site")
 | 
			
		||||
        try {
 | 
			
		||||
            //Gogoanime, Twistmoe and 9anime
 | 
			
		||||
            val url =
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        android:width="24dp"
 | 
			
		||||
        android:height="24dp"
 | 
			
		||||
        android:width="20dp"
 | 
			
		||||
        android:height="20dp"
 | 
			
		||||
        android:viewportWidth="172"
 | 
			
		||||
        android:viewportHeight="172"
 | 
			
		||||
        android:tint="?attr/white"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -375,6 +375,7 @@
 | 
			
		|||
                        </LinearLayout>
 | 
			
		||||
                    </LinearLayout>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    <com.google.android.material.button.MaterialButton
 | 
			
		||||
                            android:id="@+id/result_bookmark_button"
 | 
			
		||||
                            style="@style/BlackButton"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,22 +12,45 @@
 | 
			
		|||
            android:paddingEnd="@dimen/result_padding"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content">
 | 
			
		||||
        <LinearLayout
 | 
			
		||||
                android:orientation="horizontal"
 | 
			
		||||
                android:gravity="center_vertical"
 | 
			
		||||
                android:layout_gravity="center_vertical"
 | 
			
		||||
                android:layout_width="wrap_content"
 | 
			
		||||
                android:layout_height="wrap_content">
 | 
			
		||||
            <ImageView
 | 
			
		||||
                    android:nextFocusDown="@id/result_bookmark_button"
 | 
			
		||||
                    android:nextFocusRight="@id/result_share"
 | 
			
		||||
                    android:background="?android:attr/selectableItemBackgroundBorderless"
 | 
			
		||||
 | 
			
		||||
        <ImageView
 | 
			
		||||
                android:nextFocusDown="@id/result_bookmark_button"
 | 
			
		||||
                android:nextFocusRight="@id/result_share"
 | 
			
		||||
                android:background="?android:attr/selectableItemBackgroundBorderless"
 | 
			
		||||
                    android:id="@+id/result_back"
 | 
			
		||||
                    android:clickable="true"
 | 
			
		||||
                    android:focusable="true"
 | 
			
		||||
 | 
			
		||||
                android:id="@+id/result_back"
 | 
			
		||||
                android:clickable="true"
 | 
			
		||||
                android:focusable="true"
 | 
			
		||||
 | 
			
		||||
                android:layout_width="30dp"
 | 
			
		||||
                android:layout_height="30dp"
 | 
			
		||||
                android:layout_gravity="center_vertical|start"
 | 
			
		||||
                android:src="@drawable/ic_baseline_arrow_back_24"
 | 
			
		||||
                android:contentDescription="@string/go_back"
 | 
			
		||||
                app:tint="?attr/white" />
 | 
			
		||||
                    android:layout_width="30dp"
 | 
			
		||||
                    android:layout_height="30dp"
 | 
			
		||||
                    android:layout_gravity="center_vertical|start"
 | 
			
		||||
                    android:src="@drawable/ic_baseline_arrow_back_24"
 | 
			
		||||
                    android:contentDescription="@string/go_back"
 | 
			
		||||
                    app:tint="?attr/white" />
 | 
			
		||||
            <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
                    android:paddingStart="10dp"
 | 
			
		||||
                    android:paddingEnd="10dp"
 | 
			
		||||
                    android:id="@+id/result_mini_sync"
 | 
			
		||||
                    android:layout_width="match_parent"
 | 
			
		||||
                    android:descendantFocusability="afterDescendants"
 | 
			
		||||
                    android:layout_height="wrap_content"
 | 
			
		||||
                    android:fadingEdge="horizontal"
 | 
			
		||||
                    android:focusableInTouchMode="false"
 | 
			
		||||
                    android:focusable="false"
 | 
			
		||||
                    android:layout_gravity="center"
 | 
			
		||||
                    android:orientation="horizontal"
 | 
			
		||||
                    android:paddingTop="5dp"
 | 
			
		||||
                    android:requiresFadingEdge="horizontal"
 | 
			
		||||
                    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
 | 
			
		||||
                    tools:itemCount="2"
 | 
			
		||||
                    tools:listitem="@layout/result_mini_image" />
 | 
			
		||||
        </LinearLayout>
 | 
			
		||||
 | 
			
		||||
        <LinearLayout
 | 
			
		||||
                android:gravity="end"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								app/src/main/res/layout/result_mini_image.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/layout/result_mini_image.xml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
        xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
        android:layout_width="35dp"
 | 
			
		||||
        android:layout_height="35dp"
 | 
			
		||||
        android:layout_gravity="center"
 | 
			
		||||
        tools:src="@drawable/ic_anilist_icon"
 | 
			
		||||
        app:tint="?attr/white">
 | 
			
		||||
</ImageView>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue