mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-09-11 12:06:14 +00:00
casting: catch exceptions for playback control functions
# Conflicts: # app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt # app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt
This commit is contained in:
parent
7169cd1b82
commit
0fd83cbd74
10 changed files with 104 additions and 79 deletions
|
@ -128,7 +128,7 @@ class ConnectCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
synchronized(ExpStateCasting.instance.devices) {
|
synchronized(ExpStateCasting.instance.devices) {
|
||||||
_devices.addAll(ExpStateCasting.instance.devices.values.map { it.device.name() })
|
_devices.addAll(ExpStateCasting.instance.devices.values.map { it.device.name() })
|
||||||
}
|
}
|
||||||
_rememberedDevices.addAll(StateCasting.instance.getRememberedCastingDeviceNames())
|
_rememberedDevices.addAll(ExpStateCasting.instance.getRememberedCastingDeviceNames())
|
||||||
} else {
|
} else {
|
||||||
synchronized(StateCasting.instance.devices) {
|
synchronized(StateCasting.instance.devices) {
|
||||||
_devices.addAll(StateCasting.instance.devices.values.mapNotNull { it.name })
|
_devices.addAll(StateCasting.instance.devices.values.mapNotNull { it.name })
|
||||||
|
@ -203,11 +203,18 @@ class ConnectCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
override fun dismiss() {
|
override fun dismiss() {
|
||||||
super.dismiss()
|
super.dismiss()
|
||||||
(_imageLoader.drawable as Animatable?)?.stop()
|
(_imageLoader.drawable as Animatable?)?.stop()
|
||||||
|
if (Settings.instance.casting.experimentalCasting) {
|
||||||
|
ExpStateCasting.instance.onDeviceAdded.remove(this)
|
||||||
|
ExpStateCasting.instance.onDeviceChanged.remove(this)
|
||||||
|
ExpStateCasting.instance.onDeviceRemoved.remove(this)
|
||||||
|
ExpStateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this)
|
||||||
|
} else {
|
||||||
StateCasting.instance.onDeviceAdded.remove(this)
|
StateCasting.instance.onDeviceAdded.remove(this)
|
||||||
StateCasting.instance.onDeviceChanged.remove(this)
|
StateCasting.instance.onDeviceChanged.remove(this)
|
||||||
StateCasting.instance.onDeviceRemoved.remove(this)
|
StateCasting.instance.onDeviceRemoved.remove(this)
|
||||||
StateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this)
|
StateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateUnifiedList() {
|
private fun updateUnifiedList() {
|
||||||
val oldList = ArrayList(_unifiedDevices)
|
val oldList = ArrayList(_unifiedDevices)
|
||||||
|
|
|
@ -24,6 +24,7 @@ import com.futo.platformplayer.experimental_casting.ExpStateCasting
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailFragment
|
import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailFragment
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.states.StateApp
|
import com.futo.platformplayer.states.StateApp
|
||||||
|
import com.futo.platformplayer.views.adapters.GenericCastingDevice
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import com.google.android.material.slider.Slider.OnChangeListener
|
import com.google.android.material.slider.Slider.OnChangeListener
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -49,7 +50,7 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
private lateinit var _buttonStop: ImageButton;
|
private lateinit var _buttonStop: ImageButton;
|
||||||
private lateinit var _buttonNext: ImageButton;
|
private lateinit var _buttonNext: ImageButton;
|
||||||
|
|
||||||
private var _device: CastingDevice? = null;
|
private var _device: GenericCastingDevice? = null;
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -178,7 +179,7 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpStateCasting.instance.onActiveDeviceTimeChanged.remove(this)
|
ExpStateCasting.instance.onActiveDeviceTimeChanged.remove(this)
|
||||||
StateCasting.instance.onActiveDeviceTimeChanged.subscribe {
|
ExpStateCasting.instance.onActiveDeviceTimeChanged.subscribe {
|
||||||
_sliderPosition.value = it.toFloat().coerceAtLeast(0.0f).coerceAtMost(_sliderPosition.valueTo)
|
_sliderPosition.value = it.toFloat().coerceAtLeast(0.0f).coerceAtMost(_sliderPosition.valueTo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,16 +190,18 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
_sliderPosition.valueTo = dur
|
_sliderPosition.valueTo = dur
|
||||||
}
|
}
|
||||||
|
|
||||||
_device = StateCasting.instance.activeDevice
|
val ad = ExpStateCasting.instance.activeDevice
|
||||||
val d = _device
|
if (ad != null) {
|
||||||
val isConnected = d != null && d.connectionState == CastConnectionState.CONNECTED
|
_device = GenericCastingDevice.Experimental(ad)
|
||||||
|
}
|
||||||
|
val isConnected = ad != null && ad.connectionState == com.futo.platformplayer.experimental_casting.CastConnectionState.CONNECTED
|
||||||
setLoading(!isConnected)
|
setLoading(!isConnected)
|
||||||
ExpStateCasting.instance.onActiveDeviceConnectionStateChanged.subscribe(this) { _, connectionState ->
|
ExpStateCasting.instance.onActiveDeviceConnectionStateChanged.subscribe(this) { _, connectionState ->
|
||||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
|
||||||
setLoading(connectionState != com.futo.platformplayer.experimental_casting.CastConnectionState.CONNECTED)
|
setLoading(connectionState != com.futo.platformplayer.experimental_casting.CastConnectionState.CONNECTED)
|
||||||
}
|
}
|
||||||
updateDevice();
|
updateDevice()
|
||||||
};
|
}
|
||||||
} else {
|
} else {
|
||||||
StateCasting.instance.onActiveDeviceVolumeChanged.remove(this);
|
StateCasting.instance.onActiveDeviceVolumeChanged.remove(this);
|
||||||
StateCasting.instance.onActiveDeviceVolumeChanged.subscribe {
|
StateCasting.instance.onActiveDeviceVolumeChanged.subscribe {
|
||||||
|
@ -217,14 +220,16 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
_sliderPosition.valueTo = dur
|
_sliderPosition.valueTo = dur
|
||||||
};
|
};
|
||||||
|
|
||||||
_device = StateCasting.instance.activeDevice;
|
val ad = StateCasting.instance.activeDevice
|
||||||
val d = _device;
|
if (ad != null) {
|
||||||
val isConnected = d != null && d.connectionState == CastConnectionState.CONNECTED;
|
_device = GenericCastingDevice.Normal(ad)
|
||||||
|
}
|
||||||
|
val isConnected = ad != null && ad.connectionState == CastConnectionState.CONNECTED;
|
||||||
setLoading(!isConnected);
|
setLoading(!isConnected);
|
||||||
StateCasting.instance.onActiveDeviceConnectionStateChanged.subscribe(this) { _, connectionState ->
|
StateCasting.instance.onActiveDeviceConnectionStateChanged.subscribe(this) { _, connectionState ->
|
||||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) { setLoading(connectionState != CastConnectionState.CONNECTED); };
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) { setLoading(connectionState != CastConnectionState.CONNECTED); };
|
||||||
updateDevice();
|
updateDevice()
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDevice();
|
updateDevice();
|
||||||
|
@ -235,12 +240,12 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
StateCasting.instance.onActiveDeviceVolumeChanged.remove(this);
|
StateCasting.instance.onActiveDeviceVolumeChanged.remove(this);
|
||||||
StateCasting.instance.onActiveDeviceDurationChanged.remove(this);
|
StateCasting.instance.onActiveDeviceDurationChanged.remove(this);
|
||||||
StateCasting.instance.onActiveDeviceTimeChanged.remove(this);
|
StateCasting.instance.onActiveDeviceTimeChanged.remove(this);
|
||||||
_device = null;
|
|
||||||
StateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
StateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
||||||
ExpStateCasting.instance.onActiveDeviceVolumeChanged.remove(this);
|
ExpStateCasting.instance.onActiveDeviceVolumeChanged.remove(this);
|
||||||
ExpStateCasting.instance.onActiveDeviceDurationChanged.remove(this);
|
ExpStateCasting.instance.onActiveDeviceDurationChanged.remove(this);
|
||||||
ExpStateCasting.instance.onActiveDeviceTimeChanged.remove(this);
|
ExpStateCasting.instance.onActiveDeviceTimeChanged.remove(this);
|
||||||
ExpStateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
ExpStateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
||||||
|
_device = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateDevice() {
|
private fun updateDevice() {
|
||||||
|
|
|
@ -110,7 +110,7 @@ class CastingDeviceHandle {
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
device.loadVideo(contentType, contentId, resumePosition, speed)
|
device.loadVideo(contentType, contentId, resumePosition, speed)
|
||||||
} catch (e: Exception) {
|
} catch (e: Throwable) {
|
||||||
Logger.e("CastingDevice", "Failed to load video: $e")
|
Logger.e("CastingDevice", "Failed to load video: $e")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,11 @@ class CastingDeviceHandle {
|
||||||
duration: Double,
|
duration: Double,
|
||||||
speed: Double?
|
speed: Double?
|
||||||
) {
|
) {
|
||||||
|
try {
|
||||||
device.loadContent(contentType, content, resumePosition, duration, speed)
|
device.loadContent(contentType, content, resumePosition, duration, speed)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Logger.e("CastingDevice", "Failed to load content: $e")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,11 @@ class ExpStateCasting {
|
||||||
_resumeCastingDevice = ad.device.getDeviceInfo()
|
_resumeCastingDevice = ad.device.getDeviceInfo()
|
||||||
Log.i(TAG, "_resumeCastingDevice set to '${ad.device.name()}'")
|
Log.i(TAG, "_resumeCastingDevice set to '${ad.device.name()}'")
|
||||||
Logger.i(TAG, "Stopping active device because of onStop.")
|
Logger.i(TAG, "Stopping active device because of onStop.")
|
||||||
|
try {
|
||||||
ad.device.disconnect()
|
ad.device.disconnect()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Logger.e(TAG, "Failed to disconnect from device: $e")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onResume() {
|
fun onResume() {
|
||||||
|
@ -248,7 +252,11 @@ class ExpStateCasting {
|
||||||
device.eventHandler.onTimeChanged.clear();
|
device.eventHandler.onTimeChanged.clear();
|
||||||
device.eventHandler.onVolumeChanged.clear();
|
device.eventHandler.onVolumeChanged.clear();
|
||||||
device.eventHandler.onDurationChanged.clear();
|
device.eventHandler.onDurationChanged.clear();
|
||||||
|
try {
|
||||||
ad.device.disconnect();
|
ad.device.disconnect();
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Logger.e(TAG, "Failed to disconnect from device: $e")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
device.eventHandler.onConnectionStateChanged.subscribe { castConnectionState ->
|
device.eventHandler.onConnectionStateChanged.subscribe { castConnectionState ->
|
||||||
|
@ -441,6 +449,10 @@ class ExpStateCasting {
|
||||||
return Settings.instance.casting.alwaysProxyRequests || deviceHandle.device.castingProtocol() != ProtocolType.F_CAST || hasRequestModifier
|
return Settings.instance.casting.alwaysProxyRequests || deviceHandle.device.castingProtocol() != ProtocolType.F_CAST || hasRequestModifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
_castId.incrementAndGet()
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun castIfAvailable(
|
suspend fun castIfAvailable(
|
||||||
contentResolver: ContentResolver,
|
contentResolver: ContentResolver,
|
||||||
video: IPlatformVideoDetails,
|
video: IPlatformVideoDetails,
|
||||||
|
@ -541,7 +553,7 @@ class ExpStateCasting {
|
||||||
resumePosition,
|
resumePosition,
|
||||||
video.duration.toDouble(),
|
video.duration.toDouble(),
|
||||||
speed
|
speed
|
||||||
);
|
)
|
||||||
} else if (audioSource is IAudioUrlSource) {
|
} else if (audioSource is IAudioUrlSource) {
|
||||||
val audioPath = "/audio-${id}"
|
val audioPath = "/audio-${id}"
|
||||||
val audioUrl = if (proxyStreams) url + audioPath else audioSource.getAudioUrl();
|
val audioUrl = if (proxyStreams) url + audioPath else audioSource.getAudioUrl();
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
||||||
import com.futo.platformplayer.casting.StateCasting
|
import com.futo.platformplayer.casting.StateCasting
|
||||||
import com.futo.platformplayer.constructs.Event0
|
import com.futo.platformplayer.constructs.Event0
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
import com.futo.platformplayer.experimental_casting.ExpStateCasting
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.models.PlatformVideoWithTime
|
import com.futo.platformplayer.models.PlatformVideoWithTime
|
||||||
import com.futo.platformplayer.models.UrlVideoWithTime
|
import com.futo.platformplayer.models.UrlVideoWithTime
|
||||||
|
@ -437,7 +438,7 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
|
|
||||||
fun onUserLeaveHint() {
|
fun onUserLeaveHint() {
|
||||||
val viewDetail = _viewDetail;
|
val viewDetail = _viewDetail;
|
||||||
Logger.i(TAG, "onUserLeaveHint preventPictureInPicture=${viewDetail?.preventPictureInPicture} isCasting=${StateCasting.instance.isCasting} isBackgroundPictureInPicture=${Settings.instance.playback.isBackgroundPictureInPicture()} allowBackground=${viewDetail?.isAudioOnlyUserAction}");
|
Logger.i(TAG, "onUserLeaveHint preventPictureInPicture=${viewDetail?.preventPictureInPicture} isCasting=${StateCasting.instance.isCasting} isBackgroundPictureInPicture=${Settings.instance.playback.isBackgroundPictureInPicture()} allowBackground=${viewDetail?.allowBackground}");
|
||||||
|
|
||||||
if (viewDetail === null) {
|
if (viewDetail === null) {
|
||||||
return
|
return
|
||||||
|
@ -446,7 +447,7 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
if (viewDetail.shouldEnterPictureInPicture) {
|
if (viewDetail.shouldEnterPictureInPicture) {
|
||||||
_leavingPiP = false
|
_leavingPiP = false
|
||||||
}
|
}
|
||||||
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.S && viewDetail.preventPictureInPicture == false && !StateCasting.instance.isCasting && Settings.instance.playback.isBackgroundPictureInPicture() && !viewDetail.isAudioOnlyUserAction) {
|
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.S && viewDetail.preventPictureInPicture == false && !StateCasting.instance.isCasting && Settings.instance.playback.isBackgroundPictureInPicture() && !viewDetail.allowBackground) {
|
||||||
val params = _viewDetail?.getPictureInPictureParams();
|
val params = _viewDetail?.getPictureInPictureParams();
|
||||||
if(params != null) {
|
if(params != null) {
|
||||||
Logger.i(TAG, "enterPictureInPictureMode")
|
Logger.i(TAG, "enterPictureInPictureMode")
|
||||||
|
@ -526,7 +527,7 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
|
|
||||||
private fun stopIfRequired() {
|
private fun stopIfRequired() {
|
||||||
var shouldStop = true;
|
var shouldStop = true;
|
||||||
if (_viewDetail?.isAudioOnlyUserAction == true) {
|
if (_viewDetail?.allowBackground == true) {
|
||||||
shouldStop = false;
|
shouldStop = false;
|
||||||
} else if (Settings.instance.playback.isBackgroundPictureInPicture() && !_leavingPiP) {
|
} else if (Settings.instance.playback.isBackgroundPictureInPicture() && !_leavingPiP) {
|
||||||
shouldStop = false;
|
shouldStop = false;
|
||||||
|
|
|
@ -325,7 +325,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
val onEnterPictureInPicture = Event0();
|
val onEnterPictureInPicture = Event0();
|
||||||
val onVideoChanged = Event2<Int, Int>()
|
val onVideoChanged = Event2<Int, Int>()
|
||||||
|
|
||||||
var isAudioOnlyUserAction: Boolean = false
|
var allowBackground: Boolean = false
|
||||||
private set(value) {
|
private set(value) {
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
field = value
|
field = value
|
||||||
|
@ -337,7 +337,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
get() = !preventPictureInPicture &&
|
get() = !preventPictureInPicture &&
|
||||||
!StateCasting.instance.isCasting &&
|
!StateCasting.instance.isCasting &&
|
||||||
Settings.instance.playback.isBackgroundPictureInPicture() &&
|
Settings.instance.playback.isBackgroundPictureInPicture() &&
|
||||||
!isAudioOnlyUserAction &&
|
!allowBackground &&
|
||||||
isPlaying
|
isPlaying
|
||||||
|
|
||||||
val onShouldEnterPictureInPictureChanged = Event0();
|
val onShouldEnterPictureInPictureChanged = Event0();
|
||||||
|
@ -808,7 +808,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
MediaControlReceiver.onBackgroundReceived.subscribe(this) {
|
MediaControlReceiver.onBackgroundReceived.subscribe(this) {
|
||||||
Logger.i(TAG, "MediaControlReceiver.onBackgroundReceived")
|
Logger.i(TAG, "MediaControlReceiver.onBackgroundReceived")
|
||||||
_player.switchToAudioMode(video);
|
_player.switchToAudioMode(video);
|
||||||
isAudioOnlyUserAction = true;
|
allowBackground = true;
|
||||||
StateApp.instance.contextOrNull?.let {
|
StateApp.instance.contextOrNull?.let {
|
||||||
try {
|
try {
|
||||||
if (it is MainActivity) {
|
if (it is MainActivity) {
|
||||||
|
@ -1052,26 +1052,15 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_slideUpOverlay?.hide();
|
_slideUpOverlay?.hide();
|
||||||
} else if(video is JSVideoDetails && (video as JSVideoDetails).hasVODEvents())
|
|
||||||
RoundButton(context, R.drawable.ic_chat, context.getString(R.string.vod_chat), TAG_VODCHAT) {
|
|
||||||
video?.let {
|
|
||||||
try {
|
|
||||||
loadVODChat(it);
|
|
||||||
}
|
|
||||||
catch(ex: Throwable) {
|
|
||||||
Logger.e(TAG, "Failed to reopen vod chat", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_slideUpOverlay?.hide();
|
|
||||||
} else null,
|
} else null,
|
||||||
if (!isLimitedVersion) RoundButton(context, R.drawable.ic_screen_share, if (isAudioOnlyUserAction) context.getString(R.string.background_revert) else context.getString(R.string.background), TAG_BACKGROUND) {
|
if (!isLimitedVersion) RoundButton(context, R.drawable.ic_screen_share, if (allowBackground) context.getString(R.string.background_revert) else context.getString(R.string.background), TAG_BACKGROUND) {
|
||||||
if (!isAudioOnlyUserAction) {
|
if (!allowBackground) {
|
||||||
_player.switchToAudioMode(video);
|
_player.switchToAudioMode(video);
|
||||||
isAudioOnlyUserAction = true;
|
allowBackground = true;
|
||||||
it.text.text = resources.getString(R.string.background_revert);
|
it.text.text = resources.getString(R.string.background_revert);
|
||||||
} else {
|
} else {
|
||||||
_player.switchToVideoMode();
|
_player.switchToVideoMode();
|
||||||
isAudioOnlyUserAction = false;
|
allowBackground = false;
|
||||||
it.text.text = resources.getString(R.string.background);
|
it.text.text = resources.getString(R.string.background);
|
||||||
}
|
}
|
||||||
_slideUpOverlay?.hide();
|
_slideUpOverlay?.hide();
|
||||||
|
@ -1187,23 +1176,19 @@ class VideoDetailView : ConstraintLayout {
|
||||||
|
|
||||||
|
|
||||||
//Lifecycle
|
//Lifecycle
|
||||||
var isLoginStop = false; //TODO: This is a bit jank, but easiest solution for now without reworking flow. (Alternatively, fix MainActivity getting stopped/disposing video)
|
|
||||||
fun onResume() {
|
fun onResume() {
|
||||||
Logger.v(TAG, "onResume");
|
Logger.v(TAG, "onResume");
|
||||||
_onPauseCalled = false;
|
_onPauseCalled = false;
|
||||||
|
|
||||||
val wasLoginCall = isLoginStop;
|
|
||||||
isLoginStop = false;
|
|
||||||
|
|
||||||
Logger.i(TAG, "_video: ${video?.name ?: "no video"}");
|
Logger.i(TAG, "_video: ${video?.name ?: "no video"}");
|
||||||
Logger.i(TAG, "_didStop: $_didStop");
|
Logger.i(TAG, "_didStop: $_didStop");
|
||||||
|
|
||||||
//Recover cancelled loads
|
//Recover cancelled loads
|
||||||
if(video == null) {
|
if(video == null) {
|
||||||
val t = (lastPositionMilliseconds / 1000.0f).roundToLong();
|
val t = (lastPositionMilliseconds / 1000.0f).roundToLong();
|
||||||
if(_searchVideo != null && !wasLoginCall)
|
if(_searchVideo != null)
|
||||||
setVideoOverview(_searchVideo!!, true, t);
|
setVideoOverview(_searchVideo!!, true, t);
|
||||||
else if(_url != null && !wasLoginCall)
|
else if(_url != null)
|
||||||
setVideo(_url!!, t, _playWhenReady);
|
setVideo(_url!!, t, _playWhenReady);
|
||||||
}
|
}
|
||||||
else if(_didStop) {
|
else if(_didStop) {
|
||||||
|
@ -1215,14 +1200,11 @@ class VideoDetailView : ConstraintLayout {
|
||||||
|
|
||||||
if(_player.isAudioMode) {
|
if(_player.isAudioMode) {
|
||||||
//Requested behavior to leave it in audio mode. leaving it commented if it causes issues, revert?
|
//Requested behavior to leave it in audio mode. leaving it commented if it causes issues, revert?
|
||||||
if(!isAudioOnlyUserAction) {
|
if(!allowBackground) {
|
||||||
_player.switchToVideoMode();
|
_player.switchToVideoMode();
|
||||||
isAudioOnlyUserAction = false;
|
allowBackground = false;
|
||||||
_buttonPins.getButtonByTag(TAG_BACKGROUND)?.text?.text = resources.getString(R.string.background);
|
_buttonPins.getButtonByTag(TAG_BACKGROUND)?.text?.text = resources.getString(R.string.background);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
_buttonPins.getButtonByTag(TAG_BACKGROUND)?.text?.text = resources.getString(R.string.video);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(!_player.isFitMode && !_player.isFullScreen && !fragment.isInPictureInPicture)
|
if(!_player.isFitMode && !_player.isFullScreen && !fragment.isInPictureInPicture)
|
||||||
_player.fitHeight();
|
_player.fitHeight();
|
||||||
|
@ -1243,7 +1225,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isAudioOnlyUserAction)
|
if(allowBackground)
|
||||||
StatePlayer.instance.startOrUpdateMediaSession(context, video);
|
StatePlayer.instance.startOrUpdateMediaSession(context, video);
|
||||||
else {
|
else {
|
||||||
when (Settings.instance.playback.backgroundPlay) {
|
when (Settings.instance.playback.backgroundPlay) {
|
||||||
|
@ -1251,6 +1233,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
1 -> {
|
1 -> {
|
||||||
if(!(video?.isLive ?: false)) {
|
if(!(video?.isLive ?: false)) {
|
||||||
_player.switchToAudioMode(video);
|
_player.switchToAudioMode(video);
|
||||||
|
allowBackground = true;
|
||||||
}
|
}
|
||||||
StatePlayer.instance.startOrUpdateMediaSession(context, video);
|
StatePlayer.instance.startOrUpdateMediaSession(context, video);
|
||||||
}
|
}
|
||||||
|
@ -1273,7 +1256,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_taskLoadVideo.cancel();
|
_taskLoadVideo.cancel();
|
||||||
handleStop();
|
handleStop();
|
||||||
_didStop = true;
|
_didStop = true;
|
||||||
onShouldEnterPictureInPictureChanged.emit()
|
|
||||||
Logger.i(TAG, "_didStop set to true");
|
Logger.i(TAG, "_didStop set to true");
|
||||||
|
|
||||||
StatePlayer.instance.rotationLock = false;
|
StatePlayer.instance.rotationLock = false;
|
||||||
|
@ -2048,10 +2030,10 @@ class VideoDetailView : ConstraintLayout {
|
||||||
|
|
||||||
if (isLimitedVersion && _player.isAudioMode) {
|
if (isLimitedVersion && _player.isAudioMode) {
|
||||||
_player.switchToVideoMode()
|
_player.switchToVideoMode()
|
||||||
isAudioOnlyUserAction = false;
|
allowBackground = false;
|
||||||
} else {
|
} else {
|
||||||
val thumbnail = video.thumbnails.getHQThumbnail();
|
val thumbnail = video.thumbnails.getHQThumbnail();
|
||||||
if ((videoSource == null) && !thumbnail.isNullOrBlank()) // || _player.isAudioMode
|
if ((videoSource == null || _player.isAudioMode) && !thumbnail.isNullOrBlank())
|
||||||
Glide.with(context).asBitmap().load(thumbnail)
|
Glide.with(context).asBitmap().load(thumbnail)
|
||||||
.into(object: CustomTarget<Bitmap>() {
|
.into(object: CustomTarget<Bitmap>() {
|
||||||
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
|
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
|
||||||
|
@ -2626,7 +2608,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_player.play();
|
_player.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onShouldEnterPictureInPictureChanged.emit()
|
|
||||||
|
|
||||||
//TODO: This was needed because handleLowerVolume was done.
|
//TODO: This was needed because handleLowerVolume was done.
|
||||||
//_player.setVolume(1.0f);
|
//_player.setVolume(1.0f);
|
||||||
|
@ -2649,7 +2630,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_player.pause()
|
_player.pause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onShouldEnterPictureInPictureChanged.emit()
|
|
||||||
}
|
}
|
||||||
private fun handleSeek(ms: Long) {
|
private fun handleSeek(ms: Long) {
|
||||||
Logger.i(TAG, "handleSeek(ms=$ms)")
|
Logger.i(TAG, "handleSeek(ms=$ms)")
|
||||||
|
@ -3483,14 +3463,9 @@ class VideoDetailView : ConstraintLayout {
|
||||||
val id = e.config.let { if(it is SourcePluginConfig) it.id else null };
|
val id = e.config.let { if(it is SourcePluginConfig) it.id else null };
|
||||||
val didLogin = if(id == null)
|
val didLogin = if(id == null)
|
||||||
false
|
false
|
||||||
else {
|
else StatePlugins.instance.loginPlugin(context, id) {
|
||||||
isLoginStop = true;
|
|
||||||
StatePlugins.instance.loginPlugin(context, id) {
|
|
||||||
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
|
||||||
fetchVideo();
|
fetchVideo();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!didLogin)
|
if(!didLogin)
|
||||||
UIDialogs.showDialogOk(context, R.drawable.ic_error_pred, "Failed to login");
|
UIDialogs.showDialogOk(context, R.drawable.ic_error_pred, "Failed to login");
|
||||||
}, UIDialogs.ActionStyle.PRIMARY));
|
}, UIDialogs.ActionStyle.PRIMARY));
|
||||||
|
@ -3667,7 +3642,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
const val TAG_SHARE = "share";
|
const val TAG_SHARE = "share";
|
||||||
const val TAG_OVERLAY = "overlay";
|
const val TAG_OVERLAY = "overlay";
|
||||||
const val TAG_LIVECHAT = "livechat";
|
const val TAG_LIVECHAT = "livechat";
|
||||||
const val TAG_VODCHAT = "vodchat";
|
|
||||||
const val TAG_CHAPTERS = "chapters";
|
const val TAG_CHAPTERS = "chapters";
|
||||||
const val TAG_OPEN = "open";
|
const val TAG_OPEN = "open";
|
||||||
const val TAG_SEND_TO_DEVICE = "send_to_device";
|
const val TAG_SEND_TO_DEVICE = "send_to_device";
|
||||||
|
|
|
@ -37,6 +37,7 @@ import com.futo.platformplayer.casting.StateCasting
|
||||||
import com.futo.platformplayer.constructs.Event0
|
import com.futo.platformplayer.constructs.Event0
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
||||||
|
import com.futo.platformplayer.experimental_casting.ExpStateCasting
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.HomeFragment
|
import com.futo.platformplayer.fragment.mainactivity.main.HomeFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.SourceDetailFragment
|
import com.futo.platformplayer.fragment.mainactivity.main.SourceDetailFragment
|
||||||
import com.futo.platformplayer.logging.AndroidLogConsumer
|
import com.futo.platformplayer.logging.AndroidLogConsumer
|
||||||
|
@ -759,7 +760,11 @@ class StateApp {
|
||||||
_connectivityManager?.unregisterNetworkCallback(_connectivityEvents);
|
_connectivityManager?.unregisterNetworkCallback(_connectivityEvents);
|
||||||
|
|
||||||
StatePlayer.instance.closeMediaSession();
|
StatePlayer.instance.closeMediaSession();
|
||||||
StateCasting.instance.stop();
|
if (Settings.instance.casting.experimentalCasting) {
|
||||||
|
ExpStateCasting.instance.stop()
|
||||||
|
} else {
|
||||||
|
StateCasting.instance.stop()
|
||||||
|
}
|
||||||
StateSync.instance.stop();
|
StateSync.instance.stop();
|
||||||
StatePlayer.dispose();
|
StatePlayer.dispose();
|
||||||
Companion.dispose();
|
Companion.dispose();
|
||||||
|
|
|
@ -73,7 +73,11 @@ class DeviceViewHolder : ViewHolder {
|
||||||
is GenericCastingDevice.Experimental -> {
|
is GenericCastingDevice.Experimental -> {
|
||||||
if (dev.handle.device.isReady()) {
|
if (dev.handle.device.isReady()) {
|
||||||
// NOTE: we assume experimental casting is used
|
// NOTE: we assume experimental casting is used
|
||||||
|
try {
|
||||||
ExpStateCasting.instance.activeDevice?.device?.stopCasting()
|
ExpStateCasting.instance.activeDevice?.device?.stopCasting()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
//Ignored
|
||||||
|
}
|
||||||
ExpStateCasting.instance.connectDevice(dev.handle)
|
ExpStateCasting.instance.connectDevice(dev.handle)
|
||||||
onConnect.emit(dev)
|
onConnect.emit(dev)
|
||||||
} else {
|
} else {
|
||||||
|
@ -125,7 +129,7 @@ class DeviceViewHolder : ViewHolder {
|
||||||
_textNotReady.visibility = View.GONE;
|
_textNotReady.visibility = View.GONE;
|
||||||
|
|
||||||
val dev = StateCasting.instance.activeDevice;
|
val dev = StateCasting.instance.activeDevice;
|
||||||
if (dev == d) {
|
if (dev == d.device) {
|
||||||
if (dev.connectionState == CastConnectionState.CONNECTED) {
|
if (dev.connectionState == CastConnectionState.CONNECTED) {
|
||||||
_imageLoader.visibility = View.GONE;
|
_imageLoader.visibility = View.GONE;
|
||||||
_textNotReady.visibility = View.GONE;
|
_textNotReady.visibility = View.GONE;
|
||||||
|
@ -180,9 +184,9 @@ class DeviceViewHolder : ViewHolder {
|
||||||
} else {
|
} else {
|
||||||
_textNotReady.visibility = View.GONE;
|
_textNotReady.visibility = View.GONE;
|
||||||
|
|
||||||
val dev = StateCasting.instance.activeDevice;
|
val dev = ExpStateCasting.instance.activeDevice;
|
||||||
if (dev == d) {
|
if (dev == d.handle) {
|
||||||
if (dev.connectionState == CastConnectionState.CONNECTED) {
|
if (dev.connectionState == com.futo.platformplayer.experimental_casting.CastConnectionState.CONNECTED) {
|
||||||
_imageLoader.visibility = View.GONE;
|
_imageLoader.visibility = View.GONE;
|
||||||
_textNotReady.visibility = View.GONE;
|
_textNotReady.visibility = View.GONE;
|
||||||
_imagePin.visibility = View.VISIBLE;
|
_imagePin.visibility = View.VISIBLE;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import androidx.core.content.ContextCompat
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.Settings
|
import com.futo.platformplayer.Settings
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
|
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
|
||||||
import com.futo.platformplayer.casting.CastConnectionState
|
import com.futo.platformplayer.casting.CastConnectionState
|
||||||
import com.futo.platformplayer.casting.StateCasting
|
import com.futo.platformplayer.casting.StateCasting
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
@ -81,7 +82,10 @@ class CastButton : androidx.appcompat.widget.AppCompatImageButton {
|
||||||
|
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
setOnClickListener(null);
|
setOnClickListener(null);
|
||||||
StateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
if (Settings.instance.casting.experimentalCasting) {
|
||||||
ExpStateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
ExpStateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
||||||
|
} else {
|
||||||
|
StateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -29,6 +29,7 @@ import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.constructs.Event2
|
import com.futo.platformplayer.constructs.Event2
|
||||||
import com.futo.platformplayer.experimental_casting.ExpStateCasting
|
import com.futo.platformplayer.experimental_casting.ExpStateCasting
|
||||||
import com.futo.platformplayer.formatDuration
|
import com.futo.platformplayer.formatDuration
|
||||||
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.states.StateHistory
|
import com.futo.platformplayer.states.StateHistory
|
||||||
import com.futo.platformplayer.states.StatePlayer
|
import com.futo.platformplayer.states.StatePlayer
|
||||||
import com.futo.platformplayer.views.TargetTapLoaderView
|
import com.futo.platformplayer.views.TargetTapLoaderView
|
||||||
|
@ -103,9 +104,17 @@ class CastView : ConstraintLayout {
|
||||||
_speedHoldWasPlaying = d.isPlaying
|
_speedHoldWasPlaying = d.isPlaying
|
||||||
_speedHoldPrevRate = d.speed
|
_speedHoldPrevRate = d.speed
|
||||||
if (d.device.supportsFeature(DeviceFeature.SET_SPEED)) {
|
if (d.device.supportsFeature(DeviceFeature.SET_SPEED)) {
|
||||||
|
try {
|
||||||
d.device.changeSpeed(Settings.instance.playback.getHoldPlaybackSpeed())
|
d.device.changeSpeed(Settings.instance.playback.getHoldPlaybackSpeed())
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
// Ignored
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
d.device.resumePlayback()
|
d.device.resumePlayback()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val d = StateCasting.instance.activeDevice ?: return@subscribe;
|
val d = StateCasting.instance.activeDevice ?: return@subscribe;
|
||||||
_speedHoldWasPlaying = d.isPlaying
|
_speedHoldWasPlaying = d.isPlaying
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue