Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor and split up extensions #270

Merged
merged 1 commit into from
Dec 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app/src/main/java/org/jellyfin/mobile/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.os.Bundle
import android.os.IBinder
import android.view.OrientationEventListener
import androidx.appcompat.app.AppCompatActivity
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
Expand All @@ -17,7 +16,10 @@ import org.jellyfin.mobile.cast.IChromecast
import org.jellyfin.mobile.fragment.ConnectFragment
import org.jellyfin.mobile.fragment.WebViewFragment
import org.jellyfin.mobile.player.PlayerFragment
import org.jellyfin.mobile.utils.*
import org.jellyfin.mobile.utils.Constants
import org.jellyfin.mobile.utils.PermissionRequestHelper
import org.jellyfin.mobile.utils.SmartOrientationListener
import org.jellyfin.mobile.utils.replaceFragment
import org.jellyfin.mobile.viewmodel.MainViewModel
import org.jellyfin.mobile.viewmodel.ServerState
import org.jellyfin.mobile.webapp.RemotePlayerService
Expand All @@ -31,8 +33,6 @@ class MainActivity : AppCompatActivity() {
val chromecast: IChromecast = Chromecast()
private val permissionRequestHelper: PermissionRequestHelper by inject()

val rootView: CoordinatorLayout by lazyView(R.id.root_view)

var serviceBinder: RemotePlayerService.ServiceBinder? = null
private set
private val serviceConnection: ServiceConnection = object : ServiceConnection {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ class WebViewFragment : Fragment() {
// offsetting 100dp from the center in both directions
// uses the maximum available space
val verticalCenter = webView.measuredHeight / 2
val offset = webView.context.dip(100)
val offset = webView.resources.dip(100)

// Arbitrary, currently 2x minimum touch target size
val exclusionWidth = webView.context.dip(96)
val exclusionWidth = webView.resources.dip(96)

webView.systemGestureExclusionRects = listOf(
Rect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ class PlayerFragment : Fragment() {
return false

// Check whether swipe was started in excluded region
if (firstEvent.y < GESTURE_EXCLUSION_AREA_TOP * resources.displayMetrics.density)
if (firstEvent.y < resources.dip(GESTURE_EXCLUSION_AREA_TOP))
return false

// Check whether swipe was oriented vertically
Expand Down
47 changes: 47 additions & 0 deletions app/src/main/java/org/jellyfin/mobile/utils/ActivityExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@file:Suppress("DEPRECATION")

package org.jellyfin.mobile.utils

import android.app.Activity
import android.content.pm.ActivityInfo
import android.graphics.Point
import android.view.Surface
import android.view.View
import android.view.WindowManager

const val STABLE_LAYOUT_FLAGS = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

const val FULLSCREEN_FLAGS = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

fun Activity.isFullscreen() = window.decorView.systemUiVisibility.hasFlag(FULLSCREEN_FLAGS)

fun Activity.enableFullscreen() {
window.apply {
decorView.systemUiVisibility = FULLSCREEN_FLAGS
addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
}
}

fun Activity.disableFullscreen(keepStableLayout: Boolean = false) {
window.apply {
decorView.systemUiVisibility = if (keepStableLayout) STABLE_LAYOUT_FLAGS else 0
clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
}
}

fun Activity.lockOrientation() {
val display = windowManager.defaultDisplay
val size = Point().also(display::getSize)
val height = size.y
val width = size.x
requestedOrientation = when (display.rotation) {
Surface.ROTATION_90 -> if (width > height) ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
Surface.ROTATION_180 -> if (height > width) ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
Surface.ROTATION_270 -> if (width > height) ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
else -> if (height > width) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
}
}
24 changes: 24 additions & 0 deletions app/src/main/java/org/jellyfin/mobile/utils/FragmentExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@file:Suppress("NOTHING_TO_INLINE")

package org.jellyfin.mobile.utils

import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.add
import androidx.fragment.app.replace
import org.jellyfin.mobile.MainActivity
import org.jellyfin.mobile.R

inline fun <reified T : Fragment> FragmentManager.addFragment() {
beginTransaction().apply {
add<T>(R.id.fragment_container)
addToBackStack(null)
}.commit()
}

inline fun <reified T : Fragment> FragmentManager.replaceFragment(args: Bundle? = null) {
beginTransaction().replace<T>(R.id.fragment_container, args = args).commit()
}

inline fun Fragment.requireMainActivity(): MainActivity = requireActivity() as MainActivity
4 changes: 1 addition & 3 deletions app/src/main/java/org/jellyfin/mobile/utils/SystemUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ fun MainActivity.requestNoBatteryOptimizations() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val powerManager: PowerManager = getSystemService(AppCompatActivity.POWER_SERVICE) as PowerManager
if (!appPreferences.ignoreBatteryOptimizations && !powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)) {
Snackbar.make(rootView, R.string.battery_optimizations_message, Snackbar.LENGTH_INDEFINITE).apply {
// Add action
Snackbar.make(findViewById(R.id.root_view), R.string.battery_optimizations_message, Snackbar.LENGTH_INDEFINITE).apply {
setAction(android.R.string.ok) {
try {
val intent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)
Expand All @@ -46,7 +45,6 @@ fun MainActivity.requestNoBatteryOptimizations() {
// Ignore after the user interacted with the snackbar at least once
appPreferences.ignoreBatteryOptimizations = true
}

show()
}
}
Expand Down
80 changes: 8 additions & 72 deletions app/src/main/java/org/jellyfin/mobile/utils/UIExtensions.kt
Original file line number Diff line number Diff line change
@@ -1,75 +1,23 @@
@file:Suppress("NOTHING_TO_INLINE")
@file:Suppress("unused", "NOTHING_TO_INLINE")

package org.jellyfin.mobile.utils

import android.app.Activity
import android.content.Context
import android.content.pm.ActivityInfo
import android.graphics.Point
import android.os.Bundle
import android.view.*
import android.content.res.Resources
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.annotation.StyleRes
import androidx.core.view.ViewCompat
import androidx.core.view.updateMargins
import androidx.fragment.app.*
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.jellyfin.mobile.MainActivity
import org.jellyfin.mobile.R

inline fun <T : View> Activity.lazyView(@IdRes id: Int) =
lazy(LazyThreadSafetyMode.NONE) { findViewById<T>(id) }

@Suppress("DEPRECATION")
const val STABLE_LAYOUT_FLAGS = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

@Suppress("DEPRECATION")
const val FULLSCREEN_FLAGS = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

@Suppress("DEPRECATION")
fun Activity.isFullscreen() = window.decorView.systemUiVisibility.hasFlag(FULLSCREEN_FLAGS)

@Suppress("DEPRECATION")
fun Activity.enableFullscreen() {
window.apply {
decorView.systemUiVisibility = FULLSCREEN_FLAGS
addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
}
}

@Suppress("DEPRECATION")
fun Activity.disableFullscreen(keepStableLayout: Boolean = false) {
window.apply {
decorView.systemUiVisibility = if (keepStableLayout) STABLE_LAYOUT_FLAGS else 0
clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
}
}

@Suppress("DEPRECATION")
fun Activity.lockOrientation() {
val display = windowManager.defaultDisplay
val size = Point().also(display::getSize)
val height = size.y
val width = size.x
requestedOrientation = when (display.rotation) {
Surface.ROTATION_90 -> if (width > height) ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
Surface.ROTATION_180 -> if (height > width) ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
Surface.ROTATION_270 -> if (width > height) ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
else -> if (height > width) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
}
}

inline fun Context.dip(px: Int) = (px * resources.displayMetrics.density).toInt()

inline fun Context.toast(@StringRes text: Int, duration: Int = Toast.LENGTH_SHORT) =
Toast.makeText(this, text, duration).show()
Expand All @@ -81,24 +29,10 @@ inline fun LifecycleOwner.runOnUiThread(noinline block: suspend CoroutineScope.(
lifecycleScope.launch(Dispatchers.Main, block = block)
}

inline fun <reified T : Fragment> FragmentManager.addFragment() {
beginTransaction().apply {
add<T>(R.id.fragment_container)
addToBackStack(null)
}.commit()
}

inline fun <reified T : Fragment> FragmentManager.replaceFragment(args: Bundle? = null) {
beginTransaction().replace<T>(R.id.fragment_container, args = args).commit()
}

fun LayoutInflater.withThemedContext(context: Context, @StyleRes style: Int): LayoutInflater {
return cloneInContext(ContextThemeWrapper(context, style))
}

@Suppress("NOTHING_TO_INLINE")
inline fun Fragment.requireMainActivity(): MainActivity = requireActivity() as MainActivity

fun View.applyWindowInsetsAsMargins() {
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
val layoutParams = layoutParams as ViewGroup.MarginLayoutParams
Expand All @@ -111,3 +45,5 @@ fun View.applyWindowInsetsAsMargins() {
insets
}
}

inline fun Resources.dip(px: Int) = (px * displayMetrics.density).toInt()