forked from recloudstream/cloudstream
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…
Reference in a new issue