Split up ContextExtensions into smaller files
This commit is contained in:
parent
859601a46e
commit
d703fb7946
6 changed files with 163 additions and 150 deletions
|
@ -1,14 +0,0 @@
|
|||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
|
||||
/**
|
||||
* Checks whether if the device has a display cutout (i.e. notch, camera cutout, etc.).
|
||||
*
|
||||
* Only works in Android 9+.
|
||||
*/
|
||||
fun Activity.hasDisplayCutout(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
|
||||
window.decorView.rootWindowInsets?.displayCutout != null
|
||||
}
|
|
@ -1,10 +1,18 @@
|
|||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.content.Context
|
||||
import android.provider.Settings
|
||||
import android.view.ViewPropertyAnimator
|
||||
import android.view.animation.Animation
|
||||
import androidx.constraintlayout.motion.widget.MotionScene.Transition
|
||||
|
||||
/**
|
||||
* Gets the duration multiplier for general animations on the device
|
||||
* @see Settings.Global.ANIMATOR_DURATION_SCALE
|
||||
*/
|
||||
val Context.animatorDurationScale: Float
|
||||
get() = Settings.Global.getFloat(this.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
|
||||
|
||||
/** Scale the duration of this [Animation] by [Context.animatorDurationScale] */
|
||||
fun Animation.applySystemAnimatorScale(context: Context) {
|
||||
this.duration = (this.duration * context.animatorDurationScale).toLong()
|
||||
|
|
|
@ -1,27 +1,18 @@
|
|||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.NotificationManager
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.util.TypedValue
|
||||
import android.view.Display
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
|
@ -34,7 +25,6 @@ import androidx.core.graphics.red
|
|||
import androidx.core.net.toUri
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.TabletUiMode
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate
|
||||
|
@ -112,44 +102,9 @@ fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermi
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to px and takes into account LTR/RTL layout.
|
||||
*/
|
||||
val Float.dpToPxEnd: Float
|
||||
get() = (
|
||||
this * Resources.getSystem().displayMetrics.density *
|
||||
if (Resources.getSystem().isLTR) 1 else -1
|
||||
)
|
||||
|
||||
val Resources.isLTR
|
||||
get() = configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
|
||||
|
||||
val Context.notificationManager: NotificationManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
val Context.connectivityManager: ConnectivityManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
val Context.wifiManager: WifiManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
val Context.powerManager: PowerManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
val Context.displayCompat: Display?
|
||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
display
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
getSystemService<WindowManager>()?.defaultDisplay
|
||||
}
|
||||
|
||||
/** Gets the duration multiplier for general animations on the device
|
||||
* @see Settings.Global.ANIMATOR_DURATION_SCALE
|
||||
*/
|
||||
val Context.animatorDurationScale: Float
|
||||
get() = Settings.Global.getFloat(this.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
|
||||
|
||||
/**
|
||||
* Convenience method to acquire a partial wake lock.
|
||||
*/
|
||||
|
@ -188,7 +143,7 @@ fun Context.openInBrowser(uri: Uri, forceDefaultBrowser: Boolean = false) {
|
|||
}
|
||||
}
|
||||
|
||||
fun Context.defaultBrowserPackageName(): String? {
|
||||
private fun Context.defaultBrowserPackageName(): String? {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
||||
val resolveInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
packageManager.resolveActivity(browserIntent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()))
|
||||
|
@ -210,59 +165,6 @@ fun Context.createFileInCacheDir(name: String): File {
|
|||
return file
|
||||
}
|
||||
|
||||
private const val TABLET_UI_REQUIRED_SCREEN_WIDTH_DP = 720
|
||||
|
||||
// some tablets have screen width like 711dp = 1600px / 2.25
|
||||
private const val TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP = 700
|
||||
|
||||
// make sure icons on the nav rail fit
|
||||
private const val TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP = 600
|
||||
|
||||
fun Context.isTabletUi(): Boolean {
|
||||
return resources.configuration.isTabletUi()
|
||||
}
|
||||
|
||||
fun Configuration.isTabletUi(): Boolean {
|
||||
return smallestScreenWidthDp >= TABLET_UI_REQUIRED_SCREEN_WIDTH_DP
|
||||
}
|
||||
|
||||
fun Configuration.isAutoTabletUiAvailable(): Boolean {
|
||||
return smallestScreenWidthDp >= TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
|
||||
}
|
||||
|
||||
// TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose
|
||||
fun Context.prepareTabletUiContext(): Context {
|
||||
val configuration = resources.configuration
|
||||
val expected = when (Injekt.get<UiPreferences>().tabletUiMode().get()) {
|
||||
TabletUiMode.AUTOMATIC ->
|
||||
configuration.smallestScreenWidthDp >= when (configuration.orientation) {
|
||||
Configuration.ORIENTATION_PORTRAIT -> TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP
|
||||
else -> TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
|
||||
}
|
||||
TabletUiMode.ALWAYS -> true
|
||||
TabletUiMode.LANDSCAPE -> configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
TabletUiMode.NEVER -> false
|
||||
}
|
||||
if (configuration.isTabletUi() != expected) {
|
||||
val overrideConf = Configuration()
|
||||
overrideConf.setTo(configuration)
|
||||
overrideConf.smallestScreenWidthDp = if (expected) {
|
||||
overrideConf.smallestScreenWidthDp.coerceAtLeast(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP)
|
||||
} else {
|
||||
overrideConf.smallestScreenWidthDp.coerceAtMost(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP - 1)
|
||||
}
|
||||
return createConfigurationContext(overrideConf)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if current context is in night mode
|
||||
*/
|
||||
fun Context.isNightMode(): Boolean {
|
||||
return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates night mode Context depending on reader theme/background
|
||||
*
|
||||
|
@ -292,35 +194,6 @@ fun Context.createReaderThemeContext(): Context {
|
|||
return this
|
||||
}
|
||||
|
||||
fun Context.isOnline(): Boolean {
|
||||
val activeNetwork = connectivityManager.activeNetwork ?: return false
|
||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
|
||||
val maxTransport = when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> NetworkCapabilities.TRANSPORT_LOWPAN
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> NetworkCapabilities.TRANSPORT_WIFI_AWARE
|
||||
else -> NetworkCapabilities.TRANSPORT_VPN
|
||||
}
|
||||
return (NetworkCapabilities.TRANSPORT_CELLULAR..maxTransport).any(networkCapabilities::hasTransport)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if device is connected to Wifi.
|
||||
*/
|
||||
fun Context.isConnectedToWifi(): Boolean {
|
||||
if (!wifiManager.isWifiEnabled) return false
|
||||
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val activeNetwork = connectivityManager.activeNetwork ?: return false
|
||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
|
||||
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
|
||||
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
wifiManager.connectionInfo.bssid != null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets document size of provided [Uri]
|
||||
*
|
||||
|
@ -370,11 +243,3 @@ fun Context.getApplicationIcon(pkgName: String): Drawable? {
|
|||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets system's config_navBarNeedsScrim boolean flag added in Android 10, defaults to true.
|
||||
*/
|
||||
fun Context.isNavigationBarNeedsScrim(): Boolean {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ||
|
||||
InternalResourceHelper.getBoolean(this, "config_navBarNeedsScrim", true)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.view.Display
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.core.content.getSystemService
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.TabletUiMode
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
private const val TABLET_UI_REQUIRED_SCREEN_WIDTH_DP = 720
|
||||
|
||||
// some tablets have screen width like 711dp = 1600px / 2.25
|
||||
private const val TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP = 700
|
||||
|
||||
// make sure icons on the nav rail fit
|
||||
private const val TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP = 600
|
||||
|
||||
fun Context.isTabletUi(): Boolean {
|
||||
return resources.configuration.isTabletUi()
|
||||
}
|
||||
|
||||
fun Configuration.isTabletUi(): Boolean {
|
||||
return smallestScreenWidthDp >= TABLET_UI_REQUIRED_SCREEN_WIDTH_DP
|
||||
}
|
||||
|
||||
fun Configuration.isAutoTabletUiAvailable(): Boolean {
|
||||
return smallestScreenWidthDp >= TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
|
||||
}
|
||||
|
||||
// TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose
|
||||
fun Context.prepareTabletUiContext(): Context {
|
||||
val configuration = resources.configuration
|
||||
val expected = when (Injekt.get<UiPreferences>().tabletUiMode().get()) {
|
||||
TabletUiMode.AUTOMATIC ->
|
||||
configuration.smallestScreenWidthDp >= when (configuration.orientation) {
|
||||
Configuration.ORIENTATION_PORTRAIT -> TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP
|
||||
else -> TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
|
||||
}
|
||||
TabletUiMode.ALWAYS -> true
|
||||
TabletUiMode.LANDSCAPE -> configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
TabletUiMode.NEVER -> false
|
||||
}
|
||||
if (configuration.isTabletUi() != expected) {
|
||||
val overrideConf = Configuration()
|
||||
overrideConf.setTo(configuration)
|
||||
overrideConf.smallestScreenWidthDp = if (expected) {
|
||||
overrideConf.smallestScreenWidthDp.coerceAtLeast(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP)
|
||||
} else {
|
||||
overrideConf.smallestScreenWidthDp.coerceAtMost(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP - 1)
|
||||
}
|
||||
return createConfigurationContext(overrideConf)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if current context is in night mode
|
||||
*/
|
||||
fun Context.isNightMode(): Boolean {
|
||||
return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||
}
|
||||
|
||||
val Context.displayCompat: Display?
|
||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
display
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
getSystemService<WindowManager>()?.defaultDisplay
|
||||
}
|
||||
|
||||
val Resources.isLTR
|
||||
get() = configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
|
||||
|
||||
/**
|
||||
* Converts to px and takes into account LTR/RTL layout.
|
||||
*/
|
||||
val Float.dpToPxEnd: Float
|
||||
get() = (
|
||||
this * Resources.getSystem().displayMetrics.density *
|
||||
if (Resources.getSystem().isLTR) 1 else -1
|
||||
)
|
||||
|
||||
/**
|
||||
* Checks whether if the device has a display cutout (i.e. notch, camera cutout, etc.).
|
||||
*
|
||||
* Only works in Android 9+.
|
||||
*/
|
||||
fun Activity.hasDisplayCutout(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
|
||||
window.decorView.rootWindowInsets?.displayCutout != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets system's config_navBarNeedsScrim boolean flag added in Android 10, defaults to true.
|
||||
*/
|
||||
fun Context.isNavigationBarNeedsScrim(): Boolean {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ||
|
||||
InternalResourceHelper.getBoolean(this, "config_navBarNeedsScrim", true)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.Build
|
||||
import androidx.core.content.getSystemService
|
||||
|
||||
val Context.connectivityManager: ConnectivityManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
val Context.wifiManager: WifiManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
fun Context.isOnline(): Boolean {
|
||||
val activeNetwork = connectivityManager.activeNetwork ?: return false
|
||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
|
||||
val maxTransport = when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> NetworkCapabilities.TRANSPORT_LOWPAN
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> NetworkCapabilities.TRANSPORT_WIFI_AWARE
|
||||
else -> NetworkCapabilities.TRANSPORT_VPN
|
||||
}
|
||||
return (NetworkCapabilities.TRANSPORT_CELLULAR..maxTransport).any(networkCapabilities::hasTransport)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if device is connected to Wifi.
|
||||
*/
|
||||
fun Context.isConnectedToWifi(): Boolean {
|
||||
if (!wifiManager.isWifiEnabled) return false
|
||||
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val activeNetwork = connectivityManager.activeNetwork ?: return false
|
||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
|
||||
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
|
||||
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
wifiManager.connectionInfo.bssid != null
|
||||
}
|
||||
}
|
|
@ -1,14 +1,19 @@
|
|||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.Manifest
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationChannelCompat
|
||||
import androidx.core.app.NotificationChannelGroupCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.PermissionChecker
|
||||
import androidx.core.content.getSystemService
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
val Context.notificationManager: NotificationManager
|
||||
get() = getSystemService()!!
|
||||
|
||||
fun Context.notify(id: Int, channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null) {
|
||||
if (PermissionChecker.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PermissionChecker.PERMISSION_GRANTED) {
|
||||
return
|
||||
|
|
Reference in a new issue