diff --git a/app/src/main/java/org/jellyfin/mobile/MainActivity.kt b/app/src/main/java/org/jellyfin/mobile/MainActivity.kt index d8a1529af..e91dd38d3 100644 --- a/app/src/main/java/org/jellyfin/mobile/MainActivity.kt +++ b/app/src/main/java/org/jellyfin/mobile/MainActivity.kt @@ -35,7 +35,6 @@ import org.koin.core.KoinExperimentalAPI class MainActivity : AppCompatActivity() { private val mainViewModel: MainViewModel by viewModel() - val appPreferences: AppPreferences by inject() val chromecast: IChromecast = Chromecast() private val permissionRequestHelper: PermissionRequestHelper by inject() diff --git a/app/src/main/java/org/jellyfin/mobile/fragment/WebViewFragment.kt b/app/src/main/java/org/jellyfin/mobile/fragment/WebViewFragment.kt index 33e616507..9e851d7cd 100644 --- a/app/src/main/java/org/jellyfin/mobile/fragment/WebViewFragment.kt +++ b/app/src/main/java/org/jellyfin/mobile/fragment/WebViewFragment.kt @@ -20,6 +20,7 @@ import android.webkit.WebView import android.widget.Toast import androidx.activity.addCallback import androidx.appcompat.app.AlertDialog +import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.ViewCompat import androidx.core.view.doOnNextLayout import androidx.fragment.app.Fragment @@ -33,7 +34,6 @@ import androidx.webkit.WebViewCompat import androidx.webkit.WebViewFeature import kotlinx.coroutines.launch import org.jellyfin.mobile.AppPreferences -import org.jellyfin.mobile.MainActivity import org.jellyfin.mobile.R import org.jellyfin.mobile.bridge.ExternalPlayer import org.jellyfin.mobile.bridge.NativeInterface @@ -50,6 +50,7 @@ import org.jellyfin.mobile.utils.addFragment import org.jellyfin.mobile.utils.applyDefault import org.jellyfin.mobile.utils.applyWindowInsetsAsMargins import org.jellyfin.mobile.utils.dip +import org.jellyfin.mobile.utils.fadeIn import org.jellyfin.mobile.utils.initLocale import org.jellyfin.mobile.utils.isOutdated import org.jellyfin.mobile.utils.replaceFragment @@ -68,7 +69,7 @@ import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine class WebViewFragment : Fragment(), NativePlayerHost { - private val appPreferences: AppPreferences by inject() + val appPreferences: AppPreferences by inject() private val apiController: ApiController by inject() private val webappFunctionChannel: WebappFunctionChannel by inject() private lateinit var assetsPathHandler: AssetsPathHandler @@ -81,7 +82,8 @@ class WebViewFragment : Fragment(), NativePlayerHost { // UI private var _webViewBinding: FragmentWebviewBinding? = null private val webViewBinding get() = _webViewBinding!! - val webView: WebView get() = webViewBinding.root + val rootView: CoordinatorLayout get() = webViewBinding.root + val webView: WebView get() = webViewBinding.webView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -100,13 +102,14 @@ class WebViewFragment : Fragment(), NativePlayerHost { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _webViewBinding = FragmentWebviewBinding.inflate(inflater, container, false) - return webView.apply { applyWindowInsetsAsMargins() } + return webViewBinding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // Apply window insets + webView.applyWindowInsetsAsMargins() ViewCompat.requestApplyInsets(webView) // Setup exclusion rects for gestures @@ -185,7 +188,7 @@ class WebViewFragment : Fragment(), NativePlayerHost { val user = storedServer.getString("UserId") val token = storedServer.getString("AccessToken") apiController.setupUser(server.id, user, token) - initLocale(user) + webView.initLocale(user) } null } @@ -289,9 +292,10 @@ class WebViewFragment : Fragment(), NativePlayerHost { }.show() } - fun onConnectedToWebapp() { + private fun onConnectedToWebapp() { connected = true - (activity as? MainActivity)?.requestNoBatteryOptimizations() + runOnUiThread { webView.fadeIn() } + requestNoBatteryOptimizations() } fun onSelectServer(error: Boolean = false) = runOnUiThread { @@ -308,7 +312,7 @@ class WebViewFragment : Fragment(), NativePlayerHost { } } - fun onErrorReceived() { + private fun onErrorReceived() { connected = false onSelectServer(error = true) } diff --git a/app/src/main/java/org/jellyfin/mobile/utils/LocaleUtils.kt b/app/src/main/java/org/jellyfin/mobile/utils/LocaleUtils.kt index 85c9990ff..02ddf5c3c 100644 --- a/app/src/main/java/org/jellyfin/mobile/utils/LocaleUtils.kt +++ b/app/src/main/java/org/jellyfin/mobile/utils/LocaleUtils.kt @@ -3,20 +3,20 @@ package org.jellyfin.mobile.utils import android.content.Context import android.content.res.Configuration import android.os.Build -import org.jellyfin.mobile.fragment.WebViewFragment +import android.webkit.WebView import timber.log.Timber import java.util.Locale import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -suspend fun WebViewFragment.initLocale(userId: String) { +suspend fun WebView.initLocale(userId: String) { // Try to set locale via user settings val userSettings = suspendCoroutine { continuation -> - webView.evaluateJavascript("window.localStorage.getItem('$userId-language')") { result -> + evaluateJavascript("window.localStorage.getItem('$userId-language')") { result -> continuation.resume(result) } } - if (requireContext().setLocale(userSettings.unescapeJson())) + if (context.setLocale(userSettings.unescapeJson())) return // Fallback to device locale diff --git a/app/src/main/java/org/jellyfin/mobile/utils/SystemUtils.kt b/app/src/main/java/org/jellyfin/mobile/utils/SystemUtils.kt index 1822bbe51..242f7c0fc 100644 --- a/app/src/main/java/org/jellyfin/mobile/utils/SystemUtils.kt +++ b/app/src/main/java/org/jellyfin/mobile/utils/SystemUtils.kt @@ -24,7 +24,6 @@ import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeout import org.jellyfin.mobile.AppPreferences import org.jellyfin.mobile.BuildConfig -import org.jellyfin.mobile.MainActivity import org.jellyfin.mobile.R import org.jellyfin.mobile.fragment.WebViewFragment import org.jellyfin.mobile.settings.ExternalPlayerPackage @@ -34,11 +33,11 @@ import java.io.File import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -fun MainActivity.requestNoBatteryOptimizations() { +fun WebViewFragment.requestNoBatteryOptimizations() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val powerManager: PowerManager = getSystemService(AppCompatActivity.POWER_SERVICE) as PowerManager + val powerManager: PowerManager = requireContext().getSystemService(AppCompatActivity.POWER_SERVICE) as PowerManager if (!appPreferences.ignoreBatteryOptimizations && !powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)) { - Snackbar.make(findViewById(R.id.root_view), R.string.battery_optimizations_message, Snackbar.LENGTH_INDEFINITE).apply { + Snackbar.make(rootView, R.string.battery_optimizations_message, Snackbar.LENGTH_INDEFINITE).apply { setAction(android.R.string.ok) { try { val intent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS) diff --git a/app/src/main/java/org/jellyfin/mobile/utils/UIExtensions.kt b/app/src/main/java/org/jellyfin/mobile/utils/UIExtensions.kt index 53c4a5535..aebe3a9de 100644 --- a/app/src/main/java/org/jellyfin/mobile/utils/UIExtensions.kt +++ b/app/src/main/java/org/jellyfin/mobile/utils/UIExtensions.kt @@ -14,7 +14,9 @@ import androidx.annotation.StringRes import androidx.annotation.StyleRes import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.core.view.isVisible import androidx.core.view.updateMargins +import androidx.interpolator.view.animation.LinearOutSlowInInterpolator import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.CoroutineScope @@ -46,6 +48,18 @@ fun View.applyWindowInsetsAsMargins() { } } +fun View.fadeIn() { + alpha = 0f + isVisible = true + animate().apply { + alpha(1f) + @Suppress("MagicNumber") + duration = 300L + interpolator = LinearOutSlowInInterpolator() + withLayer() + } +} + inline fun Resources.dip(px: Int) = (px * displayMetrics.density).toInt() inline var Window.brightness: Float diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 1c925b76e..19058b67d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,14 +1,8 @@ - - - - + tools:layout="@layout/fragment_connect" + tools:viewBindingIgnore="true" /> diff --git a/app/src/main/res/layout/fragment_webview.xml b/app/src/main/res/layout/fragment_webview.xml index 21c089b8b..d7e307b05 100644 --- a/app/src/main/res/layout/fragment_webview.xml +++ b/app/src/main/res/layout/fragment_webview.xml @@ -1,5 +1,12 @@ - + android:background="@color/theme_background"> + + +