diff --git a/app/src/main/java/org/jellyfin/mobile/app/AppModule.kt b/app/src/main/java/org/jellyfin/mobile/app/AppModule.kt index 8c74282a..4923c2ea 100644 --- a/app/src/main/java/org/jellyfin/mobile/app/AppModule.kt +++ b/app/src/main/java/org/jellyfin/mobile/app/AppModule.kt @@ -19,6 +19,7 @@ import okhttp3.OkHttpClient import org.chromium.net.CronetEngine import org.chromium.net.CronetProvider import org.jellyfin.mobile.MainViewModel +import org.jellyfin.mobile.bridge.NativePlayer import org.jellyfin.mobile.events.ActivityEventHandler import org.jellyfin.mobile.player.audio.car.LibraryBrowser import org.jellyfin.mobile.player.deviceprofile.DeviceProfileBuilder @@ -58,6 +59,9 @@ val applicationModule = module { single { ActivityEventHandler(get()) } single { WebappFunctionChannel() } + // Bridge interfaces + single { NativePlayer(get(), get(), get(named(PLAYER_EVENT_CHANNEL))) } + // ViewModels viewModel { MainViewModel(get(), get()) } diff --git a/app/src/main/java/org/jellyfin/mobile/bridge/NativePlayer.kt b/app/src/main/java/org/jellyfin/mobile/bridge/NativePlayer.kt index 01b0e02c..becf4f93 100644 --- a/app/src/main/java/org/jellyfin/mobile/bridge/NativePlayer.kt +++ b/app/src/main/java/org/jellyfin/mobile/bridge/NativePlayer.kt @@ -3,20 +3,20 @@ package org.jellyfin.mobile.bridge import android.webkit.JavascriptInterface import kotlinx.coroutines.channels.Channel import org.jellyfin.mobile.app.AppPreferences -import org.jellyfin.mobile.app.PLAYER_EVENT_CHANNEL +import org.jellyfin.mobile.events.ActivityEvent +import org.jellyfin.mobile.events.ActivityEventHandler import org.jellyfin.mobile.player.interaction.PlayOptions import org.jellyfin.mobile.player.interaction.PlayerEvent import org.jellyfin.mobile.settings.VideoPlayerType import org.jellyfin.mobile.utils.Constants import org.json.JSONObject -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import org.koin.core.qualifier.named -class NativePlayer(private val host: NativePlayerHost) : KoinComponent { - - private val appPreferences: AppPreferences by inject() - private val playerEventChannel: Channel by inject(named(PLAYER_EVENT_CHANNEL)) +@Suppress("unused") +class NativePlayer( + private val appPreferences: AppPreferences, + private val activityEventHandler: ActivityEventHandler, + private val playerEventChannel: Channel, +) { @JavascriptInterface fun isEnabled() = appPreferences.videoPlayerType == VideoPlayerType.EXO_PLAYER @@ -24,7 +24,7 @@ class NativePlayer(private val host: NativePlayerHost) : KoinComponent { @JavascriptInterface fun loadPlayer(args: String) { PlayOptions.fromJson(JSONObject(args))?.let { options -> - host.loadNativePlayer(options) + activityEventHandler.emit(ActivityEvent.LaunchNativePlayer(options)) } } diff --git a/app/src/main/java/org/jellyfin/mobile/bridge/NativePlayerHost.kt b/app/src/main/java/org/jellyfin/mobile/bridge/NativePlayerHost.kt deleted file mode 100644 index d111627f..00000000 --- a/app/src/main/java/org/jellyfin/mobile/bridge/NativePlayerHost.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.jellyfin.mobile.bridge - -import org.jellyfin.mobile.player.interaction.PlayOptions - -interface NativePlayerHost { - fun loadNativePlayer(playOptions: PlayOptions) -} diff --git a/app/src/main/java/org/jellyfin/mobile/events/ActivityEvent.kt b/app/src/main/java/org/jellyfin/mobile/events/ActivityEvent.kt index d44209e4..c3ec0b01 100644 --- a/app/src/main/java/org/jellyfin/mobile/events/ActivityEvent.kt +++ b/app/src/main/java/org/jellyfin/mobile/events/ActivityEvent.kt @@ -1,10 +1,12 @@ package org.jellyfin.mobile.events import android.net.Uri +import org.jellyfin.mobile.player.interaction.PlayOptions import org.json.JSONArray sealed class ActivityEvent { class ChangeFullscreen(val isFullscreen: Boolean) : ActivityEvent() + class LaunchNativePlayer(val playOptions: PlayOptions) : ActivityEvent() class OpenUrl(val uri: String) : ActivityEvent() class DownloadFile(val uri: Uri, val title: String, val filename: String) : ActivityEvent() class CastMessage(val action: String, val args: JSONArray) : ActivityEvent() diff --git a/app/src/main/java/org/jellyfin/mobile/events/ActivityEventHandler.kt b/app/src/main/java/org/jellyfin/mobile/events/ActivityEventHandler.kt index a82ae209..720af445 100644 --- a/app/src/main/java/org/jellyfin/mobile/events/ActivityEventHandler.kt +++ b/app/src/main/java/org/jellyfin/mobile/events/ActivityEventHandler.kt @@ -4,6 +4,8 @@ import android.content.ActivityNotFoundException import android.content.Intent import android.content.pm.ActivityInfo import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.add import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -13,7 +15,9 @@ import kotlinx.coroutines.launch import org.jellyfin.mobile.MainActivity import org.jellyfin.mobile.R import org.jellyfin.mobile.bridge.JavascriptCallback +import org.jellyfin.mobile.player.ui.PlayerFragment import org.jellyfin.mobile.settings.SettingsFragment +import org.jellyfin.mobile.utils.Constants import org.jellyfin.mobile.utils.extensions.addFragment import org.jellyfin.mobile.utils.extensions.disableFullscreen import org.jellyfin.mobile.utils.extensions.enableFullscreen @@ -54,6 +58,15 @@ class ActivityEventHandler( window.setBackgroundDrawableResource(R.color.theme_background) } } + is ActivityEvent.LaunchNativePlayer -> { + supportFragmentManager.beginTransaction().apply { + val args = Bundle().apply { + putParcelable(Constants.EXTRA_MEDIA_PLAY_OPTIONS, event.playOptions) + } + add(R.id.fragment_container, args = args) + addToBackStack(null) + }.commit() + } is ActivityEvent.OpenUrl -> { try { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(event.uri)) diff --git a/app/src/main/java/org/jellyfin/mobile/webapp/WebViewFragment.kt b/app/src/main/java/org/jellyfin/mobile/webapp/WebViewFragment.kt index 4d97b4c9..86114a45 100644 --- a/app/src/main/java/org/jellyfin/mobile/webapp/WebViewFragment.kt +++ b/app/src/main/java/org/jellyfin/mobile/webapp/WebViewFragment.kt @@ -17,7 +17,6 @@ import androidx.core.view.ViewCompat import androidx.core.view.doOnNextLayout import androidx.core.view.isVisible import androidx.fragment.app.Fragment -import androidx.fragment.app.add import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.webkit.WebViewAssetLoader.AssetsPathHandler @@ -29,11 +28,8 @@ import org.jellyfin.mobile.app.AppPreferences import org.jellyfin.mobile.bridge.ExternalPlayer import org.jellyfin.mobile.bridge.NativeInterface import org.jellyfin.mobile.bridge.NativePlayer -import org.jellyfin.mobile.bridge.NativePlayerHost import org.jellyfin.mobile.data.entity.ServerEntity import org.jellyfin.mobile.databinding.FragmentWebviewBinding -import org.jellyfin.mobile.player.interaction.PlayOptions -import org.jellyfin.mobile.player.ui.PlayerFragment import org.jellyfin.mobile.setup.ConnectFragment import org.jellyfin.mobile.utils.Constants import org.jellyfin.mobile.utils.Constants.FRAGMENT_WEB_VIEW_EXTRA_SERVER @@ -50,12 +46,13 @@ import org.jellyfin.mobile.utils.requestNoBatteryOptimizations import org.jellyfin.mobile.utils.runOnUiThread import org.koin.android.ext.android.inject -class WebViewFragment : Fragment(), NativePlayerHost { +class WebViewFragment : Fragment() { val appPreferences: AppPreferences by inject() private val apiClientController: ApiClientController by inject() private val webappFunctionChannel: WebappFunctionChannel by inject() private lateinit var assetsPathHandler: AssetsPathHandler private lateinit var jellyfinWebViewClient: JellyfinWebViewClient + private val nativePlayer: NativePlayer by inject() private lateinit var externalPlayer: ExternalPlayer lateinit var server: ServerEntity @@ -178,7 +175,7 @@ class WebViewFragment : Fragment(), NativePlayerHost { webChromeClient = LoggingWebChromeClient() settings.applyDefault() addJavascriptInterface(NativeInterface(requireContext()), "NativeInterface") - addJavascriptInterface(NativePlayer(this@WebViewFragment), "NativePlayer") + addJavascriptInterface(nativePlayer, "NativePlayer") addJavascriptInterface(externalPlayer, "ExternalPlayer") loadUrl(server.hostname) @@ -250,14 +247,4 @@ class WebViewFragment : Fragment(), NativePlayerHost { connected = false onSelectServer(error = true) } - - override fun loadNativePlayer(playOptions: PlayOptions) = runOnUiThread { - parentFragmentManager.beginTransaction().apply { - val args = Bundle().apply { - putParcelable(Constants.EXTRA_MEDIA_PLAY_OPTIONS, playOptions) - } - add(R.id.fragment_container, args = args) - addToBackStack(null) - }.commit() - } }