mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
UI
This commit is contained in:
parent
1549f67cd7
commit
22f2a8810e
26 changed files with 827 additions and 16 deletions
|
@ -1,6 +1,7 @@
|
|||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-android-extensions'
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -59,4 +60,7 @@ dependencies {
|
|||
implementation("com.google.android.material:material:1.3.0")
|
||||
|
||||
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||
|
||||
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||
implementation 'jp.wasabeef:glide-transformations:4.0.0'
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.lagradost.cloudstream3">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
45
app/src/main/java/com/lagradost/cloudstream3/AllProvider.kt
Normal file
45
app/src/main/java/com/lagradost/cloudstream3/AllProvider.kt
Normal file
|
@ -0,0 +1,45 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
|
||||
class AllProvider : MainAPI() {
|
||||
override val name: String
|
||||
get() = "All Sources"
|
||||
|
||||
var providersActive = HashSet<String>()
|
||||
|
||||
override fun search(query: String): ArrayList<Any>? {
|
||||
val list = apis.filter { a ->
|
||||
a.name != this.name && (providersActive.size == 0 || providersActive.contains(a.name))
|
||||
}.pmap { a ->
|
||||
a.search(query)
|
||||
}
|
||||
|
||||
var maxCount = 0
|
||||
var providerCount = 0
|
||||
for (res in list) {
|
||||
if (res != null) {
|
||||
if (res.size > maxCount) {
|
||||
maxCount = res.size
|
||||
}
|
||||
providerCount++
|
||||
}
|
||||
}
|
||||
|
||||
if (providerCount == 0) return null
|
||||
if (maxCount == 0) return ArrayList()
|
||||
|
||||
val result = ArrayList<Any>()
|
||||
for (i in 0..maxCount) {
|
||||
for (res in list) {
|
||||
if (res != null) {
|
||||
if (i < res.size) {
|
||||
result.add(res[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||
|
@ -13,9 +15,20 @@ val mapper = JsonMapper.builder().addModule(KotlinModule())
|
|||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!!
|
||||
|
||||
object APIHolder {
|
||||
val allApi = AllProvider()
|
||||
|
||||
private const val defProvider = 0
|
||||
|
||||
val apis = arrayListOf<MainAPI>(
|
||||
ShiroProvider()
|
||||
)
|
||||
|
||||
fun Activity.getApiSettings(): HashSet<String> {
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
return settingsManager.getStringSet(this.getString(R.string.search_providers_list_key),
|
||||
setOf(apis[defProvider].name))?.toHashSet() ?: hashSetOf(apis[defProvider].name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,6 +48,17 @@ abstract class MainAPI {
|
|||
}
|
||||
}
|
||||
|
||||
fun MainAPI.fixUrl(url: String): String {
|
||||
if (url.startsWith('/')) {
|
||||
return mainUrl + url
|
||||
}
|
||||
else if(!url.startsWith("http") && !url.startsWith("//")) {
|
||||
return "$mainUrl/$url"
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
|
||||
data class Link(
|
||||
val name: String,
|
||||
val url: String,
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
//https://stackoverflow.com/questions/34697828/parallel-operations-on-kotlin-collections
|
||||
fun <T, R> Iterable<T>.pmap(
|
||||
numThreads: Int = maxOf(Runtime.getRuntime().availableProcessors() - 2, 1),
|
||||
exec: ExecutorService = Executors.newFixedThreadPool(numThreads),
|
||||
transform: (T) -> R,
|
||||
): List<R> {
|
||||
|
||||
// default size is just an inlined version of kotlin.collections.collectionSizeOrDefault
|
||||
val defaultSize = if (this is Collection<*>) this.size else 10
|
||||
val destination = Collections.synchronizedList(ArrayList<R>(defaultSize))
|
||||
|
||||
for (item in this) {
|
||||
exec.submit { destination.add(transform(item)) }
|
||||
}
|
||||
|
||||
exec.shutdown()
|
||||
exec.awaitTermination(1, TimeUnit.DAYS)
|
||||
|
||||
return ArrayList<R>(destination)
|
||||
}
|
|
@ -1,20 +1,52 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.Resources
|
||||
import android.view.View
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
object UIHelper {
|
||||
fun Activity.getStatusBarHeight(): Int {
|
||||
val Int.toPx: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
||||
val Float.toPx: Float get() = (this * Resources.getSystem().displayMetrics.density)
|
||||
val Int.toDp: Int get() = (this / Resources.getSystem().displayMetrics.density).toInt()
|
||||
val Float.toDp: Float get() = (this / Resources.getSystem().displayMetrics.density)
|
||||
|
||||
fun Activity.loadResult(url: String, apiName: String) {
|
||||
/*this.runOnUiThread {
|
||||
this.supportFragmentManager.beginTransaction()
|
||||
.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit)
|
||||
.add(R.id.homeRoot, ResultFragment().newInstance(url, apiName))
|
||||
.commit()
|
||||
}*/
|
||||
}
|
||||
|
||||
private fun Activity.getStatusBarHeight(): Int {
|
||||
var result = 0
|
||||
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
|
||||
if (resourceId > 0) {
|
||||
result = resources.getDimensionPixelSize(resourceId)
|
||||
}
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
fun Activity.fixPaddingStatusbar(v: View) {
|
||||
v.setPadding(v.paddingLeft, v.paddingTop + getStatusBarHeight(), v.paddingRight, v.paddingBottom)
|
||||
}
|
||||
|
||||
private fun Activity.getGridFormat(): String {
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
return settingsManager.getString(getString(R.string.grid_format_key), "grid")!!
|
||||
}
|
||||
|
||||
fun Activity.getGridFormatId(): Int {
|
||||
return when (getGridFormat()) {
|
||||
"list" -> R.layout.search_result_compact
|
||||
"compact_list" -> R.layout.search_result_super_compact
|
||||
else -> R.layout.search_result_grid
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.getGridIsCompact(): Boolean {
|
||||
return getGridFormat() != "grid"
|
||||
}
|
||||
}
|
|
@ -3,9 +3,9 @@ package com.lagradost.cloudstream3.animeproviders
|
|||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.lagradost.cloudstream3.*
|
||||
import org.jsoup.Jsoup
|
||||
import java.lang.Exception
|
||||
import java.net.URLEncoder
|
||||
import java.util.*
|
||||
|
||||
|
||||
class ShiroProvider : MainAPI() {
|
||||
companion object {
|
||||
|
@ -42,6 +42,12 @@ class ShiroProvider : MainAPI() {
|
|||
@JsonProperty("_id") val _id: String,
|
||||
@JsonProperty("slug") val slug: String,
|
||||
@JsonProperty("name") val name: String,
|
||||
@JsonProperty("episodeCount") val episodeCount: String,
|
||||
@JsonProperty("language") val language: String,
|
||||
@JsonProperty("type") val type: String,
|
||||
@JsonProperty("year") val year: String,
|
||||
@JsonProperty("canonicalTitle") val canonicalTitle: String,
|
||||
@JsonProperty("english") val english: String?,
|
||||
)
|
||||
|
||||
data class ShiroSearchResponse(
|
||||
|
@ -67,16 +73,50 @@ class ShiroProvider : MainAPI() {
|
|||
)
|
||||
|
||||
override fun search(query: String): ArrayList<Any>? {
|
||||
if (!autoLoadToken()) return null
|
||||
val returnValue: ArrayList<Any> = ArrayList()
|
||||
val response = khttp.get("https://tapi.shiro.is/advanced?search=${
|
||||
URLEncoder.encode(
|
||||
query,
|
||||
"UTF-8"
|
||||
)
|
||||
}&token=$token")
|
||||
val mapped = response.let { mapper.readValue<ShiroSearchResponse>(it.text) }
|
||||
try {
|
||||
if (!autoLoadToken()) return null
|
||||
val returnValue: ArrayList<Any> = ArrayList()
|
||||
val response = khttp.get("https://tapi.shiro.is/advanced?search=${
|
||||
URLEncoder.encode(
|
||||
query,
|
||||
"UTF-8"
|
||||
)
|
||||
}&token=$token".replace("+", "%20"))
|
||||
println(response.text)
|
||||
val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) }
|
||||
for (i in mapped.data.nav.currentPage.items) {
|
||||
|
||||
return returnValue
|
||||
val type = when (i.type) {
|
||||
"TV" -> TvType.Anime
|
||||
"OVA" -> TvType.ONA
|
||||
"movie" -> TvType.Movie
|
||||
else -> TvType.Anime
|
||||
}
|
||||
val isDubbed = i.language == "dubbed"
|
||||
val set: EnumSet<DubStatus> = EnumSet.noneOf(DubStatus::class.java)
|
||||
|
||||
if (isDubbed)
|
||||
set.add(DubStatus.HasDub)
|
||||
else
|
||||
set.add(DubStatus.HasSub)
|
||||
val episodeCount = i.episodeCount.toInt()
|
||||
|
||||
returnValue.add(AnimeSearchResponse(
|
||||
i.english ?: i.canonicalTitle,
|
||||
"$mainUrl/${i.slug}",
|
||||
this.name,
|
||||
type,
|
||||
"https://cdn.shiro.is/${i.image}",
|
||||
i.year.toInt(),
|
||||
i.canonicalTitle,
|
||||
set,
|
||||
if (isDubbed) episodeCount else null,
|
||||
if (!isDubbed) episodeCount else null,
|
||||
))
|
||||
}
|
||||
return returnValue
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package com.lagradost.cloudstream3.ui.search
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.UIHelper.getGridFormatId
|
||||
import com.lagradost.cloudstream3.UIHelper.getGridIsCompact
|
||||
import com.lagradost.cloudstream3.UIHelper.loadResult
|
||||
import com.lagradost.cloudstream3.UIHelper.toPx
|
||||
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||
import kotlinx.android.synthetic.main.search_result_compact.view.*
|
||||
import kotlinx.android.synthetic.main.search_result_compact.view.backgroundCard
|
||||
import kotlinx.android.synthetic.main.search_result_compact.view.imageText
|
||||
import kotlinx.android.synthetic.main.search_result_compact.view.imageView
|
||||
import kotlinx.android.synthetic.main.search_result_grid.view.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class SearchAdapter(
|
||||
activity: Activity,
|
||||
animeList: ArrayList<Any>,
|
||||
resView: AutofitRecyclerView,
|
||||
) :
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var cardList = animeList
|
||||
private var activity: Activity = activity
|
||||
var resView: AutofitRecyclerView? = resView
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val layout = activity.getGridFormatId()
|
||||
return CardViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(layout, parent, false),
|
||||
activity,
|
||||
resView!!
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is CardViewHolder -> {
|
||||
holder.bind(cardList[position])
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return cardList.size
|
||||
}
|
||||
|
||||
class CardViewHolder
|
||||
constructor(itemView: View, _activity: Activity, resView: AutofitRecyclerView) : RecyclerView.ViewHolder(itemView) {
|
||||
val activity = _activity
|
||||
val cardView: ImageView = itemView.imageView
|
||||
val cardText: TextView = itemView.imageText
|
||||
val text_type: TextView? = itemView.text_type
|
||||
val text_is_dub: TextView? = itemView.text_is_dub
|
||||
val text_is_sub: TextView? = itemView.text_is_sub
|
||||
|
||||
//val cardTextExtra: TextView? = itemView.imageTextExtra
|
||||
//val imageTextProvider: TextView? = itemView.imageTextProvider
|
||||
val bg = itemView.backgroundCard
|
||||
val compactView = activity.getGridIsCompact()
|
||||
private val coverHeight: Int = if (compactView) 80.toPx else (resView.itemWidth / 0.68).roundToInt()
|
||||
|
||||
fun bind(card: Any) {
|
||||
if (card is SearchResponse) { // GENERIC
|
||||
if (!compactView) {
|
||||
cardView.apply {
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
coverHeight
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
text_type?.text = when (card.type) {
|
||||
TvType.Anime -> "Anime"
|
||||
TvType.Movie -> "Movie"
|
||||
TvType.ONA -> "ONA"
|
||||
TvType.TvSeries -> "TV"
|
||||
}
|
||||
|
||||
text_is_dub?.visibility = View.GONE
|
||||
text_is_sub?.visibility = View.GONE
|
||||
|
||||
cardText.text = card.name
|
||||
|
||||
//imageTextProvider.text = card.apiName
|
||||
|
||||
val glideUrl =
|
||||
GlideUrl(card.posterUrl)
|
||||
activity.let {
|
||||
Glide.with(it)
|
||||
.load(glideUrl)
|
||||
.into(cardView)
|
||||
}
|
||||
|
||||
bg.setOnClickListener {
|
||||
activity.loadResult(card.url, card.apiName)
|
||||
}
|
||||
|
||||
when (card) {
|
||||
is AnimeSearchResponse -> {
|
||||
if (card.dubStatus?.contains(DubStatus.HasDub) == true) {
|
||||
text_is_dub?.visibility = View.VISIBLE
|
||||
}
|
||||
if (card.dubStatus?.contains(DubStatus.HasSub) == true) {
|
||||
text_is_sub?.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,29 @@
|
|||
package com.lagradost.cloudstream3.ui.search
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lagradost.cloudstream3.APIHolder.allApi
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiSettings
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
|
||||
import com.lagradost.cloudstream3.UIHelper.getGridIsCompact
|
||||
import kotlinx.android.synthetic.main.fragment_search.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class SearchFragment : Fragment() {
|
||||
|
||||
|
@ -18,7 +32,7 @@ class SearchFragment : Fragment() {
|
|||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
searchViewModel =
|
||||
ViewModelProvider(this).get(SearchViewModel::class.java)
|
||||
|
@ -29,6 +43,91 @@ class SearchFragment : Fragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// activity?.fixPaddingStatusbar(searchRoot)
|
||||
activity?.fixPaddingStatusbar(searchRoot)
|
||||
|
||||
val compactView = activity?.getGridIsCompact() ?: false
|
||||
val spanCountLandscape = if (compactView) 2 else 6
|
||||
val spanCountPortrait = if (compactView) 1 else 3
|
||||
val orientation = resources.configuration.orientation
|
||||
|
||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
cardSpace.spanCount = spanCountLandscape
|
||||
} else {
|
||||
cardSpace.spanCount = spanCountPortrait
|
||||
}
|
||||
|
||||
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = activity?.let {
|
||||
SearchAdapter(
|
||||
it,
|
||||
ArrayList(),
|
||||
cardSpace,
|
||||
)
|
||||
}
|
||||
|
||||
cardSpace.adapter = adapter
|
||||
search_loading_bar.alpha = 0f
|
||||
|
||||
val search_exit_icon = main_search.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
|
||||
val search_mag_icon = main_search.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon)
|
||||
search_mag_icon.scaleX = 0.65f
|
||||
search_mag_icon.scaleY = 0.65f
|
||||
search_filter.setOnClickListener {
|
||||
val apiNamesSetting = activity?.getApiSettings()
|
||||
if (apiNamesSetting != null) {
|
||||
val apiNames = apis.map { it.name }
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
|
||||
|
||||
builder.setMultiChoiceItems(apiNames.toTypedArray(),
|
||||
apiNames.map { a -> apiNamesSetting.contains(a) }.toBooleanArray()
|
||||
) { _, position: Int, checked: Boolean ->
|
||||
val apiNamesSettingLocal = activity?.getApiSettings()
|
||||
if (apiNamesSettingLocal != null) {
|
||||
val settingsManagerLocal = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
if (checked) {
|
||||
apiNamesSettingLocal.add(apiNames[position])
|
||||
} else {
|
||||
apiNamesSettingLocal.remove(apiNames[position])
|
||||
}
|
||||
|
||||
val edit = settingsManagerLocal.edit()
|
||||
edit.putStringSet(getString(R.string.search_providers_list_key),
|
||||
apiNames.filter { a -> apiNamesSettingLocal.contains(a) }.toSet())
|
||||
edit.apply()
|
||||
allApi.providersActive = apiNamesSettingLocal
|
||||
}
|
||||
}
|
||||
builder.setTitle("Search Providers")
|
||||
builder.setNegativeButton("Cancel") { _, _ -> }
|
||||
builder.show()
|
||||
}
|
||||
}
|
||||
|
||||
main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
search_exit_icon.alpha = 0f
|
||||
search_loading_bar.alpha = 1f
|
||||
thread {
|
||||
val data = allApi.search(query)//MainActivity.activeAPI.search(query)
|
||||
activity?.runOnUiThread {
|
||||
if (data == null) {
|
||||
Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
(cardSpace.adapter as SearchAdapter).cardList = data
|
||||
(cardSpace.adapter as SearchAdapter).notifyDataSetChanged()
|
||||
}
|
||||
search_exit_icon.alpha = 1f
|
||||
search_loading_bar.alpha = 0f
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
main_search.onActionViewExpanded()
|
||||
|
||||
}
|
||||
}
|
19
app/src/main/res/anim/enter_anim.xml
Normal file
19
app/src/main/res/anim/enter_anim.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<scale
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromXScale="0.9"
|
||||
android:toXScale="1.0"
|
||||
android:fromYScale="0.9"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
>
|
||||
</scale>
|
||||
<alpha
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"/>
|
||||
</set>
|
19
app/src/main/res/anim/exit_anim.xml
Normal file
19
app/src/main/res/anim/exit_anim.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<scale
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromXScale="1.0"
|
||||
android:toXScale="0.9"
|
||||
android:fromYScale="1.0"
|
||||
android:toYScale="0.9"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
>
|
||||
</scale>
|
||||
<alpha
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"/>
|
||||
</set>
|
8
app/src/main/res/anim/nav_enter_anim.xml
Normal file
8
app/src/main/res/anim/nav_enter_anim.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"/>
|
||||
</set>
|
8
app/src/main/res/anim/nav_exit_anim.xml
Normal file
8
app/src/main/res/anim/nav_exit_anim.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"/>
|
||||
</set>
|
8
app/src/main/res/anim/nav_pop_enter.xml
Normal file
8
app/src/main/res/anim/nav_pop_enter.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"/>
|
||||
</set>
|
8
app/src/main/res/anim/nav_pop_exit.xml
Normal file
8
app/src/main/res/anim/nav_pop_exit.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"/>
|
||||
</set>
|
19
app/src/main/res/anim/pop_enter.xml
Normal file
19
app/src/main/res/anim/pop_enter.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<scale
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromXScale="0.9"
|
||||
android:toXScale="1.0"
|
||||
android:fromYScale="0.9"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
>
|
||||
</scale>
|
||||
<alpha
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"/>
|
||||
</set>
|
19
app/src/main/res/anim/pop_exit.xml
Normal file
19
app/src/main/res/anim/pop_exit.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<scale
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromXScale="1.0"
|
||||
android:toXScale="0.9"
|
||||
android:fromYScale="1.0"
|
||||
android:toYScale="0.9"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
>
|
||||
</scale>
|
||||
<alpha
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:duration="@integer/config_navAnimTime"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"/>
|
||||
</set>
|
6
app/src/main/res/drawable/dub_bg_color.xml
Normal file
6
app/src/main/res/drawable/dub_bg_color.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/dubColorBg"/>
|
||||
<corners android:radius="@dimen/roundedImageRadius"/>
|
||||
<stroke android:color="@color/dubColor" android:width="1dp"/>
|
||||
</shape>
|
6
app/src/main/res/drawable/sub_bg_color.xml
Normal file
6
app/src/main/res/drawable/sub_bg_color.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/subColorBg"/>
|
||||
<corners android:radius="@dimen/roundedImageRadius"/>
|
||||
<stroke android:color="@color/subColor" android:width="1dp"/>
|
||||
</shape>
|
8
app/src/main/res/drawable/title_shadow.xml
Normal file
8
app/src/main/res/drawable/title_shadow.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="@android:color/transparent"
|
||||
android:endColor="#000"
|
||||
android:angle="-90">
|
||||
</gradient>
|
||||
</shape>
|
6
app/src/main/res/drawable/type_bg_color.xml
Normal file
6
app/src/main/res/drawable/type_bg_color.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/typeColorBg"/>
|
||||
<corners android:radius="@dimen/roundedImageRadius"/>
|
||||
<stroke android:color="@color/typeColor" android:width="1dp"/>
|
||||
</shape>
|
80
app/src/main/res/layout/search_result_compact.xml
Normal file
80
app/src/main/res/layout/search_result_compact.xml
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:layoutDirection="ltr"
|
||||
>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
|
||||
android:id="@+id/backgroundCard"
|
||||
android:layout_margin="2dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:elevation="0dp"
|
||||
|
||||
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||
app:cardBackgroundColor="@color/itemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
>
|
||||
<androidx.cardview.widget.CardView
|
||||
android:elevation="0dp"
|
||||
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||
android:layout_width="54dp"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:layout_gravity="left"
|
||||
android:id="@+id/imageView"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
tools:ignore="RtlHardcoded"/>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="64dp">
|
||||
<TextView
|
||||
tools:text="@string/no_data"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="17sp"
|
||||
android:textColor="@color/textColor"
|
||||
android:id="@+id/imageText"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="3"
|
||||
/>
|
||||
<TextView
|
||||
tools:text="@string/no_data"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/lightGrayTextColor"
|
||||
android:id="@+id/imageTextExtra"
|
||||
android:textSize="13sp"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="3"
|
||||
/>
|
||||
<TextView
|
||||
tools:text="Rated 4.13"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:id="@+id/imageTextProvider"
|
||||
android:maxLines="1"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
</LinearLayout>
|
111
app/src/main/res/layout/search_result_grid.xml
Normal file
111
app/src/main/res/layout/search_result_grid.xml
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:id="@+id/search_result_root"
|
||||
|
||||
>
|
||||
<androidx.cardview.widget.CardView
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:layout_margin="2dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:elevation="10dp"
|
||||
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||
android:id="@+id/backgroundCard"
|
||||
app:cardBackgroundColor="@color/darkBackground"
|
||||
>
|
||||
<!-- USING CROP RATIO (182/268), centerCrop for fill -->
|
||||
<ImageView
|
||||
android:duplicateParentState="true"
|
||||
android:id="@+id/imageView"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/search_poster_descript"/>
|
||||
<ImageView
|
||||
android:focusable="false"
|
||||
android:clickable="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:src="@drawable/title_shadow"
|
||||
android:layout_gravity="bottom" android:contentDescription="@string/shadow_descript">
|
||||
</ImageView>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="bottom"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingTop="5dp"
|
||||
android:textColor="@color/textColor"
|
||||
android:id="@+id/imageText"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="2"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:ellipsize="end"
|
||||
/>
|
||||
<TextView
|
||||
android:text="Movie"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/text_type"
|
||||
android:textColor="@color/textColor"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="4dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_gravity="start"
|
||||
android:paddingBottom="4dp"
|
||||
android:minWidth="50dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/type_bg_color"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content">
|
||||
</TextView>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="end"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:text="Dub"
|
||||
android:id="@+id/text_is_dub"
|
||||
android:textColor="@color/textColor"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="4dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_gravity="end"
|
||||
android:paddingBottom="4dp"
|
||||
android:minWidth="50dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/dub_bg_color"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content">
|
||||
</TextView>
|
||||
<TextView
|
||||
android:id="@+id/text_is_sub"
|
||||
android:text="Sub"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="@color/textColor"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:minWidth="50dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/sub_bg_color"
|
||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||
android:baselineAligned="false">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</FrameLayout>
|
78
app/src/main/res/layout/search_result_super_compact.xml
Normal file
78
app/src/main/res/layout/search_result_super_compact.xml
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
>
|
||||
<androidx.cardview.widget.CardView
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/backgroundCard"
|
||||
android:layout_margin="2dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:elevation="0dp"
|
||||
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||
app:cardBackgroundColor="@color/itemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
>
|
||||
<!-- USING CROP RATIO (182/268), centerCrop for fill -->
|
||||
<androidx.cardview.widget.CardView
|
||||
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||
android:layout_width="35dp"
|
||||
android:elevation="0dp"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:layout_gravity="left"
|
||||
android:id="@+id/imageView"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
tools:ignore="RtlHardcoded" android:contentDescription="@string/search_poster_descript"/>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="45dp">
|
||||
<TextView
|
||||
tools:text="@string/no_data"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="17sp"
|
||||
android:textColor="@color/textColor"
|
||||
android:id="@+id/imageText"
|
||||
android:textStyle="normal"
|
||||
android:maxLines="3"
|
||||
/>
|
||||
<TextView
|
||||
tools:text="@string/no_data"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:textColor="@color/lightGrayTextColor"
|
||||
android:id="@+id/imageTextExtra"
|
||||
android:textSize="13sp"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="3"
|
||||
/>
|
||||
<TextView
|
||||
tools:text="Rated 4.13"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:id="@+id/imageTextProvider"
|
||||
android:maxLines="1"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</LinearLayout>
|
|
@ -21,4 +21,10 @@
|
|||
<color name="transparent">#00000000</color>
|
||||
|
||||
<color name="white">#FFF</color>
|
||||
<color name="dubColor">#3b65f5</color>
|
||||
<color name="dubColorBg">#4D3B65F5</color>
|
||||
<color name="subColor">#F53B66</color>
|
||||
<color name="subColorBg">#4DF53B66</color>
|
||||
<color name="typeColor">#F54A3B</color>
|
||||
<color name="typeColorBg">#4DF54A3B</color>
|
||||
</resources>
|
|
@ -5,4 +5,9 @@
|
|||
<string name="title_downloads">Downloads</string>
|
||||
<string name="search_hint">Search...</string>
|
||||
<string name="change_providers_descript">Change Providers</string>
|
||||
<string name="search_providers_list_key">search_providers_list</string>
|
||||
<string name="grid_format_key">grid_format</string>
|
||||
<string name="search_poster_descript">Poster</string>
|
||||
<string name="no_data">No Data</string>
|
||||
<string name="shadow_descript">Shadow</string>
|
||||
</resources>
|
Loading…
Reference in a new issue