2021-05-12 21:51:02 +00:00
|
|
|
package com.lagradost.cloudstream3.ui
|
|
|
|
|
|
|
|
import android.content.Context
|
|
|
|
import android.util.AttributeSet
|
|
|
|
import android.view.View
|
2023-09-15 22:30:34 +00:00
|
|
|
import androidx.core.view.children
|
2021-05-12 21:51:02 +00:00
|
|
|
import androidx.recyclerview.widget.GridLayoutManager
|
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
|
|
import kotlin.math.abs
|
|
|
|
|
2023-01-28 22:38:02 +00:00
|
|
|
class GrdLayoutManager(val context: Context, _spanCount: Int) :
|
|
|
|
GridLayoutManager(context, _spanCount) {
|
2021-05-12 21:51:02 +00:00
|
|
|
override fun onFocusSearchFailed(
|
|
|
|
focused: View,
|
|
|
|
focusDirection: Int,
|
|
|
|
recycler: RecyclerView.Recycler,
|
|
|
|
state: RecyclerView.State
|
|
|
|
): View? {
|
|
|
|
return try {
|
|
|
|
val fromPos = getPosition(focused)
|
|
|
|
val nextPos = getNextViewPos(fromPos, focusDirection)
|
|
|
|
findViewByPosition(nextPos)
|
|
|
|
} catch (e: Exception) {
|
|
|
|
null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-28 02:18:28 +00:00
|
|
|
/*override fun onRequestChildFocus(
|
2021-05-12 21:51:02 +00:00
|
|
|
parent: RecyclerView,
|
|
|
|
state: RecyclerView.State,
|
|
|
|
child: View,
|
|
|
|
focused: View?
|
|
|
|
): Boolean {
|
|
|
|
// android.widget.FrameLayout$LayoutParams cannot be cast to androidx.recyclerview.widget.RecyclerView$LayoutParams
|
|
|
|
return try {
|
2023-07-28 02:18:28 +00:00
|
|
|
if(focused != null) {
|
|
|
|
// val pos = maxOf(0, getPosition(focused) - 2) // IDK WHY
|
|
|
|
val pos = getPosition(focused)
|
|
|
|
if(pos >= 0) parent.scrollToPosition(pos)
|
|
|
|
}
|
|
|
|
|
2021-05-12 21:51:02 +00:00
|
|
|
super.onRequestChildFocus(parent, state, child, focused)
|
2023-01-28 22:38:02 +00:00
|
|
|
} catch (e: Exception) {
|
2021-05-12 21:51:02 +00:00
|
|
|
false
|
|
|
|
}
|
2023-07-28 02:18:28 +00:00
|
|
|
}*/
|
2021-05-12 21:51:02 +00:00
|
|
|
|
|
|
|
// Allows moving right and left with focus https://gist.github.com/vganin/8930b41f55820ec49e4d
|
|
|
|
override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
|
|
|
|
return try {
|
|
|
|
val fromPos = getPosition(focused)
|
|
|
|
val nextPos = getNextViewPos(fromPos, direction)
|
|
|
|
findViewByPosition(nextPos)
|
|
|
|
} catch (e: Exception) {
|
|
|
|
null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getNextViewPos(fromPos: Int, direction: Int): Int {
|
|
|
|
val offset = calcOffsetToNextView(direction)
|
|
|
|
|
|
|
|
if (hitBorder(fromPos, offset)) {
|
|
|
|
return fromPos
|
|
|
|
}
|
|
|
|
|
|
|
|
return fromPos + offset
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun calcOffsetToNextView(direction: Int): Int {
|
2022-03-20 22:31:56 +00:00
|
|
|
val spanCount = this.spanCount
|
2021-05-12 21:51:02 +00:00
|
|
|
val orientation = this.orientation
|
|
|
|
|
2023-07-28 02:18:28 +00:00
|
|
|
// fixes arabic by inverting left and right layout focus
|
2023-09-15 22:30:34 +00:00
|
|
|
val correctDirection = if (this.isLayoutRTL) {
|
|
|
|
when (direction) {
|
2023-07-28 02:18:28 +00:00
|
|
|
View.FOCUS_RIGHT -> View.FOCUS_LEFT
|
|
|
|
View.FOCUS_LEFT -> View.FOCUS_RIGHT
|
|
|
|
else -> direction
|
|
|
|
}
|
|
|
|
} else direction
|
|
|
|
|
2021-05-12 21:51:02 +00:00
|
|
|
if (orientation == VERTICAL) {
|
2023-07-28 02:18:28 +00:00
|
|
|
when (correctDirection) {
|
2021-05-12 21:51:02 +00:00
|
|
|
View.FOCUS_DOWN -> {
|
|
|
|
return spanCount
|
|
|
|
}
|
2023-09-15 22:30:34 +00:00
|
|
|
|
2021-05-12 21:51:02 +00:00
|
|
|
View.FOCUS_UP -> {
|
|
|
|
return -spanCount
|
|
|
|
}
|
2023-09-15 22:30:34 +00:00
|
|
|
|
2021-05-12 21:51:02 +00:00
|
|
|
View.FOCUS_RIGHT -> {
|
|
|
|
return 1
|
|
|
|
}
|
2023-09-15 22:30:34 +00:00
|
|
|
|
2021-05-12 21:51:02 +00:00
|
|
|
View.FOCUS_LEFT -> {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (orientation == HORIZONTAL) {
|
2023-07-28 02:18:28 +00:00
|
|
|
when (correctDirection) {
|
2021-05-12 21:51:02 +00:00
|
|
|
View.FOCUS_DOWN -> {
|
|
|
|
return 1
|
|
|
|
}
|
2023-09-15 22:30:34 +00:00
|
|
|
|
2021-05-12 21:51:02 +00:00
|
|
|
View.FOCUS_UP -> {
|
|
|
|
return -1
|
|
|
|
}
|
2023-09-15 22:30:34 +00:00
|
|
|
|
2021-05-12 21:51:02 +00:00
|
|
|
View.FOCUS_RIGHT -> {
|
|
|
|
return spanCount
|
|
|
|
}
|
2023-09-15 22:30:34 +00:00
|
|
|
|
2021-05-12 21:51:02 +00:00
|
|
|
View.FOCUS_LEFT -> {
|
|
|
|
return -spanCount
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun hitBorder(from: Int, offset: Int): Boolean {
|
|
|
|
val spanCount = spanCount
|
|
|
|
|
|
|
|
return if (abs(offset) == 1) {
|
|
|
|
val spanIndex = from % spanCount
|
|
|
|
val newSpanIndex = spanIndex + offset
|
|
|
|
newSpanIndex < 0 || newSpanIndex >= spanCount
|
|
|
|
} else {
|
|
|
|
val newPos = from + offset
|
|
|
|
newPos in spanCount..-1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-09 21:59:37 +00:00
|
|
|
// https://riptutorial.com/android/example/4810/gridlayoutmanager-with-dynamic-span-count
|
2021-05-12 21:51:02 +00:00
|
|
|
class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
|
|
|
RecyclerView(context, attrs) {
|
|
|
|
|
|
|
|
private val manager = GrdLayoutManager(context, 2) // THIS CONTROLS SPANS
|
|
|
|
|
|
|
|
private var columnWidth = -1
|
|
|
|
|
|
|
|
var spanCount = 0
|
|
|
|
set(value) {
|
|
|
|
field = value
|
|
|
|
if (value > 0) {
|
|
|
|
manager.spanCount = value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val itemWidth: Int
|
|
|
|
get() = measuredWidth / manager.spanCount
|
|
|
|
|
|
|
|
init {
|
|
|
|
if (attrs != null) {
|
|
|
|
val attrsArray = intArrayOf(android.R.attr.columnWidth)
|
|
|
|
val array = context.obtainStyledAttributes(attrs, attrsArray)
|
|
|
|
columnWidth = array.getDimensionPixelSize(0, -1)
|
|
|
|
array.recycle()
|
|
|
|
}
|
|
|
|
|
|
|
|
layoutManager = manager
|
|
|
|
}
|
2023-09-15 22:30:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recyclerview wherein the max item width or height is set by the biggest view to prevent inconsistent view sizes.
|
|
|
|
*/
|
|
|
|
class MaxRecyclerView(ctx: Context, attrs: AttributeSet) : RecyclerView(ctx, attrs) {
|
|
|
|
private var biggestObserved: Int = 0
|
|
|
|
private val orientation = LayoutManager.getProperties(context, attrs, 0, 0).orientation
|
|
|
|
private val isHorizontal = orientation == HORIZONTAL
|
|
|
|
private fun View.updateMaxSize() {
|
|
|
|
if (isHorizontal) {
|
|
|
|
this.minimumHeight = biggestObserved
|
|
|
|
} else {
|
|
|
|
this.minimumWidth = biggestObserved
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onChildAttachedToWindow(child: View) {
|
|
|
|
child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
|
|
|
|
val observed = if (isHorizontal) child.measuredHeight else child.measuredWidth
|
|
|
|
if (observed > biggestObserved) {
|
|
|
|
biggestObserved = observed
|
|
|
|
children.forEach { it.updateMaxSize() }
|
|
|
|
} else {
|
|
|
|
child.updateMaxSize()
|
|
|
|
}
|
|
|
|
super.onChildAttachedToWindow(child)
|
|
|
|
}
|
2021-05-12 21:51:02 +00:00
|
|
|
}
|