mirror of
https://github.com/jellyfin/jellyfin-android.git
synced 2026-10-07 00:11:58 +02:00
Merge pull request #326 from jellyfin/webview-checks
Check WebView support and version
This commit is contained in:
@@ -7,6 +7,7 @@ import android.content.ServiceConnection
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.view.OrientationEventListener
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.flow.collect
|
||||
@@ -19,6 +20,7 @@ import org.jellyfin.mobile.player.PlayerFragment
|
||||
import org.jellyfin.mobile.utils.Constants
|
||||
import org.jellyfin.mobile.utils.PermissionRequestHelper
|
||||
import org.jellyfin.mobile.utils.SmartOrientationListener
|
||||
import org.jellyfin.mobile.utils.isWebViewSupported
|
||||
import org.jellyfin.mobile.utils.replaceFragment
|
||||
import org.jellyfin.mobile.viewmodel.MainViewModel
|
||||
import org.jellyfin.mobile.viewmodel.ServerState
|
||||
@@ -53,6 +55,19 @@ class MainActivity : AppCompatActivity() {
|
||||
// Bind player service
|
||||
bindService(Intent(this, RemotePlayerService::class.java), serviceConnection, Service.BIND_AUTO_CREATE)
|
||||
|
||||
// Check WebView support
|
||||
if (!isWebViewSupported()) {
|
||||
AlertDialog.Builder(this).apply {
|
||||
setTitle(R.string.dialog_web_view_not_supported)
|
||||
setMessage(R.string.dialog_web_view_not_supported_message)
|
||||
setCancelable(false)
|
||||
setNegativeButton(R.string.dialog_button_close_app) { _, _ ->
|
||||
finish()
|
||||
}
|
||||
}.show()
|
||||
return
|
||||
}
|
||||
|
||||
// Load UI
|
||||
lifecycleScope.launch {
|
||||
mainViewModel.serverState.collect { state ->
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.jellyfin.mobile.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
@@ -14,6 +13,7 @@ import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import androidx.activity.addCallback
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
@@ -22,6 +22,7 @@ import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.webkit.WebResourceErrorCompat
|
||||
import androidx.webkit.WebViewClientCompat
|
||||
import androidx.webkit.WebViewCompat
|
||||
import androidx.webkit.WebViewFeature
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jellyfin.apiclient.interaction.ApiClient
|
||||
@@ -126,8 +127,41 @@ class WebViewFragment : Fragment(), NativePlayerHost {
|
||||
_webViewBinding = null
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
private fun WebView.initialize() {
|
||||
if (isOutdated()) { // Check WebView version
|
||||
AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle(R.string.dialog_web_view_outdated)
|
||||
setMessage(R.string.dialog_web_view_outdated_message)
|
||||
setCancelable(false)
|
||||
|
||||
val webViewPackage = WebViewCompat.getCurrentWebViewPackage(context)
|
||||
if (webViewPackage != null) {
|
||||
setPositiveButton(R.string.dialog_button_check_for_updates) { _, _ ->
|
||||
val marketUri = Uri.Builder().apply {
|
||||
scheme("market")
|
||||
authority("details")
|
||||
appendQueryParameter("id", webViewPackage.packageName)
|
||||
}.build()
|
||||
val referrerUri = Uri.Builder().apply {
|
||||
scheme("android-app")
|
||||
authority(context.packageName)
|
||||
}.build()
|
||||
|
||||
val marketIntent = Intent(Intent.ACTION_VIEW).apply {
|
||||
data = marketUri
|
||||
putExtra(Intent.EXTRA_REFERRER, referrerUri)
|
||||
}
|
||||
startActivity(marketIntent)
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
setNegativeButton(R.string.dialog_button_close_app) { _, _ ->
|
||||
requireActivity().finish()
|
||||
}
|
||||
}.show()
|
||||
return
|
||||
}
|
||||
|
||||
webViewClient = object : WebViewClientCompat() {
|
||||
override fun shouldInterceptRequest(webView: WebView, request: WebResourceRequest): WebResourceResponse? {
|
||||
val url = request.url
|
||||
@@ -186,10 +220,7 @@ class WebViewFragment : Fragment(), NativePlayerHost {
|
||||
}
|
||||
}
|
||||
webChromeClient = WebChromeClient()
|
||||
settings.apply {
|
||||
javaScriptEnabled = true
|
||||
domStorageEnabled = true
|
||||
}
|
||||
settings.applyDefault()
|
||||
addJavascriptInterface(NativeInterface(this@WebViewFragment), "NativeInterface")
|
||||
addJavascriptInterface(NativePlayer(this@WebViewFragment), "NativePlayer")
|
||||
addJavascriptInterface(externalPlayer, "ExternalPlayer")
|
||||
|
||||
@@ -9,6 +9,7 @@ object Constants {
|
||||
const val APP_INFO_VERSION: String = BuildConfig.VERSION_NAME
|
||||
|
||||
// Webapp constants
|
||||
const val MINIMUM_WEB_VIEW_VERSION = 88
|
||||
const val WEB_CONFIG_PATH = "config.json"
|
||||
const val CAST_SDK_PATH = "cast_sender.js"
|
||||
const val SESSION_CAPABILITIES_PATH = "sessions/capabilities/full"
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Taken and adapted from https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt
|
||||
*
|
||||
* Copyright 2015 Javier Tomás
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jellyfin.mobile.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import timber.log.Timber
|
||||
|
||||
fun Context.isWebViewSupported(): Boolean {
|
||||
try {
|
||||
// May throw android.webkit.WebViewFactory$MissingWebViewPackageException if WebView
|
||||
// is not installed
|
||||
CookieManager.getInstance()
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
|
||||
return packageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)
|
||||
}
|
||||
|
||||
fun WebView.isOutdated(): Boolean =
|
||||
getWebViewMajorVersion() < Constants.MINIMUM_WEB_VIEW_VERSION
|
||||
|
||||
private fun WebView.getWebViewMajorVersion(): Int {
|
||||
return """.*Chrome/(\d+)\..*""".toRegex().matchEntire(getDefaultUserAgentString())?.let { match ->
|
||||
match.groupValues.getOrNull(1)?.toInt()
|
||||
} ?: 0
|
||||
}
|
||||
|
||||
// Based on https://stackoverflow.com/a/29218966
|
||||
private fun WebView.getDefaultUserAgentString(): String {
|
||||
val originalUA: String = settings.userAgentString
|
||||
|
||||
// Next call to getUserAgentString() will get us the default
|
||||
settings.userAgentString = null
|
||||
val defaultUserAgentString = settings.userAgentString
|
||||
|
||||
// Revert to original UA string
|
||||
settings.userAgentString = originalUA
|
||||
|
||||
return defaultUserAgentString
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
fun WebSettings.applyDefault() {
|
||||
javaScriptEnabled = true
|
||||
domStorageEnabled = true
|
||||
}
|
||||
@@ -1,4 +1,11 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
<string name="dialog_web_view_not_supported">WebView not supported</string>
|
||||
<string name="dialog_web_view_not_supported_message">WebViews are not supported by this device.\nInstall a compatible WebView implementation or switch to a supported device.</string>
|
||||
<string name="dialog_web_view_outdated">WebView outdated</string>
|
||||
<string name="dialog_web_view_outdated_message">The WebView implementation used by your device is outdated.\nPlease update it to a compatible version.</string>
|
||||
<string name="dialog_button_check_for_updates">Check for updates</string>
|
||||
<string name="dialog_button_close_app">Close app</string>
|
||||
|
||||
<string name="connect_to_server_title">Connect to Server</string>
|
||||
<string name="host_input_hint">Host</string>
|
||||
<string name="connection_error_invalid_format">Host has an invalid format</string>
|
||||
|
||||
@@ -579,7 +579,9 @@ public final class Chromecast implements IChromecast {
|
||||
|
||||
stopRouteScan(callback);
|
||||
sessionStop(callback);
|
||||
media.destroy();
|
||||
if (media != null) {
|
||||
media.destroy();
|
||||
}
|
||||
media = null;
|
||||
if (connection != null) {
|
||||
connection.destroy();
|
||||
|
||||
Reference in New Issue
Block a user