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>, 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>, 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>, 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 } }