Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay

This commit is contained in:
Kelvin 2024-01-09 22:25:49 +01:00
commit 9306024d17
11 changed files with 251 additions and 90 deletions

View file

@ -1,5 +1,6 @@
package com.futo.platformplayer
import android.util.Log
import com.google.common.base.CharMatcher
import java.io.ByteArrayOutputStream
import java.io.IOException
@ -215,17 +216,20 @@ private fun ByteArray.toInetAddress(): InetAddress {
}
fun getConnectedSocket(addresses: List<InetAddress>, port: Int): Socket? {
val timeout = 5000
val timeout = 2000
if (addresses.isEmpty()) {
return null;
}
if (addresses.size == 1) {
val socket = Socket()
try {
return Socket().apply { this.connect(InetSocketAddress(addresses[0], port), timeout) };
return socket.apply { this.connect(InetSocketAddress(addresses[0], port), timeout) }
} catch (e: Throwable) {
//Ignored.
Log.i("getConnectedSocket", "Failed to connect to: ${addresses[0]}", e)
socket.close()
}
return null;
@ -264,7 +268,7 @@ fun getConnectedSocket(addresses: List<InetAddress>, port: Int): Socket? {
}
}
} catch (e: Throwable) {
//Ignore
Log.i("getConnectedSocket", "Failed to connect to: $address", e)
}
};

View file

@ -809,7 +809,27 @@ class Settings : FragmentedStorageFileJson() {
var polycentricEnabled: Boolean = true;
}
@FormField(R.string.info, FieldForm.GROUP, -1, 19)
@FormField(R.string.gesture_controls, FieldForm.GROUP, -1, 19)
var gestureControls = GestureControls();
@Serializable
class GestureControls {
@FormField(R.string.volume_slider, FieldForm.TOGGLE, R.string.volume_slider_descr, 1)
var volumeSlider: Boolean = true;
@FormField(R.string.brightness_slider, FieldForm.TOGGLE, R.string.brightness_slider_descr, 2)
var brightnessSlider: Boolean = true;
@FormField(R.string.toggle_full_screen, FieldForm.TOGGLE, R.string.toggle_full_screen_descr, 3)
var toggleFullscreen: Boolean = true;
@FormField(R.string.system_brightness, FieldForm.TOGGLE, R.string.system_brightness_descr, 4)
var useSystemBrightness: Boolean = true;
@FormField(R.string.system_volume, FieldForm.TOGGLE, R.string.system_volume_descr, 5)
var useSystemVolume: Boolean = true;
}
@FormField(R.string.info, FieldForm.GROUP, -1, 20)
var info = Info();
@Serializable
class Info {

View file

@ -328,6 +328,8 @@ class ChromecastCastingDevice : CastingDevice {
val factory = sslContext.socketFactory;
val address = InetSocketAddress(usedRemoteAddress, port)
//Connection loop
while (_scopeIO?.isActive == true) {
Logger.i(TAG, "Connecting to Chromecast.");
@ -341,7 +343,7 @@ class ChromecastCastingDevice : CastingDevice {
connectedSocket = null
} else {
Logger.i(TAG, "Using new socket.")
val s = Socket().apply { this.connect(InetSocketAddress(usedRemoteAddress, port), 5000) }
val s = Socket().apply { this.connect(address, 2000) }
_socket = factory.createSocket(s, s.inetAddress.hostAddress, s.port, true) as SSLSocket
}
@ -444,10 +446,11 @@ class ChromecastCastingDevice : CastingDevice {
while (_scopeIO?.isActive == true) {
try {
sendChannelMessage("sender-0", "receiver-0", "urn:x-cast:com.google.cast.tp.heartbeat", pingObject.toString());
Thread.sleep(5000);
} catch (e: Throwable) {
Log.w(TAG, "Failed to send ping.");
}
Thread.sleep(5000);
}
Logger.i(TAG, "Stopped ping loop.");

View file

@ -15,6 +15,7 @@ import com.futo.platformplayer.casting.models.FCastSetSpeedMessage
import com.futo.platformplayer.casting.models.FCastSetVolumeMessage
import com.futo.platformplayer.casting.models.FCastVersionMessage
import com.futo.platformplayer.casting.models.FCastVolumeUpdateMessage
import com.futo.platformplayer.ensureNotMainThread
import com.futo.platformplayer.getConnectedSocket
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.CastingDeviceInfo
@ -27,9 +28,9 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.math.BigInteger
import java.net.InetAddress
import java.net.InetSocketAddress
@ -82,12 +83,15 @@ class FCastCastingDevice : CastingDevice {
var port: Int = 0;
private var _socket: Socket? = null;
private var _outputStream: DataOutputStream? = null;
private var _inputStream: DataInputStream? = null;
private var _outputStream: OutputStream? = null;
private var _inputStream: InputStream? = null;
private var _scopeIO: CoroutineScope? = null;
private var _started: Boolean = false;
private var _version: Long = 1;
private var _thread: Thread? = null
private var _pingThread: Thread? = null
private var _lastPongTime = -1L
private var _outputStreamLock = Object()
constructor(name: String, addresses: Array<InetAddress>, port: Int) : super() {
this.name = name;
@ -207,7 +211,13 @@ class FCastCastingDevice : CastingDevice {
private fun invokeInIOScopeIfRequired(action: () -> Unit): Boolean {
if(Looper.getMainLooper().thread == Thread.currentThread()) {
_scopeIO?.launch { action(); }
_scopeIO?.launch {
try {
action();
} catch (e: Throwable) {
Logger.e(TAG, "Failed to invoke in IO scope.", e)
}
}
return true;
}
@ -241,7 +251,8 @@ class FCastCastingDevice : CastingDevice {
val adrs = addresses ?: return;
val thread = _thread
if (thread == null || !thread.isAlive) {
val pingThread = _pingThread
if (_started && (thread == null || !thread.isAlive || pingThread == null || !pingThread.isAlive)) {
Log.i(TAG, "(Re)starting thread because the thread has died")
_scopeIO?.let {
@ -258,7 +269,7 @@ class FCastCastingDevice : CastingDevice {
var connectedSocket: Socket? = null
while (_scopeIO?.isActive == true) {
try {
Log.i(TAG, "getConnectedSocket.")
Log.i(TAG, "getConnectedSocket (adrs = [ ${adrs.joinToString(", ")} ], port = ${port}).")
val resultSocket = getConnectedSocket(adrs.toList(), port);
@ -279,6 +290,8 @@ class FCastCastingDevice : CastingDevice {
}
}
val address = InetSocketAddress(usedRemoteAddress, port)
//Connection loop
while (_scopeIO?.isActive == true) {
Logger.i(TAG, "Connecting to FastCast.");
@ -286,20 +299,24 @@ class FCastCastingDevice : CastingDevice {
try {
_socket?.close()
_inputStream?.close()
_outputStream?.close()
if (connectedSocket != null) {
Logger.i(TAG, "Using connected socket.");
_socket = connectedSocket
connectedSocket = null
} else {
Logger.i(TAG, "Using new socket.");
_socket = Socket().apply { this.connect(InetSocketAddress(usedRemoteAddress, port), 5000) };
_socket = Socket().apply { this.connect(address, 2000) };
}
Logger.i(TAG, "Successfully connected to FastCast at $usedRemoteAddress:$port");
_outputStream = DataOutputStream(_socket?.outputStream);
_inputStream = DataInputStream(_socket?.inputStream);
_outputStream = _socket?.outputStream;
_inputStream = _socket?.inputStream;
} catch (e: IOException) {
_socket?.close();
_socket?.close()
_inputStream?.close()
_outputStream?.close()
Logger.i(TAG, "Failed to connect to FastCast.", e);
connectionState = CastConnectionState.CONNECTING;
@ -309,28 +326,38 @@ class FCastCastingDevice : CastingDevice {
localAddress = _socket?.localAddress;
connectionState = CastConnectionState.CONNECTED;
_lastPongTime = -1L
val buffer = ByteArray(4096);
Logger.i(TAG, "Started receiving.");
var exceptionOccurred = false;
while (_scopeIO?.isActive == true && !exceptionOccurred) {
while (_scopeIO?.isActive == true) {
try {
val inputStream = _inputStream ?: break;
Log.d(TAG, "Receiving next packet...");
val b1 = inputStream.readUnsignedByte();
val b2 = inputStream.readUnsignedByte();
val b3 = inputStream.readUnsignedByte();
val b4 = inputStream.readUnsignedByte();
val size = ((b4.toLong() shl 24) or (b3.toLong() shl 16) or (b2.toLong() shl 8) or b1.toLong()).toInt();
var headerBytesRead = 0
while (headerBytesRead < 4) {
val read = inputStream.read(buffer, headerBytesRead, 4 - headerBytesRead)
if (read == -1)
throw Exception("Stream closed")
headerBytesRead += read
}
val size = ((buffer[3].toLong() shl 24) or (buffer[2].toLong() shl 16) or (buffer[1].toLong() shl 8) or buffer[0].toLong()).toInt();
if (size > buffer.size) {
Logger.w(TAG, "Skipping packet that is too large $size bytes.")
inputStream.skip(size.toLong());
continue;
Logger.w(TAG, "Packets larger than $size bytes are not supported.")
break
}
Log.d(TAG, "Received header indicating $size bytes. Waiting for message.");
inputStream.read(buffer, 0, size);
var bytesRead = 0
while (bytesRead < size) {
val read = inputStream.read(buffer, bytesRead, size - bytesRead)
if (read == -1)
throw Exception("Stream closed")
bytesRead += read
}
val messageBytes = buffer.sliceArray(IntRange(0, size));
Log.d(TAG, "Received $size bytes: ${messageBytes.toHexString()}.");
@ -344,19 +371,22 @@ class FCastCastingDevice : CastingDevice {
try {
handleMessage(Opcode.find(opcode), json);
} catch (e: Throwable) {
Logger.w(TAG, "Failed to handle message.", e);
Logger.w(TAG, "Failed to handle message.", e)
break
}
} catch (e: java.net.SocketException) {
Logger.e(TAG, "Socket exception while receiving.", e);
exceptionOccurred = true;
break
} catch (e: Throwable) {
Logger.e(TAG, "Exception while receiving.", e);
exceptionOccurred = true;
break
}
}
try {
_socket?.close();
_socket?.close()
_inputStream?.close()
_outputStream?.close()
Logger.i(TAG, "Socket disconnected.");
} catch (e: Throwable) {
Logger.e(TAG, "Failed to close socket.", e)
@ -368,7 +398,41 @@ class FCastCastingDevice : CastingDevice {
Logger.i(TAG, "Stopped connection loop.");
connectionState = CastConnectionState.DISCONNECTED;
}.apply { start() };
}.apply { start() }
_pingThread = Thread {
Logger.i(TAG, "Started ping loop.")
while (_scopeIO?.isActive == true) {
try {
send(Opcode.Ping)
} catch (e: Throwable) {
Log.w(TAG, "Failed to send ping.")
try {
_socket?.close()
_inputStream?.close()
_outputStream?.close()
} catch (e: Throwable) {
Log.w(TAG, "Failed to close socket.", e)
}
}
/*if (_lastPongTime != -1L && System.currentTimeMillis() - _lastPongTime > 6000) {
Logger.w(TAG, "Closing socket due to last pong time being larger than 6 seconds.")
try {
_socket?.close()
} catch (e: Throwable) {
Log.w(TAG, "Failed to close socket.", e)
}
}*/
Thread.sleep(2000)
}
Logger.i(TAG, "Stopped ping loop.");
}.apply { start() }
} else {
Log.i(TAG, "Thread was still alive, not restarted")
}
@ -421,39 +485,44 @@ class FCastCastingDevice : CastingDevice {
Logger.i(TAG, "Remote version received: $version")
}
Opcode.Ping -> send(Opcode.Pong)
Opcode.Pong -> _lastPongTime = System.currentTimeMillis()
else -> { }
}
}
private fun send(opcode: Opcode, message: String? = null) {
try {
val data: ByteArray = message?.encodeToByteArray() ?: ByteArray(0)
val size = 1 + data.size
val outputStream = _outputStream
if (outputStream == null) {
Log.w(TAG, "Failed to send $size bytes, output stream is null.")
return
ensureNotMainThread()
synchronized (_outputStreamLock) {
try {
val data: ByteArray = message?.encodeToByteArray() ?: ByteArray(0)
val size = 1 + data.size
val outputStream = _outputStream
if (outputStream == null) {
Log.w(TAG, "Failed to send $size bytes, output stream is null.")
return
}
val serializedSizeLE = ByteArray(4)
serializedSizeLE[0] = (size and 0xff).toByte()
serializedSizeLE[1] = (size shr 8 and 0xff).toByte()
serializedSizeLE[2] = (size shr 16 and 0xff).toByte()
serializedSizeLE[3] = (size shr 24 and 0xff).toByte()
outputStream.write(serializedSizeLE)
val opcodeBytes = ByteArray(1)
opcodeBytes[0] = opcode.value
outputStream.write(opcodeBytes)
if (data.isNotEmpty()) {
outputStream.write(data)
}
Log.d(TAG, "Sent $size bytes: (opcode: $opcode, body: $message).")
} catch (e: Throwable) {
Log.i(TAG, "Failed to send message.", e)
throw e
}
val serializedSizeLE = ByteArray(4)
serializedSizeLE[0] = (size and 0xff).toByte()
serializedSizeLE[1] = (size shr 8 and 0xff).toByte()
serializedSizeLE[2] = (size shr 16 and 0xff).toByte()
serializedSizeLE[3] = (size shr 24 and 0xff).toByte()
outputStream.write(serializedSizeLE)
val opcodeBytes = ByteArray(1)
opcodeBytes[0] = opcode.value
outputStream.write(opcodeBytes)
if (data.isNotEmpty()) {
outputStream.write(data)
}
Log.d(TAG, "Sent $size bytes: (opcode: $opcode, body: $message).")
} catch (e: Throwable) {
Log.i(TAG, "Failed to send message.", e)
throw e
}
}
@ -473,6 +542,7 @@ class FCastCastingDevice : CastingDevice {
_started = false;
//TODO: Kill and/or join thread?
_thread = null;
_pingThread = null;
val socket = _socket;
val scopeIO = _scopeIO;
@ -482,6 +552,8 @@ class FCastCastingDevice : CastingDevice {
scopeIO.launch {
socket.close();
_inputStream?.close()
_outputStream?.close()
connectionState = CastConnectionState.DISCONNECTED;
scopeIO.cancel();
Logger.i(TAG, "Cancelled scopeIO with open socket.")

View file

@ -143,7 +143,9 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
StateCasting.instance.onActiveDeviceDurationChanged.remove(this);
StateCasting.instance.onActiveDeviceDurationChanged.subscribe {
_sliderPosition.valueTo = it.toFloat().coerceAtLeast(1.0f);
val dur = it.toFloat().coerceAtLeast(1.0f)
_sliderPosition.value = _sliderPosition.value.coerceAtLeast(0.0f).coerceAtMost(dur);
_sliderPosition.valueTo = dur
};
_device = StateCasting.instance.activeDevice;
@ -185,8 +187,10 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
_sliderPosition.valueFrom = 0.0f;
_sliderVolume.valueFrom = 0.0f;
_sliderVolume.value = d.volume.toFloat().coerceAtLeast(0.0f).coerceAtMost(_sliderVolume.valueTo);
_sliderPosition.valueTo = d.duration.toFloat().coerceAtLeast(1.0f);
_sliderPosition.value = d.time.toFloat().coerceAtLeast(0.0f).coerceAtMost(_sliderVolume.valueTo);
val dur = d.duration.toFloat().coerceAtLeast(1.0f)
_sliderPosition.value = d.time.toFloat().coerceAtLeast(0.0f).coerceAtMost(dur)
_sliderPosition.valueTo = dur
if (d.canSetVolume) {
_layoutVolumeAdjustable.visibility = View.VISIBLE;

View file

@ -145,7 +145,6 @@ import com.futo.polycentric.core.Opinion
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
import com.google.protobuf.ByteString
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -1372,7 +1371,9 @@ class VideoDetailView : ConstraintLayout {
val toResume = _videoResumePositionMilliseconds;
_videoResumePositionMilliseconds = 0;
loadCurrentVideo(toResume);
_player.setGestureSoundFactor(1.0f);
if (!Settings.instance.gestureControls.useSystemVolume) {
_player.setGestureSoundFactor(1.0f);
}
updateQueueState();

View file

@ -13,17 +13,17 @@ import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageButton
import android.widget.TextView
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.R
import com.futo.platformplayer.stores.SearchHistoryStorage
import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.fragment.mainactivity.main.*
import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragment
import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragmentData
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.SearchType
import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.SearchHistoryStorage
class SearchTopBarFragment : TopFragment() {
private val TAG = "SearchTopBarFragment"
@ -54,11 +54,12 @@ class SearchTopBarFragment : TopFragment() {
private val _searchDoneListener = object : TextView.OnEditorActionListener {
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
if (actionId != EditorInfo.IME_ACTION_DONE)
val isEnterPress = event?.keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN
if (actionId != EditorInfo.IME_ACTION_DONE && !isEnterPress)
return false
onDone();
return true;
onDone()
return true
}
};

View file

@ -3,8 +3,10 @@ package com.futo.platformplayer.views.behavior
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.app.Activity
import android.content.Context
import android.graphics.drawable.Animatable
import android.media.AudioManager
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.LayoutInflater
@ -19,6 +21,7 @@ import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import androidx.core.view.GestureDetectorCompat
import com.futo.platformplayer.R
import com.futo.platformplayer.Settings
import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.logging.Logger
@ -59,6 +62,7 @@ class GestureControlView : LinearLayout {
private val _progressSound: CircularProgressBar;
private var _animatorSound: ObjectAnimator? = null;
private var _brightnessFactor = 1.0f;
private var _originalBrightnessFactor = 1.0f;
private var _adjustingBrightness: Boolean = false;
private val _layoutControlsBrightness: FrameLayout;
private val _progressBrightness: CircularProgressBar;
@ -126,11 +130,11 @@ class GestureControlView : LinearLayout {
val rx = (p0.x + p1.x) / (2 * width);
val ry = (p0.y + p1.y) / (2 * height);
if (ry > 0.1 && ry < 0.9) {
if (_isFullScreen && rx < 0.2) {
if (Settings.instance.gestureControls.brightnessSlider && _isFullScreen && rx < 0.2) {
startAdjustingBrightness();
} else if (_isFullScreen && rx > 0.8) {
} else if (Settings.instance.gestureControls.volumeSlider && _isFullScreen && rx > 0.8) {
startAdjustingSound();
} else if (fullScreenGestureEnabled && rx in 0.3..0.7) {
} else if (Settings.instance.gestureControls.toggleFullscreen && fullScreenGestureEnabled && rx in 0.3..0.7) {
if (_isFullScreen) {
startAdjustingFullscreenDown();
} else {
@ -559,10 +563,34 @@ class GestureControlView : LinearLayout {
fun setFullscreen(isFullScreen: Boolean) {
if (isFullScreen) {
val c = context
if (c is Activity && Settings.instance.gestureControls.useSystemBrightness) {
_brightnessFactor = c.window.attributes.screenBrightness
if (_brightnessFactor == -1.0f) {
_brightnessFactor = android.provider.Settings.System.getInt(
context.contentResolver,
android.provider.Settings.System.SCREEN_BRIGHTNESS
) / 255.0f;
}
_originalBrightnessFactor = _brightnessFactor
}
if (Settings.instance.gestureControls.useSystemVolume) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
_soundFactor = currentVolume.toFloat() / maxVolume.toFloat()
}
onBrightnessAdjusted.emit(_brightnessFactor);
onSoundAdjusted.emit(_soundFactor);
} else {
onBrightnessAdjusted.emit(1.0f);
val c = context
if (c is Activity && Settings.instance.gestureControls.useSystemBrightness) {
onBrightnessAdjusted.emit(_originalBrightnessFactor);
} else {
onBrightnessAdjusted.emit(1.0f);
}
//onSoundAdjusted.emit(1.0f);
stopAdjustingBrightness();
stopAdjustingSound();

View file

@ -24,8 +24,8 @@ import com.futo.platformplayer.casting.AirPlayCastingDevice
import com.futo.platformplayer.casting.StateCasting
import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event2
import com.futo.platformplayer.formatDuration
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.toHumanTime
import com.futo.platformplayer.views.behavior.GestureControlView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -252,8 +252,8 @@ class CastView : ConstraintLayout {
.load(video.thumbnails.getHQThumbnail())
.placeholder(R.drawable.placeholder_video_thumbnail)
.into(_thumbnail);
_textPosition.text = position.toHumanTime(false);
_textDuration.text = video.duration.toHumanTime(false);
_textPosition.text = (position * 1000).formatDuration();
_textDuration.text = (video.duration * 1000).formatDuration();
_timeBar.setPosition(position);
_timeBar.setDuration(video.duration);
}
@ -261,7 +261,7 @@ class CastView : ConstraintLayout {
@OptIn(UnstableApi::class)
fun setTime(ms: Long) {
updateCurrentChapter(ms);
_textPosition.text = ms.toHumanTime(true);
_textPosition.text = ms.formatDuration();
_timeBar.setPosition(ms / 1000);
StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), ms);
}

View file

@ -1,15 +1,18 @@
package com.futo.platformplayer.views.video
import android.app.Activity
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.media.AudioManager
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.TextView
@ -235,14 +238,29 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
gestureControl.setupTouchArea(_layoutControls, background);
gestureControl.onSeek.subscribe { seekFromCurrent(it); };
gestureControl.onSoundAdjusted.subscribe { setVolume(it) };
gestureControl.onSoundAdjusted.subscribe {
if (Settings.instance.gestureControls.useSystemVolume) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (it * maxVolume).toInt(), 0)
} else {
setVolume(it)
}
};
gestureControl.onToggleFullscreen.subscribe { setFullScreen(!isFullScreen) };
gestureControl.onBrightnessAdjusted.subscribe {
if (it == 1.0f) {
_overlay_brightness.visibility = View.GONE;
if (context is Activity && Settings.instance.gestureControls.useSystemBrightness) {
val window = context.window
val layout: WindowManager.LayoutParams = window.attributes
layout.screenBrightness = it
window.attributes = layout
} else {
_overlay_brightness.visibility = View.VISIBLE;
_overlay_brightness.setBackgroundColor(Color.valueOf(0.0f, 0.0f, 0.0f, (1.0f - it)).toArgb());
if (it == 1.0f) {
_overlay_brightness.visibility = View.GONE;
} else {
_overlay_brightness.visibility = View.VISIBLE;
_overlay_brightness.setBackgroundColor(Color.valueOf(0.0f, 0.0f, 0.0f, (1.0f - it)).toArgb());
}
}
};
@ -531,7 +549,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
_videoView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
_videoControls_fullscreen.show();
videoControls.hide();
videoControls.hideImmediately();
}
else {
val lp = background.layoutParams as ConstraintLayout.LayoutParams;
@ -543,7 +561,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
_videoView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
videoControls.show();
_videoControls_fullscreen.hide();
_videoControls_fullscreen.hideImmediately();
}
fitOrFill(fullScreen);
@ -730,7 +748,6 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
}
}
fun setGestureSoundFactor(soundFactor: Float) {
gestureControl.setSoundFactor(soundFactor);
}

View file

@ -344,6 +344,17 @@
<string name="get_answers_to_common_questions">Get answers to common questions</string>
<string name="give_feedback_on_the_application">Give feedback on the application</string>
<string name="info">Info</string>
<string name="gesture_controls">Gesture controls</string>
<string name="volume_slider">Volume slider</string>
<string name="volume_slider_descr">Enable slide gesture to change volume</string>
<string name="brightness_slider">Brightness slider</string>
<string name="brightness_slider_descr">Enable slide gesture to change brightness</string>
<string name="toggle_full_screen">Toggle full screen</string>
<string name="toggle_full_screen_descr">Enable swipe gesture to toggle fullscreen</string>
<string name="system_brightness">System brightness</string>
<string name="system_brightness_descr">Gesture controls adjust system brightness</string>
<string name="system_volume">System volume</string>
<string name="system_volume_descr">Gesture controls adjust system volume</string>
<string name="live_chat_webview">Live Chat Webview</string>
<string name="full_screen_portrait">Fullscreen portrait</string>
<string name="allow_full_screen_portrait">Allow fullscreen portrait</string>