mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-04-20 03:24:50 +00:00
Added casting controls to connected dialog.
This commit is contained in:
parent
0432f06eb3
commit
2ac8e0e621
10 changed files with 306 additions and 58 deletions
|
@ -11,12 +11,27 @@ import android.view.Gravity
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.*
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.futo.platformplayer.activities.MainActivity
|
||||
import com.futo.platformplayer.api.media.models.comments.IPlatformComment
|
||||
import com.futo.platformplayer.casting.StateCasting
|
||||
import com.futo.platformplayer.dialogs.*
|
||||
import com.futo.platformplayer.dialogs.AutoUpdateDialog
|
||||
import com.futo.platformplayer.dialogs.AutomaticBackupDialog
|
||||
import com.futo.platformplayer.dialogs.AutomaticRestoreDialog
|
||||
import com.futo.platformplayer.dialogs.CastingAddDialog
|
||||
import com.futo.platformplayer.dialogs.CastingHelpDialog
|
||||
import com.futo.platformplayer.dialogs.ChangelogDialog
|
||||
import com.futo.platformplayer.dialogs.CommentDialog
|
||||
import com.futo.platformplayer.dialogs.ConnectCastingDialog
|
||||
import com.futo.platformplayer.dialogs.ConnectedCastingDialog
|
||||
import com.futo.platformplayer.dialogs.ImportDialog
|
||||
import com.futo.platformplayer.dialogs.ImportOptionsDialog
|
||||
import com.futo.platformplayer.dialogs.MigrateDialog
|
||||
import com.futo.platformplayer.dialogs.ProgressDialog
|
||||
import com.futo.platformplayer.engine.exceptions.PluginException
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
|
@ -341,11 +356,17 @@ class UIDialogs {
|
|||
val d = StateCasting.instance.activeDevice;
|
||||
if (d != null) {
|
||||
val dialog = ConnectedCastingDialog(context);
|
||||
if (context is Activity) {
|
||||
dialog.setOwnerActivity(context)
|
||||
}
|
||||
registerDialogOpened(dialog);
|
||||
dialog.setOnDismissListener { registerDialogClosed(dialog) };
|
||||
dialog.show();
|
||||
} else {
|
||||
val dialog = ConnectCastingDialog(context);
|
||||
if (context is Activity) {
|
||||
dialog.setOwnerActivity(context)
|
||||
}
|
||||
registerDialogOpened(dialog);
|
||||
val c = context
|
||||
if (c is Activity) {
|
||||
|
|
|
@ -56,7 +56,8 @@ class AirPlayCastingDevice : CastingDevice {
|
|||
|
||||
Logger.i(FCastCastingDevice.TAG, "Start streaming (streamType: $streamType, contentType: $contentType, contentId: $contentId, resumePosition: $resumePosition, duration: $duration, speed: $speed)");
|
||||
|
||||
time = resumePosition;
|
||||
setTime(resumePosition);
|
||||
setDuration(duration);
|
||||
if (resumePosition > 0.0) {
|
||||
val pos = resumePosition / duration;
|
||||
Logger.i(TAG, "resumePosition: $resumePosition, duration: ${duration}, pos: $pos")
|
||||
|
@ -170,8 +171,16 @@ class AirPlayCastingDevice : CastingDevice {
|
|||
}
|
||||
|
||||
val progress = progressInfo.substring(progressIndex + "position: ".length).toDoubleOrNull() ?: continue;
|
||||
setTime(progress);
|
||||
|
||||
time = progress;
|
||||
|
||||
val durationIndex = progressInfo.lowercase().indexOf("duration: ");
|
||||
if (durationIndex == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
val duration = progressInfo.substring(durationIndex + "duration: ".length).toDoubleOrNull() ?: continue;
|
||||
setDuration(duration);
|
||||
} catch (e: Throwable) {
|
||||
Logger.w(TAG, "Failed to get server info from AirPlay device.", e)
|
||||
}
|
||||
|
@ -196,7 +205,7 @@ class AirPlayCastingDevice : CastingDevice {
|
|||
}
|
||||
|
||||
override fun changeSpeed(speed: Double) {
|
||||
this.speed = speed
|
||||
setSpeed(speed)
|
||||
post("rate?value=$speed")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.futo.platformplayer.casting
|
||||
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.getNowDiffMiliseconds
|
||||
import com.futo.platformplayer.models.CastingDeviceInfo
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
|
@ -11,7 +10,6 @@ import kotlinx.serialization.descriptors.SerialDescriptor
|
|||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import java.net.InetAddress
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
enum class CastConnectionState {
|
||||
DISCONNECTED,
|
||||
|
@ -59,36 +57,58 @@ abstract class CastingDevice {
|
|||
onPlayChanged.emit(value);
|
||||
}
|
||||
};
|
||||
var timeReceivedAt: OffsetDateTime = OffsetDateTime.now()
|
||||
private set;
|
||||
|
||||
private var lastTimeChangeTime_ms: Long = 0
|
||||
var time: Double = 0.0
|
||||
set(value) {
|
||||
val changed = value != field;
|
||||
field = value;
|
||||
if (changed) {
|
||||
timeReceivedAt = OffsetDateTime.now();
|
||||
onTimeChanged.emit(value);
|
||||
}
|
||||
};
|
||||
private set
|
||||
|
||||
protected fun setTime(value: Double, changeTime_ms: Long = System.currentTimeMillis()) {
|
||||
if (changeTime_ms > lastTimeChangeTime_ms && value != time) {
|
||||
time = value
|
||||
lastTimeChangeTime_ms = changeTime_ms
|
||||
onTimeChanged.emit(value)
|
||||
}
|
||||
}
|
||||
|
||||
private var lastDurationChangeTime_ms: Long = 0
|
||||
var duration: Double = 0.0
|
||||
private set
|
||||
|
||||
protected fun setDuration(value: Double, changeTime_ms: Long = System.currentTimeMillis()) {
|
||||
if (changeTime_ms > lastDurationChangeTime_ms && value != duration) {
|
||||
duration = value
|
||||
lastDurationChangeTime_ms = changeTime_ms
|
||||
onDurationChanged.emit(value)
|
||||
}
|
||||
}
|
||||
|
||||
private var lastVolumeChangeTime_ms: Long = 0
|
||||
var volume: Double = 1.0
|
||||
set(value) {
|
||||
val changed = value != field;
|
||||
field = value;
|
||||
if (changed) {
|
||||
onVolumeChanged.emit(value);
|
||||
}
|
||||
};
|
||||
private set
|
||||
|
||||
protected fun setVolume(value: Double, changeTime_ms: Long = System.currentTimeMillis()) {
|
||||
if (changeTime_ms > lastVolumeChangeTime_ms && value != volume) {
|
||||
volume = value
|
||||
lastVolumeChangeTime_ms = changeTime_ms
|
||||
onVolumeChanged.emit(value)
|
||||
}
|
||||
}
|
||||
|
||||
private var lastSpeedChangeTime_ms: Long = 0
|
||||
var speed: Double = 1.0
|
||||
set(value) {
|
||||
val changed = value != field;
|
||||
field = value;
|
||||
if (changed) {
|
||||
onSpeedChanged.emit(value);
|
||||
}
|
||||
};
|
||||
private set
|
||||
|
||||
protected fun setSpeed(value: Double, changeTime_ms: Long = System.currentTimeMillis()) {
|
||||
if (changeTime_ms > lastSpeedChangeTime_ms && value != speed) {
|
||||
speed = value
|
||||
lastSpeedChangeTime_ms = changeTime_ms
|
||||
onSpeedChanged.emit(value)
|
||||
}
|
||||
}
|
||||
|
||||
val expectedCurrentTime: Double
|
||||
get() {
|
||||
val diff = timeReceivedAt.getNowDiffMiliseconds().toDouble() / 1000.0;
|
||||
val diff = (System.currentTimeMillis() - lastTimeChangeTime_ms).toDouble() / 1000.0;
|
||||
return time + diff;
|
||||
};
|
||||
var connectionState: CastConnectionState = CastConnectionState.DISCONNECTED
|
||||
|
@ -104,6 +124,7 @@ abstract class CastingDevice {
|
|||
var onConnectionStateChanged = Event1<CastConnectionState>();
|
||||
var onPlayChanged = Event1<Boolean>();
|
||||
var onTimeChanged = Event1<Double>();
|
||||
var onDurationChanged = Event1<Double>();
|
||||
var onVolumeChanged = Event1<Double>();
|
||||
var onSpeedChanged = Event1<Double>();
|
||||
|
||||
|
|
|
@ -74,7 +74,8 @@ class ChromecastCastingDevice : CastingDevice {
|
|||
|
||||
Logger.i(TAG, "Start streaming (streamType: $streamType, contentType: $contentType, contentId: $contentId, resumePosition: $resumePosition, duration: $duration, speed: $speed)");
|
||||
|
||||
time = resumePosition;
|
||||
setTime(resumePosition);
|
||||
setDuration(duration);
|
||||
_streamType = streamType;
|
||||
_contentType = contentType;
|
||||
_contentId = contentId;
|
||||
|
@ -136,7 +137,7 @@ class ChromecastCastingDevice : CastingDevice {
|
|||
return;
|
||||
}
|
||||
|
||||
this.volume = volume
|
||||
setVolume(volume)
|
||||
val setVolumeObject = JSONObject();
|
||||
setVolumeObject.put("type", "SET_VOLUME");
|
||||
|
||||
|
@ -490,7 +491,7 @@ class ChromecastCastingDevice : CastingDevice {
|
|||
if (!sessionIsRunning) {
|
||||
_sessionId = null;
|
||||
_mediaSessionId = null;
|
||||
time = 0.0;
|
||||
setTime(0.0);
|
||||
_transportId = null;
|
||||
Logger.w(TAG, "Session not found.");
|
||||
|
||||
|
@ -510,7 +511,7 @@ class ChromecastCastingDevice : CastingDevice {
|
|||
val volumeLevel = volume.getString("level").toDouble();
|
||||
val volumeMuted = volume.getBoolean("muted");
|
||||
//val volumeStepInterval = volume.getString("stepInterval").toFloat();
|
||||
this.volume = if (volumeMuted) 0.0 else volumeLevel;
|
||||
setVolume(if (volumeMuted) 0.0 else volumeLevel);
|
||||
|
||||
Logger.i(TAG, "Status update received volume (level: $volumeLevel, muted: $volumeMuted)");
|
||||
} else if (type == "MEDIA_STATUS") {
|
||||
|
@ -521,10 +522,16 @@ class ChromecastCastingDevice : CastingDevice {
|
|||
|
||||
val playerState = status.getString("playerState");
|
||||
val currentTime = status.getDouble("currentTime");
|
||||
if (status.has("media")) {
|
||||
val media = status.getJSONObject("media")
|
||||
if (media.has("duration")) {
|
||||
setDuration(media.getDouble("duration"))
|
||||
}
|
||||
}
|
||||
|
||||
isPlaying = playerState == "PLAYING";
|
||||
if (isPlaying) {
|
||||
time = currentTime;
|
||||
setTime(currentTime);
|
||||
}
|
||||
|
||||
val playbackRate = status.getInt("playbackRate");
|
||||
|
|
|
@ -92,7 +92,8 @@ class FCastCastingDevice : CastingDevice {
|
|||
|
||||
Logger.i(TAG, "Start streaming (streamType: $streamType, contentType: $contentType, contentId: $contentId, resumePosition: $resumePosition, duration: $duration, speed: $speed)");
|
||||
|
||||
time = resumePosition;
|
||||
setTime(resumePosition);
|
||||
setDuration(duration);
|
||||
sendMessage(Opcode.PLAY, FCastPlayMessage(
|
||||
container = contentType,
|
||||
url = contentId,
|
||||
|
@ -100,7 +101,7 @@ class FCastCastingDevice : CastingDevice {
|
|||
speed = speed
|
||||
));
|
||||
|
||||
this.speed = speed ?: 1.0
|
||||
setSpeed(speed ?: 1.0);
|
||||
}
|
||||
|
||||
override fun loadContent(contentType: String, content: String, resumePosition: Double, duration: Double, speed: Double?) {
|
||||
|
@ -115,7 +116,8 @@ class FCastCastingDevice : CastingDevice {
|
|||
|
||||
Logger.i(TAG, "Start streaming content (contentType: $contentType, resumePosition: $resumePosition, duration: $duration, speed: $speed)");
|
||||
|
||||
time = resumePosition;
|
||||
setTime(resumePosition);
|
||||
setDuration(duration);
|
||||
sendMessage(Opcode.PLAY, FCastPlayMessage(
|
||||
container = contentType,
|
||||
content = content,
|
||||
|
@ -123,7 +125,7 @@ class FCastCastingDevice : CastingDevice {
|
|||
speed = speed
|
||||
));
|
||||
|
||||
this.speed = speed ?: 1.0
|
||||
setSpeed(speed ?: 1.0);
|
||||
}
|
||||
|
||||
override fun changeVolume(volume: Double) {
|
||||
|
@ -131,7 +133,7 @@ class FCastCastingDevice : CastingDevice {
|
|||
return;
|
||||
}
|
||||
|
||||
this.volume = volume
|
||||
setVolume(volume);
|
||||
sendMessage(Opcode.SET_VOLUME, FCastSetVolumeMessage(volume))
|
||||
}
|
||||
|
||||
|
@ -140,7 +142,7 @@ class FCastCastingDevice : CastingDevice {
|
|||
return;
|
||||
}
|
||||
|
||||
this.speed = speed
|
||||
setSpeed(speed);
|
||||
sendMessage(Opcode.SET_SPEED, FCastSetSpeedMessage(speed))
|
||||
}
|
||||
|
||||
|
@ -330,7 +332,8 @@ class FCastCastingDevice : CastingDevice {
|
|||
}
|
||||
|
||||
val playbackUpdate = FCastCastingDevice.json.decodeFromString<FCastPlaybackUpdateMessage>(json);
|
||||
time = playbackUpdate.time;
|
||||
setTime(playbackUpdate.time, playbackUpdate.generationTime);
|
||||
setDuration(playbackUpdate.duration, playbackUpdate.generationTime);
|
||||
isPlaying = when (playbackUpdate.state) {
|
||||
1 -> true
|
||||
else -> false
|
||||
|
@ -343,7 +346,7 @@ class FCastCastingDevice : CastingDevice {
|
|||
}
|
||||
|
||||
val volumeUpdate = FCastCastingDevice.json.decodeFromString<FCastVolumeUpdateMessage>(json);
|
||||
volume = volumeUpdate.volume;
|
||||
setVolume(volumeUpdate.volume, volumeUpdate.generationTime);
|
||||
}
|
||||
Opcode.PLAYBACK_ERROR -> {
|
||||
if (json == null) {
|
||||
|
|
|
@ -66,6 +66,8 @@ class StateCasting {
|
|||
val onActiveDeviceConnectionStateChanged = Event2<CastingDevice, CastConnectionState>();
|
||||
val onActiveDevicePlayChanged = Event1<Boolean>();
|
||||
val onActiveDeviceTimeChanged = Event1<Double>();
|
||||
val onActiveDeviceDurationChanged = Event1<Double>();
|
||||
val onActiveDeviceVolumeChanged = Event1<Double>();
|
||||
var activeDevice: CastingDevice? = null;
|
||||
private val _client = ManagedHttpClient();
|
||||
var _resumeCastingDevice: CastingDeviceInfo? = null;
|
||||
|
@ -297,9 +299,11 @@ class StateCasting {
|
|||
val ad = activeDevice;
|
||||
if (ad != null) {
|
||||
Logger.i(TAG, "Stopping previous device because a new one is being connected.")
|
||||
ad.onPlayChanged.clear();
|
||||
ad.onTimeChanged.clear();
|
||||
ad.onConnectionStateChanged.clear();
|
||||
device.onConnectionStateChanged.clear();
|
||||
device.onPlayChanged.clear();
|
||||
device.onTimeChanged.clear();
|
||||
device.onVolumeChanged.clear();
|
||||
device.onDurationChanged.clear();
|
||||
ad.stop();
|
||||
}
|
||||
|
||||
|
@ -309,9 +313,11 @@ class StateCasting {
|
|||
if (castConnectionState == CastConnectionState.DISCONNECTED) {
|
||||
Logger.i(TAG, "Clearing events: $castConnectionState");
|
||||
|
||||
device.onConnectionStateChanged.clear();
|
||||
device.onPlayChanged.clear();
|
||||
device.onTimeChanged.clear();
|
||||
device.onConnectionStateChanged.clear();
|
||||
device.onVolumeChanged.clear();
|
||||
device.onDurationChanged.clear();
|
||||
activeDevice = null;
|
||||
}
|
||||
|
||||
|
@ -331,6 +337,12 @@ class StateCasting {
|
|||
device.onPlayChanged.subscribe {
|
||||
invokeInMainScopeIfRequired { onActiveDevicePlayChanged.emit(it) };
|
||||
}
|
||||
device.onDurationChanged.subscribe {
|
||||
invokeInMainScopeIfRequired { onActiveDeviceDurationChanged.emit(it) };
|
||||
};
|
||||
device.onVolumeChanged.subscribe {
|
||||
invokeInMainScopeIfRequired { onActiveDeviceVolumeChanged.emit(it) };
|
||||
};
|
||||
device.onTimeChanged.subscribe {
|
||||
invokeInMainScopeIfRequired { onActiveDeviceTimeChanged.emit(it) };
|
||||
};
|
||||
|
@ -345,6 +357,8 @@ class StateCasting {
|
|||
device.onConnectionStateChanged.clear();
|
||||
device.onPlayChanged.clear();
|
||||
device.onTimeChanged.clear();
|
||||
device.onVolumeChanged.clear();
|
||||
device.onDurationChanged.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,12 +7,20 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.casting.*
|
||||
import com.futo.platformplayer.activities.MainActivity
|
||||
import com.futo.platformplayer.casting.AirPlayCastingDevice
|
||||
import com.futo.platformplayer.casting.CastConnectionState
|
||||
import com.futo.platformplayer.casting.CastingDevice
|
||||
import com.futo.platformplayer.casting.ChromecastCastingDevice
|
||||
import com.futo.platformplayer.casting.FCastCastingDevice
|
||||
import com.futo.platformplayer.casting.StateCasting
|
||||
import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailFragment
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.google.android.material.slider.Slider.OnChangeListener
|
||||
|
@ -27,8 +35,16 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
|||
private lateinit var _textType: TextView;
|
||||
private lateinit var _buttonDisconnect: LinearLayout;
|
||||
private lateinit var _sliderVolume: Slider;
|
||||
private lateinit var _sliderPosition: Slider;
|
||||
private lateinit var _layoutVolumeAdjustable: LinearLayout;
|
||||
private lateinit var _layoutVolumeFixed: LinearLayout;
|
||||
|
||||
private lateinit var _buttonPrevious: ImageButton;
|
||||
private lateinit var _buttonPlay: ImageButton;
|
||||
private lateinit var _buttonPause: ImageButton;
|
||||
private lateinit var _buttonStop: ImageButton;
|
||||
private lateinit var _buttonNext: ImageButton;
|
||||
|
||||
private var _device: CastingDevice? = null;
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -42,17 +58,61 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
|||
_textType = findViewById(R.id.text_type);
|
||||
_buttonDisconnect = findViewById(R.id.button_disconnect);
|
||||
_sliderVolume = findViewById(R.id.slider_volume);
|
||||
_sliderPosition = findViewById(R.id.slider_position);
|
||||
_layoutVolumeAdjustable = findViewById(R.id.layout_volume_adjustable);
|
||||
_layoutVolumeFixed = findViewById(R.id.layout_volume_fixed);
|
||||
|
||||
_buttonPrevious = findViewById(R.id.button_previous);
|
||||
_buttonPrevious.setOnClickListener {
|
||||
(ownerActivity as MainActivity?)?.getFragment<VideoDetailFragment>()?.previousVideo()
|
||||
}
|
||||
|
||||
_buttonPlay = findViewById(R.id.button_play);
|
||||
_buttonPlay.setOnClickListener {
|
||||
StateCasting.instance.activeDevice?.resumeVideo()
|
||||
}
|
||||
|
||||
_buttonPause = findViewById(R.id.button_pause);
|
||||
_buttonPause.setOnClickListener {
|
||||
StateCasting.instance.activeDevice?.pauseVideo()
|
||||
}
|
||||
|
||||
_buttonStop = findViewById(R.id.button_stop);
|
||||
_buttonStop.setOnClickListener {
|
||||
(ownerActivity as MainActivity?)?.getFragment<VideoDetailFragment>()?.closeVideoDetails()
|
||||
StateCasting.instance.activeDevice?.stopVideo()
|
||||
}
|
||||
|
||||
_buttonNext = findViewById(R.id.button_next);
|
||||
_buttonNext.setOnClickListener {
|
||||
(ownerActivity as MainActivity?)?.getFragment<VideoDetailFragment>()?.nextVideo()
|
||||
}
|
||||
|
||||
_buttonClose.setOnClickListener { dismiss(); };
|
||||
_buttonDisconnect.setOnClickListener {
|
||||
StateCasting.instance.activeDevice?.stopCasting();
|
||||
dismiss();
|
||||
};
|
||||
|
||||
_sliderPosition.addOnChangeListener(OnChangeListener { _, value, fromUser ->
|
||||
if (!fromUser) {
|
||||
return@OnChangeListener
|
||||
}
|
||||
|
||||
val activeDevice = StateCasting.instance.activeDevice ?: return@OnChangeListener;
|
||||
try {
|
||||
activeDevice.seekVideo(value.toDouble());
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Failed to change volume.", e);
|
||||
}
|
||||
});
|
||||
|
||||
//TODO: Check if volume slider is properly hidden in all cases
|
||||
_sliderVolume.addOnChangeListener(OnChangeListener { _, value, _ ->
|
||||
_sliderVolume.addOnChangeListener(OnChangeListener { _, value, fromUser ->
|
||||
if (!fromUser) {
|
||||
return@OnChangeListener
|
||||
}
|
||||
|
||||
val activeDevice = StateCasting.instance.activeDevice ?: return@OnChangeListener;
|
||||
if (activeDevice.canSetVolume) {
|
||||
try {
|
||||
|
@ -71,11 +131,21 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
|||
super.show();
|
||||
Logger.i(TAG, "Dialog shown.");
|
||||
|
||||
_device?.onVolumeChanged?.remove(this);
|
||||
_device?.onVolumeChanged?.subscribe {
|
||||
StateCasting.instance.onActiveDeviceVolumeChanged.remove(this);
|
||||
StateCasting.instance.onActiveDeviceVolumeChanged.subscribe {
|
||||
_sliderVolume.value = it.toFloat();
|
||||
};
|
||||
|
||||
StateCasting.instance.onActiveDeviceTimeChanged.remove(this);
|
||||
StateCasting.instance.onActiveDeviceTimeChanged.subscribe {
|
||||
_sliderPosition.value = it.toFloat();
|
||||
};
|
||||
|
||||
StateCasting.instance.onActiveDeviceDurationChanged.remove(this);
|
||||
StateCasting.instance.onActiveDeviceDurationChanged.subscribe {
|
||||
_sliderPosition.valueTo = it.toFloat();
|
||||
};
|
||||
|
||||
_device = StateCasting.instance.activeDevice;
|
||||
val d = _device;
|
||||
val isConnected = d != null && d.connectionState == CastConnectionState.CONNECTED;
|
||||
|
@ -89,7 +159,9 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
|||
|
||||
override fun dismiss() {
|
||||
super.dismiss();
|
||||
_device?.onVolumeChanged?.remove(this);
|
||||
StateCasting.instance.onActiveDeviceVolumeChanged.remove(this);
|
||||
StateCasting.instance.onActiveDeviceDurationChanged.remove(this);
|
||||
StateCasting.instance.onActiveDeviceTimeChanged.remove(this);
|
||||
_device = null;
|
||||
StateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
||||
}
|
||||
|
@ -110,6 +182,9 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
|||
|
||||
_textName.text = d.name;
|
||||
_sliderVolume.value = d.volume.toFloat();
|
||||
_sliderPosition.valueFrom = 0.0f;
|
||||
_sliderPosition.valueTo = d.duration.toFloat();
|
||||
_sliderPosition.value = d.time.toFloat();
|
||||
|
||||
if (d.canSetVolume) {
|
||||
_layoutVolumeAdjustable.visibility = View.VISIBLE;
|
||||
|
|
|
@ -67,6 +67,14 @@ class VideoDetailFragment : MainFragment {
|
|||
constructor() : super() {
|
||||
}
|
||||
|
||||
fun nextVideo() {
|
||||
_viewDetail?.nextVideo(true, true, true);
|
||||
}
|
||||
|
||||
fun previousVideo() {
|
||||
_viewDetail?.prevVideo(true);
|
||||
}
|
||||
|
||||
override fun onShownWithView(parameter: Any?, isBack: Boolean) {
|
||||
super.onShownWithView(parameter, isBack);
|
||||
Logger.i(TAG, "onShownWithView parameter=$parameter")
|
||||
|
|
|
@ -51,7 +51,8 @@
|
|||
android:layout_height="35dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:clickable="true">
|
||||
android:clickable="true"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_device"
|
||||
|
@ -125,11 +126,12 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="20dp">
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_volume"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/volume"
|
||||
android:textSize="14dp"
|
||||
|
@ -150,6 +152,93 @@
|
|||
android:layout_marginEnd="15dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_position_adjustable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_position"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/position"
|
||||
android:textSize="14dp"
|
||||
android:textColor="@color/white"
|
||||
android:fontFamily="@font/inter_regular" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider_position"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
app:thumbColor="@color/colorPrimary"
|
||||
app:trackColorActive="@color/colorPrimary"
|
||||
app:trackColorInactive="@color/gray_67"
|
||||
android:value="0.2"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="1"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginEnd="15dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:divider="@drawable/divider_transparent_8dp"
|
||||
android:showDividers="middle"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<ImageButton
|
||||
android:id="@id/button_previous"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:clickable="true"
|
||||
android:padding="10dp"
|
||||
app:srcCompat="@drawable/ic_skip_previous" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_play"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:padding="20dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:clickable="true"
|
||||
app:srcCompat="@drawable/ic_play_white_nopad" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_pause"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:padding="10dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:clickable="true"
|
||||
app:srcCompat="@drawable/ic_pause_white" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_stop"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:padding="5dp"
|
||||
android:clickable="true"
|
||||
app:srcCompat="@drawable/ic_stop_notif" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@id/button_next"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:clickable="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:padding="10dp"
|
||||
app:srcCompat="@drawable/ic_skip_next" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_volume_fixed"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -715,6 +715,7 @@
|
|||
<string name="login_to_view_your_comments">Login to view your comments</string>
|
||||
<string name="polycentric_is_disabled">Polycentric is disabled</string>
|
||||
<string name="play_pause">Play Pause</string>
|
||||
<string name="position">Position</string>
|
||||
<string-array name="home_screen_array">
|
||||
<item>Recommendations</item>
|
||||
<item>Subscriptions</item>
|
||||
|
|
Loading…
Add table
Reference in a new issue