tv UI fixes

This commit is contained in:
reduplicated 2022-08-29 01:52:15 +02:00
parent 01c81fc421
commit cd119822f2
29 changed files with 393 additions and 167 deletions

View file

@ -6,7 +6,6 @@ import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Resources import android.content.res.Resources
import android.os.Build import android.os.Build
import android.os.Looper
import android.util.Log import android.util.Log
import android.view.* import android.view.*
import android.widget.TextView import android.widget.TextView
@ -20,12 +19,12 @@ import com.google.android.gms.cast.framework.CastSession
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.PlayerEventType import com.lagradost.cloudstream3.ui.player.PlayerEventType
import com.lagradost.cloudstream3.ui.result.UiText import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv
import com.lagradost.cloudstream3.utils.Event import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.UIHelper import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission
import com.lagradost.cloudstream3.utils.UIHelper.shouldShowPIPMode import com.lagradost.cloudstream3.utils.UIHelper.shouldShowPIPMode
import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.coroutines.currentCoroutineContext
import org.schabi.newpipe.extractor.NewPipe import org.schabi.newpipe.extractor.NewPipe
import java.util.* import java.util.*
@ -128,7 +127,7 @@ object CommonActivity {
act.hasPIPPermission() // CHECK IF FEATURE IS ENABLED IN SETTINGS act.hasPIPPermission() // CHECK IF FEATURE IS ENABLED IN SETTINGS
act.updateLocale() act.updateLocale()
act.updateTv()
NewPipe.init(DownloaderTestImpl.getInstance()) NewPipe.init(DownloaderTestImpl.getInstance())
} }

View file

@ -226,7 +226,7 @@ object APIHolder {
} }
private fun Context.getHasTrailers(): Boolean { private fun Context.getHasTrailers(): Boolean {
if (this.isTvSettings()) return false if (isTvSettings()) return false
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
return settingsManager.getBoolean(this.getString(R.string.show_trailers_key), true) return settingsManager.getBoolean(this.getString(R.string.show_trailers_key), true)
} }

View file

@ -175,7 +175,7 @@ class DownloadFragment : Fragment() {
download_list?.adapter = adapter download_list?.adapter = adapter
download_list?.layoutManager = GridLayoutManager(context, 1) download_list?.layoutManager = GridLayoutManager(context, 1)
download_stream_button?.isGone = context?.isTvSettings() == true download_stream_button?.isGone = isTvSettings()
download_stream_button?.setOnClickListener { download_stream_button?.setOnClickListener {
val dialog = val dialog =
Dialog(it.context ?: return@setOnClickListener, R.style.AlertDialogCustom) Dialog(it.context ?: return@setOnClickListener, R.style.AlertDialogCustom)

View file

@ -400,7 +400,7 @@ class HomeFragment : Fragment() {
//homeViewModel = //homeViewModel =
// ViewModelProvider(this).get(HomeViewModel::class.java) // ViewModelProvider(this).get(HomeViewModel::class.java)
val layout = val layout =
if (context?.isTvSettings() == true) R.layout.fragment_home_tv else R.layout.fragment_home if (isTvSettings()) R.layout.fragment_home_tv else R.layout.fragment_home
return inflater.inflate(layout, container, false) return inflater.inflate(layout, container, false)
} }
@ -568,7 +568,7 @@ class HomeFragment : Fragment() {
val randomSize = items.size val randomSize = items.size
tempAdapter?.updateList(items) tempAdapter?.updateList(items)
if (context?.isTvSettings() == false) { if (!isTvSettings()) {
home_main_poster_recyclerview?.post { home_main_poster_recyclerview?.post {
(home_main_poster_recyclerview?.layoutManager as CenterZoomLayoutManager?)?.let { manager -> (home_main_poster_recyclerview?.layoutManager as CenterZoomLayoutManager?)?.let { manager ->
manager.updateSize(forceUpdate = true) manager.updateSize(forceUpdate = true)
@ -939,7 +939,7 @@ class HomeFragment : Fragment() {
} }
} // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() } } // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() }
if (context?.isTvSettings() == false) { if (!isTvSettings()) {
LinearSnapHelper().attachToRecyclerView(home_main_poster_recyclerview) // snap LinearSnapHelper().attachToRecyclerView(home_main_poster_recyclerview) // snap
val centerLayoutManager = val centerLayoutManager =
CenterZoomLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) CenterZoomLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
@ -975,7 +975,7 @@ class HomeFragment : Fragment() {
home_api_fab?.shrink() // hide home_api_fab?.shrink() // hide
home_random?.shrink() home_random?.shrink()
} else if (dy < -5) { } else if (dy < -5) {
if (v.context?.isTvSettings() == false) { if (!isTvSettings()) {
home_api_fab?.extend() // show home_api_fab?.extend() // show
home_random?.extend() home_random?.extend()
} }
@ -984,37 +984,35 @@ class HomeFragment : Fragment() {
// nice profile pic on homepage // nice profile pic on homepage
home_profile_picture_holder?.isVisible = false home_profile_picture_holder?.isVisible = false
context?.let { ctx -> // just in case
// just in case if (isTvSettings()) {
if (ctx.isTvSettings()) { home_api_fab?.isVisible = false
home_api_fab?.isVisible = false home_change_api?.isVisible = true
home_change_api?.isVisible = true if (isTrueTvSettings()) {
if (ctx.isTrueTvSettings()) { home_change_api_loading?.isVisible = true
home_change_api_loading?.isVisible = true home_change_api_loading?.isFocusable = true
home_change_api_loading?.isFocusable = true home_change_api_loading?.isFocusableInTouchMode = true
home_change_api_loading?.isFocusableInTouchMode = true home_change_api?.isFocusable = true
home_change_api?.isFocusable = true home_change_api?.isFocusableInTouchMode = true
home_change_api?.isFocusableInTouchMode = true
}
// home_bookmark_select?.isFocusable = true
// home_bookmark_select?.isFocusableInTouchMode = true
} else {
home_api_fab?.isVisible = true
home_change_api?.isVisible = false
home_change_api_loading?.isVisible = false
} }
// home_bookmark_select?.isFocusable = true
// home_bookmark_select?.isFocusableInTouchMode = true
} else {
home_api_fab?.isVisible = true
home_change_api?.isVisible = false
home_change_api_loading?.isVisible = false
}
for (syncApi in OAuth2Apis) { for (syncApi in OAuth2Apis) {
val login = syncApi.loginInfo() val login = syncApi.loginInfo()
val pic = login?.profilePicture val pic = login?.profilePicture
if (home_profile_picture?.setImage( if (home_profile_picture?.setImage(
pic, pic,
errorImageDrawable = errorProfilePic errorImageDrawable = errorProfilePic
) == true ) == true
) { ) {
home_profile_picture_holder?.isVisible = true home_profile_picture_holder?.isVisible = true
break break
}
} }
} }
} }

View file

@ -29,7 +29,7 @@ class ParentItemAdapter(
override fun onCreateViewHolder(parent: ViewGroup, i: Int): ParentViewHolder { override fun onCreateViewHolder(parent: ViewGroup, i: Int): ParentViewHolder {
//println("onCreateViewHolder $i") //println("onCreateViewHolder $i")
val layout = val layout =
if (parent.context.isTvSettings()) R.layout.homepage_parent_tv else R.layout.homepage_parent if (isTvSettings()) R.layout.homepage_parent_tv else R.layout.homepage_parent
return ParentViewHolder( return ParentViewHolder(
LayoutInflater.from(parent.context).inflate(layout, parent, false), LayoutInflater.from(parent.context).inflate(layout, parent, false),
clickCallback, clickCallback,

View file

@ -993,7 +993,7 @@ class GeneratorPlayer : FullScreenPlayer() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
// this is used instead of layout-television to follow the settings and some TV devices are not classified as TV for some reason // this is used instead of layout-television to follow the settings and some TV devices are not classified as TV for some reason
isTv = context?.isTvSettings() == true isTv = isTvSettings()
layout = layout =
if (isTv) R.layout.fragment_player_tv else R.layout.fragment_player if (isTv) R.layout.fragment_player_tv else R.layout.fragment_player

View file

@ -124,7 +124,7 @@ class PlayerEpisodeAdapter(
clickCallback.invoke(PlayerEpisodeClickEvent(0, card)) clickCallback.invoke(PlayerEpisodeClickEvent(0, card))
} }
if (parentView.context.isTrueTvSettings()) { if (isTrueTvSettings()) {
parentView.isFocusable = true parentView.isFocusable = true
parentView.isFocusableInTouchMode = true parentView.isFocusableInTouchMode = true
parentView.touchscreenBlocksFocus = false parentView.touchscreenBlocksFocus = false

View file

@ -166,7 +166,7 @@ class EpisodeAdapter(
fun bind(card: ResultEpisode) { fun bind(card: ResultEpisode) {
localCard = card localCard = card
val isTrueTv = itemView.context?.isTrueTvSettings() == true val isTrueTv = isTrueTvSettings()
val (parentView, otherView) = if (card.poster == null) { val (parentView, otherView) = if (card.poster == null) {
itemView.episode_holder to itemView.episode_holder_large itemView.episode_holder to itemView.episode_holder_large

View file

@ -83,7 +83,7 @@ class ImageAdapter(
this.nextFocusUpId = nextFocusUp this.nextFocusUpId = nextFocusUp
} }
if (clickCallback != null) { if (clickCallback != null) {
if (context.isTrueTvSettings()) { if (isTrueTvSettings()) {
isClickable = true isClickable = true
isLongClickable = true isLongClickable = true
isFocusable = true isFocusable = true

View file

@ -293,7 +293,7 @@ open class ResultFragment : ResultTrailerPlayer() {
result_reload_connection_open_in_browser?.isVisible = true result_reload_connection_open_in_browser?.isVisible = true
} }
2 -> { 2 -> {
result_bookmark_fab?.isGone = result_bookmark_fab?.context?.isTvSettings() == true result_bookmark_fab?.isGone = isTvSettings()
result_bookmark_fab?.extend() result_bookmark_fab?.extend()
//if (result_bookmark_button?.context?.isTrueTvSettings() == true) { //if (result_bookmark_button?.context?.isTrueTvSettings() == true) {
// when { // when {
@ -551,7 +551,7 @@ open class ResultFragment : ResultTrailerPlayer() {
} }
// This is to band-aid FireTV navigation // This is to band-aid FireTV navigation
val isTv = context?.isTvSettings() == true val isTv = isTvSettings()
result_season_button?.isFocusableInTouchMode = isTv result_season_button?.isFocusableInTouchMode = isTv
result_episode_select?.isFocusableInTouchMode = isTv result_episode_select?.isFocusableInTouchMode = isTv
result_dub_select?.isFocusableInTouchMode = isTv result_dub_select?.isFocusableInTouchMode = isTv
@ -794,7 +794,7 @@ open class ResultFragment : ResultTrailerPlayer() {
result_next_airing_time.setText(d.nextAiringDate) result_next_airing_time.setText(d.nextAiringDate)
result_poster.setImage(d.posterImage) result_poster.setImage(d.posterImage)
if (d.posterImage != null && context?.isTrueTvSettings() == false) if (d.posterImage != null && !isTrueTvSettings())
result_poster_holder?.setOnClickListener { result_poster_holder?.setOnClickListener {
try { try {
context?.let { ctx -> context?.let { ctx ->
@ -883,7 +883,7 @@ open class ResultFragment : ResultTrailerPlayer() {
result_tag_holder?.isVisible = tags.isNotEmpty() result_tag_holder?.isVisible = tags.isNotEmpty()
if (tags.isNotEmpty()) { if (tags.isNotEmpty()) {
//result_tag_holder?.visibility = VISIBLE //result_tag_holder?.visibility = VISIBLE
val isOnTv = context?.isTrueTvSettings() == true val isOnTv = isTrueTvSettings()
for ((index, tag) in tags.withIndex()) { for ((index, tag) in tags.withIndex()) {
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null) val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card) val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
@ -941,7 +941,7 @@ open class ResultFragment : ResultTrailerPlayer() {
} }
// bloats the navigation on tv // bloats the navigation on tv
if (context?.isTrueTvSettings() == false) { if (!isTrueTvSettings()) {
result_meta_site?.setOnClickListener { result_meta_site?.setOnClickListener {
it.context?.openBrowser(storedData.url) it.context?.openBrowser(storedData.url)
} }

View file

@ -85,7 +85,7 @@ class SelectAdaptor(val callback: (Any) -> Unit) : RecyclerView.Adapter<Recycler
fun bind( fun bind(
data: SelectData, isSelected: Boolean, callback: (Any) -> Unit data: SelectData, isSelected: Boolean, callback: (Any) -> Unit
) { ) {
val isTrueTv = itemView.context?.isTrueTvSettings() == true val isTrueTv = isTrueTvSettings()
if (isTrueTv) { if (isTrueTv) {
item.isFocusable = true item.isFocusable = true
item.isFocusableInTouchMode = true item.isFocusableInTouchMode = true

View file

@ -386,7 +386,7 @@ class SearchFragment : Fragment() {
} }
} }
if (context?.isTrueTvSettings() == true) { if (isTrueTvSettings()) {
search_filter.isFocusable = true search_filter.isFocusable = true
search_filter.isFocusableInTouchMode = true search_filter.isFocusableInTouchMode = true
} }

View file

@ -164,7 +164,7 @@ object SearchResultBuilder {
} }
} }
if (bg.context.isTrueTvSettings()) { if (isTrueTvSettings()) {
bg.isFocusable = true bg.isFocusable = true
bg.isFocusableInTouchMode = true bg.isFocusableInTouchMode = true
bg.touchscreenBlocksFocus = false bg.touchscreenBlocksFocus = false

View file

@ -117,7 +117,7 @@ class SettingsAccount : PreferenceFragmentCompat() {
dialog.login_username_input to api.requiresUsername dialog.login_username_input to api.requiresUsername
) )
if (activity.isTvSettings()) { if (isTvSettings()) {
visibilityMap.forEach { (input, isVisible) -> visibilityMap.forEach { (input, isVisible) ->
input.isVisible = isVisible input.isVisible = isVisible

View file

@ -30,6 +30,9 @@ class SettingsFragment : Fragment() {
companion object { companion object {
var beneneCount = 0 var beneneCount = 0
private var isTv : Boolean = false
private var isTrueTv : Boolean = false
fun PreferenceFragmentCompat?.getPref(id: Int): Preference? { fun PreferenceFragmentCompat?.getPref(id: Int): Preference? {
if (this == null) return null if (this == null) return null
@ -45,7 +48,7 @@ class SettingsFragment : Fragment() {
* On TV you cannot properly scroll to the bottom of settings, this fixes that. * On TV you cannot properly scroll to the bottom of settings, this fixes that.
* */ * */
fun PreferenceFragmentCompat.setPaddingBottom() { fun PreferenceFragmentCompat.setPaddingBottom() {
if (this.context?.isTvSettings() == true) { if (isTvSettings()) {
listView?.setPadding(0, 0, 0, 100.toPx) listView?.setPadding(0, 0, 0, 100.toPx)
} }
} }
@ -93,7 +96,7 @@ class SettingsFragment : Fragment() {
return settingsManager.getInt(this.getString(R.string.app_layout_key), -1) return settingsManager.getInt(this.getString(R.string.app_layout_key), -1)
} }
fun Context.isTvSettings(): Boolean { private fun Context.isTvSettings(): Boolean {
var value = getLayoutInt() var value = getLayoutInt()
if (value == -1) { if (value == -1) {
value = if (isAutoTv()) 1 else 0 value = if (isAutoTv()) 1 else 0
@ -101,7 +104,7 @@ class SettingsFragment : Fragment() {
return value == 1 || value == 2 return value == 1 || value == 2
} }
fun Context.isTrueTvSettings(): Boolean { private fun Context.isTrueTvSettings(): Boolean {
var value = getLayoutInt() var value = getLayoutInt()
if (value == -1) { if (value == -1) {
value = if (isAutoTv()) 1 else 0 value = if (isAutoTv()) 1 else 0
@ -109,6 +112,19 @@ class SettingsFragment : Fragment() {
return value == 1 return value == 1
} }
fun Context.updateTv() {
isTrueTv = isTrueTvSettings()
isTv = isTvSettings()
}
fun isTrueTvSettings(): Boolean {
return isTrueTv
}
fun isTvSettings(): Boolean {
return isTv
}
fun Context.isEmulatorSettings(): Boolean { fun Context.isEmulatorSettings(): Boolean {
return getLayoutInt() == 2 return getLayoutInt() == 2
} }
@ -136,7 +152,7 @@ class SettingsFragment : Fragment() {
activity?.navigate(id, Bundle()) activity?.navigate(id, Bundle())
} }
val isTrueTv = context?.isTrueTvSettings() == true val isTrueTv = isTrueTvSettings()
for (syncApi in accountManagers) { for (syncApi in accountManagers) {
val login = syncApi.loginInfo() val login = syncApi.loginInfo()

View file

@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
@ -22,6 +23,7 @@ class SettingsUI : PreferenceFragmentCompat() {
setUpToolbar(R.string.category_ui) setUpToolbar(R.string.category_ui)
setPaddingBottom() setPaddingBottom()
} }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard() hideKeyboard()
setPreferencesFromResource(R.xml.settins_ui, rootKey) setPreferencesFromResource(R.xml.settins_ui, rootKey)
@ -71,6 +73,7 @@ class SettingsUI : PreferenceFragmentCompat() {
settingsManager.edit() settingsManager.edit()
.putInt(getString(R.string.app_layout_key), prefValues[it]) .putInt(getString(R.string.app_layout_key), prefValues[it])
.apply() .apply()
context?.updateTv()
activity?.recreate() activity?.recreate()
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
@ -130,7 +133,10 @@ class SettingsUI : PreferenceFragmentCompat() {
getPref(R.string.pref_filter_search_quality_key)?.setOnPreferenceClickListener { getPref(R.string.pref_filter_search_quality_key)?.setOnPreferenceClickListener {
val names = enumValues<SearchQuality>().sorted().map { it.name } val names = enumValues<SearchQuality>().sorted().map { it.name }
val currentList = settingsManager.getStringSet(getString(R.string.pref_filter_search_quality_key), setOf())?.map { val currentList = settingsManager.getStringSet(
getString(R.string.pref_filter_search_quality_key),
setOf()
)?.map {
it.toInt() it.toInt()
} ?: listOf() } ?: listOf()

View file

@ -11,6 +11,7 @@ import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@ -23,12 +24,14 @@ import com.lagradost.cloudstream3.mvvm.Some
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.plugins.RepositoryManager import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.setText
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.widget.LinearRecycleViewLayoutManager
import kotlinx.android.synthetic.main.add_repo_input.* import kotlinx.android.synthetic.main.add_repo_input.*
import kotlinx.android.synthetic.main.fragment_extensions.* import kotlinx.android.synthetic.main.fragment_extensions.*
import kotlinx.android.synthetic.main.fragment_extensions.list_repositories import kotlinx.android.synthetic.main.fragment_extensions.list_repositories
@ -119,9 +122,15 @@ class ExtensionsFragment : Fragment() {
(repo_recycler_view?.adapter as? RepoAdapter)?.updateList(it) (repo_recycler_view?.adapter as? RepoAdapter)?.updateList(it)
} }
repo_recycler_view?.apply {
context?.let { ctx ->
layoutManager = LinearRecycleViewLayoutManager(ctx, nextFocusUpId, nextFocusDownId)
}
}
list_repositories?.setOnClickListener { list_repositories?.setOnClickListener {
// Open webview on tv if browser fails // Open webview on tv if browser fails
val isTv = it.context.isTvSettings() val isTv = isTvSettings()
openBrowser(PUBLIC_REPOSITORIES_LIST, isTv, this) openBrowser(PUBLIC_REPOSITORIES_LIST, isTv, this)
// Set clipboard on TV because the browser might not exist or work properly // Set clipboard on TV because the browser might not exist or work properly
@ -170,9 +179,9 @@ class ExtensionsFragment : Fragment() {
) )
} }
add_repo_button?.setOnClickListener { val addRepositoryClick = View.OnClickListener {
val builder = val builder =
AlertDialog.Builder(context ?: return@setOnClickListener, R.style.AlertDialogCustom) AlertDialog.Builder(context ?: return@OnClickListener, R.style.AlertDialogCustom)
.setView(R.layout.add_repo_input) .setView(R.layout.add_repo_input)
val dialog = builder.create() val dialog = builder.create()
@ -193,8 +202,7 @@ class ExtensionsFragment : Fragment() {
dialog.list_repositories?.setOnClickListener { dialog.list_repositories?.setOnClickListener {
// Open webview on tv if browser fails // Open webview on tv if browser fails
val isTv = it.context.isTvSettings() openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
openBrowser(PUBLIC_REPOSITORIES_LIST, isTv, this)
} }
// dialog.text2?.text = provider.name // dialog.text2?.text = provider.name
@ -223,6 +231,12 @@ class ExtensionsFragment : Fragment() {
} }
} }
val isTv = isTrueTvSettings()
add_repo_button?.isGone = isTv
add_repo_button_imageview_holder?.isVisible = isTv
add_repo_button?.setOnClickListener(addRepositoryClick)
add_repo_button_imageview?.setOnClickListener(addRepositoryClick)
reloadRepositories() reloadRepositories()
} }
} }

View file

@ -14,6 +14,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.setText
import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.GlideApp import com.lagradost.cloudstream3.utils.GlideApp
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
@ -36,8 +37,9 @@ class PluginAdapter(
private val plugins: MutableList<PluginViewData> = mutableListOf() private val plugins: MutableList<PluginViewData> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layout = if(isTrueTvSettings()) R.layout.repository_item_tv else R.layout.repository_item
return PluginViewHolder( return PluginViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.repository_item, parent, false) LayoutInflater.from(parent.context).inflate(layout, parent, false)
) )
} }
@ -123,7 +125,6 @@ class PluginAdapter(
itemView.action_button?.setOnClickListener { itemView.action_button?.setOnClickListener {
iconClickCallback.invoke(data.plugin) iconClickCallback.invoke(data.plugin)
} }
testFindClosestBase2()
//if (itemView.context?.isTrueTvSettings() == false) { //if (itemView.context?.isTrueTvSettings() == false) {
// val siteUrl = metadata.repositoryUrl // val siteUrl = metadata.repositoryUrl
// if (siteUrl != null && siteUrl.isNotBlank() && siteUrl != "NONE") { // if (siteUrl != null && siteUrl.isNotBlank() && siteUrl != "NONE") {

View file

@ -128,7 +128,7 @@ class PluginsFragment : Fragment() {
pluginViewModel.handlePluginAction(activity, url, it, isLocal) pluginViewModel.handlePluginAction(activity, url, it, isLocal)
} }
if (context?.isTvSettings() == true) { if (isTvSettings()) {
// Scrolling down does not reveal the whole RecyclerView on TV, add to bypass that. // Scrolling down does not reveal the whole RecyclerView on TV, add to bypass that.
plugin_recycler_view?.setPadding(0, 0, 0, 200.toPx) plugin_recycler_view?.setPadding(0, 0, 0, 200.toPx)
} }

View file

@ -7,6 +7,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import kotlinx.android.synthetic.main.repository_item.view.* import kotlinx.android.synthetic.main.repository_item.view.*
class RepoAdapter( class RepoAdapter(
@ -19,8 +20,9 @@ class RepoAdapter(
private val repositories: MutableList<RepositoryData> = mutableListOf() private val repositories: MutableList<RepositoryData> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layout = if(isTrueTvSettings()) R.layout.repository_item_tv else R.layout.repository_item
return RepoViewHolder( return RepoViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.repository_item, parent, false) LayoutInflater.from(parent.context).inflate(layout, parent, false)
) )
} }

View file

@ -73,8 +73,7 @@ class SetupFragmentExtensions : Fragment() {
} else { } else {
list_repositories?.setOnClickListener { list_repositories?.setOnClickListener {
// Open webview on tv if browser fails // Open webview on tv if browser fails
val isTv = it.context.isTvSettings() openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
openBrowser(PUBLIC_REPOSITORIES_LIST, isTv, this)
} }
} }
} }
@ -86,8 +85,7 @@ class SetupFragmentExtensions : Fragment() {
val isSetup = arguments?.getBoolean(SETUP_EXTENSION_BUNDLE_IS_SETUP) ?: false val isSetup = arguments?.getBoolean(SETUP_EXTENSION_BUNDLE_IS_SETUP) ?: false
view_public_repositories_button?.setOnClickListener { view_public_repositories_button?.setOnClickListener {
val isTv = it.context.isTvSettings() openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
openBrowser(PUBLIC_REPOSITORIES_LIST, isTv, this)
} }
with(context) { with(context) {

View file

@ -164,7 +164,7 @@ class ChromecastSubtitlesFragment : Fragment() {
state = getCurrentSavedStyle() state = getCurrentSavedStyle()
context?.updateState() context?.updateState()
val isTvSettings = context?.isTvSettings() == true val isTvSettings = isTvSettings()
fun View.setFocusableInTv() { fun View.setFocusableInTv() {
this.isFocusableInTouchMode = isTvSettings this.isFocusableInTouchMode = isTvSettings

View file

@ -243,7 +243,7 @@ class SubtitlesFragment : Fragment() {
state = getCurrentSavedStyle() state = getCurrentSavedStyle()
context?.updateState() context?.updateState()
val isTvTrueSettings = context?.isTrueTvSettings() == true val isTvTrueSettings = isTrueTvSettings()
fun View.setFocusableInTv() { fun View.setFocusableInTv() {
this.isFocusableInTouchMode = isTvTrueSettings this.isFocusableInTouchMode = isTvTrueSettings

View file

@ -407,8 +407,8 @@ object AppUtils {
//private val viewModel: ResultViewModel by activityViewModels() //private val viewModel: ResultViewModel by activityViewModels()
private fun getResultsId(context: Context): Int { private fun getResultsId(): Int {
return if (context.isTrueTvSettings()) { return if (isTrueTvSettings()) {
R.id.global_to_navigation_results_tv R.id.global_to_navigation_results_tv
} else { } else {
R.id.global_to_navigation_results_phone R.id.global_to_navigation_results_phone
@ -424,7 +424,7 @@ object AppUtils {
this.runOnUiThread { this.runOnUiThread {
// viewModelStore.clear() // viewModelStore.clear()
this.navigate( this.navigate(
getResultsId(this.applicationContext ?: return@runOnUiThread), getResultsId(),
ResultFragment.newInstance(url, apiName, startAction, startValue) ResultFragment.newInstance(url, apiName, startAction, startValue)
) )
} }
@ -438,7 +438,7 @@ object AppUtils {
this?.runOnUiThread { this?.runOnUiThread {
// viewModelStore.clear() // viewModelStore.clear()
this.navigate( this.navigate(
getResultsId(this), getResultsId(),
ResultFragment.newInstance(card, startAction, startValue) ResultFragment.newInstance(card, startAction, startValue)
) )
} }

View file

@ -44,7 +44,7 @@ object SingleSelectionHelper {
) { ) {
if (this == null) return if (this == null) return
if (this.isTvSettings()) { if (isTvSettings()) {
val builder = val builder =
AlertDialog.Builder(this, R.style.AlertDialogCustom) AlertDialog.Builder(this, R.style.AlertDialogCustom)
.setView(R.layout.options_popup_tv) .setView(R.layout.options_popup_tv)

View file

@ -0,0 +1,30 @@
package com.lagradost.cloudstream3.widget
import android.content.Context
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
class LinearRecycleViewLayoutManager(
val context: Context,
val nextFocusUp: Int,
val nextFocusDown: Int
) : LinearLayoutManager(context) {
override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
return try {
val position = getPosition(focused)
val count = itemCount
//println("onInterceptFocusSearch position=$position count=$count focused=$focused direction=$direction")
(if (position == count - 1 && direction == View.FOCUS_DOWN) {
focused.rootView.findViewById(nextFocusDown)
} else if (position == 0 && direction == View.FOCUS_UP) {
focused.rootView.findViewById(nextFocusUp)
} else {
super.onInterceptFocusSearch(focused, direction)
}) ?: super.onInterceptFocusSearch(focused, direction)
} catch (t : Throwable) {
super.onInterceptFocusSearch(focused, direction)
}
}
}

View file

@ -29,7 +29,10 @@
android:textStyle="bold" /> android:textStyle="bold" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:nextFocusDown="@id/repo_name_input"
android:id="@+id/list_repositories" android:id="@+id/list_repositories"
android:nextFocusLeft="@id/apply_btt"
android:nextFocusRight="@id/cancel_btt"
style="@style/WhiteButton" style="@style/WhiteButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
@ -67,6 +70,7 @@
android:autofillHints="username" android:autofillHints="username"
android:hint="@string/repository_name_hint" android:hint="@string/repository_name_hint"
android:inputType="text" android:inputType="text"
android:nextFocusUp="@id/list_repositories"
android:nextFocusLeft="@id/apply_btt" android:nextFocusLeft="@id/apply_btt"
android:nextFocusRight="@id/cancel_btt" android:nextFocusRight="@id/cancel_btt"
android:nextFocusDown="@id/site_url_input" android:nextFocusDown="@id/site_url_input"

View file

@ -11,6 +11,8 @@
<include layout="@layout/standard_toolbar" /> <include layout="@layout/standard_toolbar" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:nextFocusUp="@id/settings_toolbar"
android:nextFocusDown="@id/plugin_storage_appbar"
android:id="@+id/repo_recycler_view" android:id="@+id/repo_recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -23,13 +25,13 @@
tools:visibility="visible" /> tools:visibility="visible" />
<LinearLayout <LinearLayout
tools:visibility="gone"
android:id="@+id/blank_repo_screen" android:id="@+id/blank_repo_screen"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_margin="20dp" android:layout_margin="20dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical"> android:orientation="vertical"
tools:visibility="gone">
<ImageView <ImageView
android:layout_width="30dp" android:layout_width="30dp"
@ -38,134 +40,168 @@
android:src="@drawable/ic_baseline_extension_24" /> android:src="@drawable/ic_baseline_extension_24" />
<TextView <TextView
android:layout_marginBottom="20dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:gravity="center" android:gravity="center"
android:text="@string/blank_repo_message" android:text="@string/blank_repo_message"
android:textSize="16sp" /> android:textSize="16sp" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/list_repositories" android:id="@+id/list_repositories"
android:nextFocusDown="@id/add_repo_button"
style="@style/WhiteButton" style="@style/WhiteButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:nextFocusDown="@id/add_repo_button"
android:text="@string/view_public_repositories_button" /> android:text="@string/view_public_repositories_button" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:foreground="@drawable/outline_drawable"
android:nextFocusUp="@id/add_repo_button"
android:id="@+id/plugin_storage_appbar" android:id="@+id/plugin_storage_appbar"
android:nextFocusRight="@id/add_repo_button_imageview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="80dp" android:layout_height="80dp"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:background="?attr/primaryGrayBackground" android:background="?attr/primaryGrayBackground"
android:orientation="vertical" android:foreground="@drawable/outline_drawable"
android:padding="10dp" android:padding="10dp"
android:visibility="visible" android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent" android:orientation="horizontal">
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:text="@string/extensions"
android:textColor="?attr/textColor" />
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_weight="1"
android:layout_height="12dp" android:layout_width="0dp"
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<View
android:id="@+id/plugin_download"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="?attr/white"
tools:layout_weight="0.5" />
<View
android:id="@+id/plugin_disabled"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="?attr/colorPrimary"
tools:layout_weight="0.10" />
<View
android:id="@+id/plugin_not_downloaded"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="?attr/grayTextColor"
tools:layout_weight="0.10" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal">
<View android:orientation="vertical">
android:layout_width="10dp"
android:layout_height="10dp" <TextView
android:layout_gravity="center_vertical" android:layout_width="wrap_content"
android:layout_marginTop="5dp" android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:background="?attr/white" /> android:text="@string/extensions"
android:textColor="?attr/textColor" />
<TextView <LinearLayout
android:id="@+id/plugin_download_txt" android:layout_width="fill_parent"
android:layout_width="wrap_content" android:layout_height="12dp"
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<View
android:id="@+id/plugin_download"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="?attr/white"
tools:layout_weight="0.5" />
<View
android:id="@+id/plugin_disabled"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="?attr/colorPrimary"
tools:layout_weight="0.10" />
<View
android:id="@+id/plugin_not_downloaded"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="?attr/grayTextColor"
tools:layout_weight="0.10" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:orientation="horizontal">
android:textColor="?attr/textColor"
android:textSize="12sp"
tools:text="Downloaded: 7" />
<View <View
android:layout_width="10dp" android:layout_width="10dp"
android:layout_height="10dp" android:layout_height="10dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_marginTop="5dp"
android:background="?attr/colorPrimary" /> android:layout_marginEnd="5dp"
android:layout_marginBottom="5dp"
android:background="?attr/white" />
<TextView <TextView
android:id="@+id/plugin_disabled_txt" android:id="@+id/plugin_download_txt"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:textSize="12sp" android:textSize="12sp"
tools:text="Disabled: 3" /> tools:text="Downloaded: 7" />
<View <View
android:layout_width="10dp" android:layout_width="10dp"
android:layout_height="10dp" android:layout_height="10dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_margin="5dp" android:layout_margin="5dp"
android:background="?attr/grayTextColor" /> android:background="?attr/colorPrimary" />
<TextView <TextView
android:id="@+id/plugin_not_downloaded_txt" android:id="@+id/plugin_disabled_txt"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:textSize="12sp" android:textSize="12sp"
tools:text="Not downloaded 3" /> tools:text="Disabled: 3" />
<View
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:background="?attr/grayTextColor" />
<TextView
android:id="@+id/plugin_not_downloaded_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="?attr/textColor"
android:textSize="12sp"
tools:text="Not downloaded 3" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/add_repo_button_imageview_holder"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:textColor="?attr/textColor"
android:layout_gravity="center"
android:text="@string/add_repository"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:background="@drawable/outline_drawable"
android:nextFocusLeft="@id/plugin_storage_appbar"
android:layout_gravity="center"
android:id="@+id/add_repo_button_imageview"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_baseline_add_24"
android:contentDescription="@string/add_repository"
app:tint="?attr/textColor">
</ImageView>
</LinearLayout>
</LinearLayout> </LinearLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/add_repo_button" android:id="@+id/add_repo_button"
style="@style/ExtendedFloatingActionButton"
android:foreground="@drawable/outline_drawable" android:foreground="@drawable/outline_drawable"
android:nextFocusDown="@id/plugin_storage_appbar" android:nextFocusDown="@id/plugin_storage_appbar"
style="@style/ExtendedFloatingActionButton"
android:text="@string/add_repository" android:text="@string/add_repository"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:translationY="-80dp" android:translationY="-80dp"

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/repository_item_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/outline_drawable"
android:nextFocusRight="@id/action_button"
android:orientation="horizontal"
android:padding="12dp">
<ImageView
android:id="@+id/entry_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="start|center_vertical"
android:layout_marginEnd="16dp"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_github_logo" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/main_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/textColor"
android:textSize="16sp"
tools:text="Test repository" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/lang_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="5dp"
android:text="🇷🇼"
android:textColor="?attr/grayTextColor"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/ext_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="5dp"
android:text="v1"
android:textColor="?attr/grayTextColor"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/ext_filesize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="5dp"
android:text="100MB"
android:textColor="?attr/grayTextColor"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/nsfw_marker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/is_adult"
android:textColor="@color/adultColor"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
<TextView
android:id="@+id/sub_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/grayTextColor"
android:textSize="12sp"
tools:text="https://github.com/..." />
</LinearLayout>
<ImageView
android:id="@+id/action_settings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="12dp"
android:background="@drawable/outline_drawable"
android:contentDescription="@string/title_settings"
android:visibility="gone"
app:srcCompat="@drawable/ic_baseline_tune_24"
tools:visibility="visible" />
<ImageView
android:id="@+id/action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_marginStart="10dp"
android:background="@drawable/outline_drawable"
android:clickable="true"
android:contentDescription="@string/download"
android:focusable="true"
android:nextFocusLeft="@id/repository_item_root"
android:padding="12dp"
tools:src="@drawable/ic_baseline_add_24" />
</LinearLayout>