AquaStream/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt

356 lines
12 KiB
Kotlin

package com.lagradost.cloudstream3.utils
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AppOpsManager
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Resources
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.Gravity
import android.view.MenuItem
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import androidx.annotation.*
import androidx.appcompat.view.ContextThemeWrapper
import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.widget.PopupMenu
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.navigation.fragment.NavHostFragment
import androidx.preference.PreferenceManager
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import kotlin.math.roundToInt
object UIHelper {
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.checkWrite(): Boolean {
return (ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
== PackageManager.PERMISSION_GRANTED)
}
fun Activity.requestRW() {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
),
1337
)
}
fun Fragment.hideKeyboard() {
activity?.window?.decorView?.clearFocus()
view.let {
if (it != null) {
activity?.hideKeyboard(it)
}
}
}
fun Activity?.navigate(@IdRes navigation : Int, arguments : Bundle? = null) {
if(this is FragmentActivity) {
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.navController?.navigate(
navigation, arguments
)
}
}
@ColorInt
fun Context.getResourceColor(@AttrRes resource: Int, alphaFactor: Float = 1f): Int {
val typedArray = obtainStyledAttributes(intArrayOf(resource))
val color = typedArray.getColor(0, 0)
typedArray.recycle()
if (alphaFactor < 1f) {
val alpha = (color.alpha * alphaFactor).roundToInt()
return Color.argb(alpha, color.red, color.green, color.blue)
}
return color
}
fun ImageView?.setImage(url : String?) {
if(this == null || url.isNullOrBlank()) return
try {
GlideApp.with(this.context)
.load(GlideUrl(url))
.into(this)
} catch (e : Exception) {
logError(e)
}
}
fun adjustAlpha(@ColorInt color: Int, factor: Float): Int {
val alpha = (Color.alpha(color) * factor).roundToInt()
val red = Color.red(color)
val green = Color.green(color)
val blue = Color.blue(color)
return Color.argb(alpha, red, green, blue)
}
fun Context.colorFromAttribute(attribute: Int): Int {
val attributes = obtainStyledAttributes(intArrayOf(attribute))
val color = attributes.getColor(0, 0)
attributes.recycle()
return color
}
fun Activity.hideSystemUI() {
// Enables regular immersive mode.
// For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
// Set the content to appear under the system bars so that the
// content doesn't resize when the system bars hide and show.
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
// Hide the nav bar and status bar
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN
// or View.SYSTEM_UI_FLAG_LOW_PROFILE
)
// window.addFlags(View.KEEP_SCREEN_ON)
}
fun FragmentActivity.popCurrentPage() {
this.onBackPressed()
/*val currentFragment = supportFragmentManager.fragments.lastOrNull {
it.isVisible
} ?: return
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.enter_anim,
R.anim.exit_anim,
R.anim.pop_enter,
R.anim.pop_exit
)
.remove(currentFragment)
.commitAllowingStateLoss()*/
}
/*
fun FragmentActivity.popCurrentPage(isInPlayer: Boolean, isInExpandedView: Boolean, isInResults: Boolean) {
val currentFragment = supportFragmentManager.fragments.lastOrNull {
it.isVisible
}
?: //this.onBackPressed()
return
/*
if (tvActivity == null) {
requestedOrientation = if (settingsManager?.getBoolean("force_landscape", false) == true) {
ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
} else {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}*/
// No fucked animations leaving the player :)
when {
isInPlayer -> {
supportFragmentManager.beginTransaction()
//.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.pop_enter, R.anim.pop_exit)
.remove(currentFragment)
.commitAllowingStateLoss()
}
isInExpandedView && !isInResults -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.enter_anim,//R.anim.enter_from_right,
R.anim.exit_anim,//R.anim.exit_to_right,
R.anim.pop_enter,
R.anim.pop_exit
)
.remove(currentFragment)
.commitAllowingStateLoss()
}
else -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit)
.remove(currentFragment)
.commitAllowingStateLoss()
}
}
}*/
fun Context.getStatusBarHeight(): Int {
var result = 0
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
if (resourceId > 0) {
result = resources.getDimensionPixelSize(resourceId)
}
return result
}
fun Context.fixPaddingStatusbar(v: View) {
v.setPadding(v.paddingLeft, v.paddingTop + getStatusBarHeight(), v.paddingRight, v.paddingBottom)
}
fun Context.getNavigationBarHeight(): Int {
var result = 0
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
if (resourceId > 0) {
result = resources.getDimensionPixelSize(resourceId)
}
return result
}
private fun Context.getGridFormat(): String {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
return settingsManager.getString(getString(R.string.grid_format_key), "grid")!!
}
fun Context.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 Context.getGridIsCompact(): Boolean {
return getGridFormat() != "grid"
}
fun Activity.changeStatusBarState(hide: Boolean): Int {
return if (hide) {
window?.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
0
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
this.getStatusBarHeight()
}
}
// Shows the system bars by removing all the flags
// except for the ones that make the content appear under the system bars.
fun Activity.showSystemUI() {
window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
) // or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
// window.clearFlags(View.KEEP_SCREEN_ON)
}
fun Context.shouldShowPIPMode(isInPlayer: Boolean): Boolean {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
return settingsManager?.getBoolean("pip_enabled", true) ?: true && isInPlayer
}
@RequiresApi(Build.VERSION_CODES.O)
fun Context.hasPIPPermission(): Boolean {
val appOps =
getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
return appOps.checkOpNoThrow(
AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
android.os.Process.myUid(),
packageName
) == AppOpsManager.MODE_ALLOWED
}
fun Context.hideKeyboard(view: View) {
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}
/**id, icon, stringRes */
@SuppressLint("RestrictedApi")
fun View.popupMenu(
items: List<Triple<Int, Int, Int>>,
onMenuItemClick: MenuItem.() -> Unit,
): PopupMenu {
val ctw = ContextThemeWrapper(context, R.style.PopupMenu)
val popup = PopupMenu(ctw, this, Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0)
items.forEach { (id, icon, stringRes) ->
popup.menu.add(0, id, 0, stringRes).setIcon(icon)
}
(popup.menu as? MenuBuilder)?.setOptionalIconsVisible(true)
popup.setOnMenuItemClickListener {
it.onMenuItemClick()
true
}
popup.show()
return popup
}
/**id, stringRes */
@SuppressLint("RestrictedApi")
fun View.popupMenuNoIcons(
items: List<Pair<Int, Int>>,
onMenuItemClick: MenuItem.() -> Unit,
): PopupMenu {
val ctw = ContextThemeWrapper(context, R.style.PopupMenu)
val popup = PopupMenu(ctw, this, Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0)
items.forEach { (id, stringRes) ->
popup.menu.add(0, id, 0, stringRes)
}
(popup.menu as? MenuBuilder)?.setOptionalIconsVisible(true)
popup.setOnMenuItemClickListener {
it.onMenuItemClick()
true
}
popup.show()
return popup
}
/**id, string */
@SuppressLint("RestrictedApi")
fun View.popupMenuNoIconsAndNoStringRes(
items: List<Pair<Int, String>>,
onMenuItemClick: MenuItem.() -> Unit,
): PopupMenu {
val ctw = ContextThemeWrapper(context, R.style.PopupMenu)
val popup = PopupMenu(ctw, this, Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0)
items.forEach { (id, string) ->
popup.menu.add(0, id, 0, string)
}
(popup.menu as? MenuBuilder)?.setOptionalIconsVisible(true)
popup.setOnMenuItemClickListener {
it.onMenuItemClick()
true
}
popup.show()
return popup
}
}