mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-05-01 23:24:34 +02:00
Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay
This commit is contained in:
commit
7cdded8fd7
@ -52,7 +52,7 @@
|
|||||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
|
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.FutoVideo.NoActionBar"
|
android:theme="@style/Theme.FutoVideo.NoActionBar"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleInstance"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:supportsPictureInPicture="true">
|
android:supportsPictureInPicture="true">
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import android.graphics.Color
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
import android.view.KeyEvent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
@ -57,11 +58,21 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol
|
|||||||
_editComment = findViewById(R.id.edit_comment);
|
_editComment = findViewById(R.id.edit_comment);
|
||||||
_textCharacterCount = findViewById(R.id.character_count);
|
_textCharacterCount = findViewById(R.id.character_count);
|
||||||
_textCharacterCountMax = findViewById(R.id.character_count_max);
|
_textCharacterCountMax = findViewById(R.id.character_count_max);
|
||||||
|
setCanceledOnTouchOutside(false)
|
||||||
|
setOnKeyListener { _, keyCode, event ->
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
|
||||||
|
handleCloseAttempt()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_editComment.addTextChangedListener(object : TextWatcher {
|
_editComment.addTextChangedListener(object : TextWatcher {
|
||||||
override fun afterTextChanged(s: Editable?) = Unit
|
override fun afterTextChanged(s: Editable?) = Unit
|
||||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, c: Int) {
|
||||||
|
val count = s?.length ?: 0;
|
||||||
_textCharacterCount.text = count.toString();
|
_textCharacterCount.text = count.toString();
|
||||||
|
|
||||||
if (count > PolycentricPlatformComment.MAX_COMMENT_SIZE) {
|
if (count > PolycentricPlatformComment.MAX_COMMENT_SIZE) {
|
||||||
@ -79,10 +90,13 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol
|
|||||||
_inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager;
|
_inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager;
|
||||||
|
|
||||||
_buttonCancel.setOnClickListener {
|
_buttonCancel.setOnClickListener {
|
||||||
clearFocus();
|
handleCloseAttempt()
|
||||||
dismiss();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setOnCancelListener {
|
||||||
|
handleCloseAttempt()
|
||||||
|
}
|
||||||
|
|
||||||
_buttonCreate.setOnClickListener {
|
_buttonCreate.setOnClickListener {
|
||||||
clearFocus();
|
clearFocus();
|
||||||
|
|
||||||
@ -134,6 +148,22 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol
|
|||||||
focus();
|
focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleCloseAttempt() {
|
||||||
|
if (_editComment.text.isEmpty()) {
|
||||||
|
clearFocus()
|
||||||
|
dismiss()
|
||||||
|
} else {
|
||||||
|
UIDialogs.showConfirmationDialog(
|
||||||
|
context,
|
||||||
|
context.resources.getString(R.string.not_empty_close),
|
||||||
|
action = {
|
||||||
|
clearFocus()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun focus() {
|
private fun focus() {
|
||||||
_editComment.requestFocus();
|
_editComment.requestFocus();
|
||||||
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
||||||
|
@ -8,6 +8,7 @@ import android.widget.AdapterView
|
|||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageButton
|
||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
@ -25,11 +26,20 @@ class CreatorsFragment : MainFragment() {
|
|||||||
private var _overlayContainer: FrameLayout? = null;
|
private var _overlayContainer: FrameLayout? = null;
|
||||||
private var _containerSearch: FrameLayout? = null;
|
private var _containerSearch: FrameLayout? = null;
|
||||||
private var _editSearch: EditText? = null;
|
private var _editSearch: EditText? = null;
|
||||||
|
private var _buttonClearSearch: ImageButton? = null
|
||||||
|
|
||||||
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
val view = inflater.inflate(R.layout.fragment_creators, container, false);
|
val view = inflater.inflate(R.layout.fragment_creators, container, false);
|
||||||
_containerSearch = view.findViewById(R.id.container_search);
|
_containerSearch = view.findViewById(R.id.container_search);
|
||||||
_editSearch = view.findViewById(R.id.edit_search);
|
val editSearch: EditText = view.findViewById(R.id.edit_search);
|
||||||
|
val buttonClearSearch: ImageButton = view.findViewById(R.id.button_clear_search)
|
||||||
|
_editSearch = editSearch
|
||||||
|
_buttonClearSearch = buttonClearSearch
|
||||||
|
buttonClearSearch.setOnClickListener {
|
||||||
|
editSearch.text.clear()
|
||||||
|
editSearch.requestFocus()
|
||||||
|
_buttonClearSearch?.visibility = View.INVISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
val adapter = SubscriptionAdapter(inflater, getString(R.string.confirm_delete_subscription));
|
val adapter = SubscriptionAdapter(inflater, getString(R.string.confirm_delete_subscription));
|
||||||
adapter.onClick.subscribe { platformUser -> navigate<ChannelFragment>(platformUser) };
|
adapter.onClick.subscribe { platformUser -> navigate<ChannelFragment>(platformUser) };
|
||||||
@ -51,7 +61,12 @@ class CreatorsFragment : MainFragment() {
|
|||||||
_spinnerSortBy = spinnerSortBy;
|
_spinnerSortBy = spinnerSortBy;
|
||||||
|
|
||||||
_editSearch?.addTextChangedListener {
|
_editSearch?.addTextChangedListener {
|
||||||
adapter.query = it.toString();
|
adapter.query = it.toString()
|
||||||
|
if (it?.isEmpty() == true) {
|
||||||
|
_buttonClearSearch?.visibility = View.INVISIBLE
|
||||||
|
} else {
|
||||||
|
_buttonClearSearch?.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_subscriptions);
|
val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_subscriptions);
|
||||||
|
@ -1799,8 +1799,13 @@ class VideoDetailView : ConstraintLayout {
|
|||||||
private fun onSourceChanged(videoSource: IVideoSource?, audioSource: IAudioSource?, resume: Boolean){
|
private fun onSourceChanged(videoSource: IVideoSource?, audioSource: IAudioSource?, resume: Boolean){
|
||||||
Logger.i(TAG, "onSourceChanged(videoSource=$videoSource, audioSource=$audioSource, resume=$resume)")
|
Logger.i(TAG, "onSourceChanged(videoSource=$videoSource, audioSource=$audioSource, resume=$resume)")
|
||||||
|
|
||||||
if((videoSource == null || videoSource is LocalVideoSource) && (audioSource == null || audioSource is LocalAudioSource))
|
if((videoSource == null || videoSource is LocalVideoSource) && (audioSource == null || audioSource is LocalAudioSource)) {
|
||||||
|
Logger.i(TAG, "Time since last offline playback toast: " + (System.currentTimeMillis() - _lastOfflinePlaybackToastTime).toString())
|
||||||
|
if (System.currentTimeMillis() - _lastOfflinePlaybackToastTime > 5000) {
|
||||||
UIDialogs.toast(context, context.getString(R.string.offline_playback), false);
|
UIDialogs.toast(context, context.getString(R.string.offline_playback), false);
|
||||||
|
_lastOfflinePlaybackToastTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
}
|
||||||
//If LiveStream, set to end
|
//If LiveStream, set to end
|
||||||
if(videoSource is IDashManifestSource || videoSource is IHLSManifestSource) {
|
if(videoSource is IDashManifestSource || videoSource is IHLSManifestSource) {
|
||||||
if (video?.isLive == true) {
|
if (video?.isLive == true) {
|
||||||
@ -2379,8 +2384,8 @@ class VideoDetailView : ConstraintLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isLandscapeVideo(): Boolean? {
|
fun isLandscapeVideo(): Boolean? {
|
||||||
var videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width
|
val videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width
|
||||||
var videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height
|
val videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height
|
||||||
|
|
||||||
return if (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0){
|
return if (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0){
|
||||||
null
|
null
|
||||||
@ -3030,8 +3035,6 @@ class VideoDetailView : ConstraintLayout {
|
|||||||
const val TAG_MORE = "MORE";
|
const val TAG_MORE = "MORE";
|
||||||
|
|
||||||
private val _buttonPinStore = FragmentedStorage.get<StringArrayStorage>("videoPinnedButtons");
|
private val _buttonPinStore = FragmentedStorage.get<StringArrayStorage>("videoPinnedButtons");
|
||||||
|
private var _lastOfflinePlaybackToastTime: Long = 0
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,70 +12,109 @@ import com.futo.platformplayer.activities.MainActivity
|
|||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.receivers.MediaControlReceiver
|
import com.futo.platformplayer.receivers.MediaControlReceiver
|
||||||
import com.futo.platformplayer.timestampRegex
|
import com.futo.platformplayer.timestampRegex
|
||||||
import com.futo.platformplayer.views.behavior.NonScrollingTextView
|
|
||||||
import com.futo.platformplayer.views.behavior.NonScrollingTextView.Companion
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
class PlatformLinkMovementMethod : LinkMovementMethod {
|
class PlatformLinkMovementMethod(private val _context: Context) : LinkMovementMethod() {
|
||||||
private val _context: Context;
|
|
||||||
|
|
||||||
constructor(context: Context) : super() {
|
private var pressedLinks: Array<URLSpan>? = null
|
||||||
_context = context;
|
private var linkPressed = false
|
||||||
}
|
private var downX = 0f
|
||||||
|
private var downY = 0f
|
||||||
|
private val touchSlop = 20
|
||||||
|
|
||||||
override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean {
|
override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean {
|
||||||
val action = event.action;
|
val action = event.actionMasked
|
||||||
Logger.i(TAG, "onTouchEvent (action = $action)")
|
|
||||||
if (action == MotionEvent.ACTION_UP) {
|
|
||||||
val x = event.x.toInt() - widget.totalPaddingLeft + widget.scrollX;
|
|
||||||
val y = event.y.toInt() - widget.totalPaddingTop + widget.scrollY;
|
|
||||||
|
|
||||||
val layout = widget.layout;
|
|
||||||
val line = layout.getLineForVertical(y);
|
|
||||||
val off = layout.getOffsetForHorizontal(line, x.toFloat());
|
|
||||||
val links = buffer.getSpans(off, off, URLSpan::class.java);
|
|
||||||
|
|
||||||
|
when (action) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
val links = findLinksAtTouchPosition(widget, buffer, event)
|
||||||
if (links.isNotEmpty()) {
|
if (links.isNotEmpty()) {
|
||||||
runBlocking {
|
pressedLinks = links
|
||||||
for (link in links) {
|
linkPressed = true
|
||||||
Logger.i(TAG) { "Link clicked '${link.url}'." };
|
downX = event.x
|
||||||
|
downY = event.y
|
||||||
if (_context is MainActivity) {
|
widget.parent?.requestDisallowInterceptTouchEvent(true)
|
||||||
if (_context.handleUrl(link.url)) {
|
return true
|
||||||
continue;
|
} else {
|
||||||
|
linkPressed = false
|
||||||
|
pressedLinks = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timestampRegex.matches(link.url)) {
|
MotionEvent.ACTION_MOVE -> {
|
||||||
val tokens = link.url.split(':');
|
if (linkPressed) {
|
||||||
|
val dx = event.x - downX
|
||||||
|
val dy = event.y - downY
|
||||||
|
if (Math.abs(dx) > touchSlop || Math.abs(dy) > touchSlop) {
|
||||||
|
linkPressed = false
|
||||||
|
pressedLinks = null
|
||||||
|
widget.parent?.requestDisallowInterceptTouchEvent(false)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var time_s = -1L;
|
MotionEvent.ACTION_UP -> {
|
||||||
if (tokens.size == 2) {
|
if (linkPressed && pressedLinks != null) {
|
||||||
time_s = tokens[0].toLong() * 60 + tokens[1].toLong();
|
val dx = event.x - downX
|
||||||
} else if (tokens.size == 3) {
|
val dy = event.y - downY
|
||||||
time_s =
|
if (Math.abs(dx) <= touchSlop && Math.abs(dy) <= touchSlop) {
|
||||||
tokens[0].toLong() * 60 * 60 + tokens[1].toLong() * 60 + tokens[2].toLong();
|
runBlocking {
|
||||||
|
for (link in pressedLinks!!) {
|
||||||
|
Logger.i(TAG) { "Link clicked '${link.url}'." }
|
||||||
|
|
||||||
|
if (_context is MainActivity) {
|
||||||
|
if (_context.handleUrl(link.url)) continue
|
||||||
|
if (timestampRegex.matches(link.url)) {
|
||||||
|
val tokens = link.url.split(':')
|
||||||
|
var time_s = -1L
|
||||||
|
when (tokens.size) {
|
||||||
|
2 -> time_s = tokens[0].toLong() * 60 + tokens[1].toLong()
|
||||||
|
3 -> time_s = tokens[0].toLong() * 3600 +
|
||||||
|
tokens[1].toLong() * 60 +
|
||||||
|
tokens[2].toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time_s != -1L) {
|
if (time_s != -1L) {
|
||||||
MediaControlReceiver.onSeekToReceived.emit(time_s * 1000);
|
MediaControlReceiver.onSeekToReceived.emit(time_s * 1000)
|
||||||
continue;
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(link.url)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pressedLinks = null
|
||||||
|
linkPressed = false
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
pressedLinks = null
|
||||||
|
linkPressed = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_CANCEL -> {
|
||||||
_context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(link.url)));
|
linkPressed = false
|
||||||
|
pressedLinks = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return super.onTouchEvent(widget, buffer, event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onTouchEvent(widget, buffer, event);
|
private fun findLinksAtTouchPosition(widget: TextView, buffer: Spannable, event: MotionEvent): Array<URLSpan> {
|
||||||
|
val x = (event.x - widget.totalPaddingLeft + widget.scrollX).toInt()
|
||||||
|
val y = (event.y - widget.totalPaddingTop + widget.scrollY).toInt()
|
||||||
|
|
||||||
|
val layout = widget.layout ?: return emptyArray()
|
||||||
|
val line = layout.getLineForVertical(y)
|
||||||
|
val off = layout.getOffsetForHorizontal(line, x.toFloat())
|
||||||
|
return buffer.getSpans(off, off, URLSpan::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "PlatformLinkMovementMethod";
|
const val TAG = "PlatformLinkMovementMethod"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -112,6 +112,7 @@ class StateSync {
|
|||||||
Logger.i(TAG, "Sync key pair initialized (public key = ${publicKey})")
|
Logger.i(TAG, "Sync key pair initialized (public key = ${publicKey})")
|
||||||
|
|
||||||
_thread = Thread {
|
_thread = Thread {
|
||||||
|
try {
|
||||||
val serverSocket = ServerSocket(PORT)
|
val serverSocket = ServerSocket(PORT)
|
||||||
_serverSocket = serverSocket
|
_serverSocket = serverSocket
|
||||||
|
|
||||||
@ -125,6 +126,10 @@ class StateSync {
|
|||||||
|
|
||||||
session.startAsResponder()
|
session.startAsResponder()
|
||||||
}
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Logger.e(TAG, "Failed to bind server socket to port ${PORT}", e)
|
||||||
|
UIDialogs.toast("Failed to start sync, port in use")
|
||||||
|
}
|
||||||
}.apply { start() }
|
}.apply { start() }
|
||||||
|
|
||||||
if (Settings.instance.synchronization.connectLast) {
|
if (Settings.instance.synchronization.connectLast) {
|
||||||
|
@ -16,70 +16,110 @@ import com.futo.platformplayer.timestampRegex
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
class NonScrollingTextView : androidx.appcompat.widget.AppCompatTextView {
|
class NonScrollingTextView : androidx.appcompat.widget.AppCompatTextView {
|
||||||
|
private var _lastTouchedLinks: Array<URLSpan>? = null
|
||||||
|
private var downX = 0f
|
||||||
|
private var downY = 0f
|
||||||
|
private var linkPressed = false
|
||||||
|
private val touchSlop = 20
|
||||||
|
|
||||||
constructor(context: Context) : super(context) {}
|
constructor(context: Context) : super(context) {}
|
||||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
|
||||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
|
||||||
|
|
||||||
override fun scrollTo(x: Int, y: Int) {
|
override fun scrollTo(x: Int, y: Int) {
|
||||||
//do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||||
val action = event?.action
|
val action = event?.actionMasked
|
||||||
Logger.i(TAG, "onTouchEvent (action = $action)");
|
if (event == null) return super.onTouchEvent(event)
|
||||||
|
|
||||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
|
when (action) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
val x = event.x.toInt()
|
val x = event.x.toInt()
|
||||||
val y = event.y.toInt()
|
val y = event.y.toInt()
|
||||||
|
|
||||||
val layout: Layout? = this.layout
|
val layout: Layout? = this.layout
|
||||||
if (layout != null) {
|
if (layout != null && this.text is Spannable) {
|
||||||
val line = layout.getLineForVertical(y)
|
val offset = layout.getOffsetForHorizontal(layout.getLineForVertical(y), x.toFloat())
|
||||||
val offset = layout.getOffsetForHorizontal(line, x.toFloat())
|
val text = this.text as Spannable
|
||||||
|
|
||||||
val text = this.text
|
|
||||||
if (text is Spannable) {
|
|
||||||
val links = text.getSpans(offset, offset, URLSpan::class.java)
|
val links = text.getSpans(offset, offset, URLSpan::class.java)
|
||||||
if (links.isNotEmpty()) {
|
if (links.isNotEmpty()) {
|
||||||
runBlocking {
|
parent?.requestDisallowInterceptTouchEvent(true)
|
||||||
for (link in links) {
|
_lastTouchedLinks = links
|
||||||
Logger.i(PlatformLinkMovementMethod.TAG) { "Link clicked '${link.url}'." };
|
downX = event.x
|
||||||
|
downY = event.y
|
||||||
val c = context;
|
linkPressed = true
|
||||||
if (c is MainActivity) {
|
return true
|
||||||
if (c.handleUrl(link.url)) {
|
} else {
|
||||||
continue;
|
linkPressed = false
|
||||||
}
|
_lastTouchedLinks = null
|
||||||
|
|
||||||
if (timestampRegex.matches(link.url)) {
|
|
||||||
val tokens = link.url.split(':');
|
|
||||||
|
|
||||||
var time_s = -1L;
|
|
||||||
if (tokens.size == 2) {
|
|
||||||
time_s = tokens[0].toLong() * 60 + tokens[1].toLong();
|
|
||||||
} else if (tokens.size == 3) {
|
|
||||||
time_s = tokens[0].toLong() * 60 * 60 + tokens[1].toLong() * 60 + tokens[2].toLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (time_s != -1L) {
|
|
||||||
MediaControlReceiver.onSeekToReceived.emit(time_s * 1000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(link.url)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_MOVE -> {
|
||||||
|
if (linkPressed) {
|
||||||
|
val dx = event.x - downX
|
||||||
|
val dy = event.y - downY
|
||||||
|
if (Math.abs(dx) > touchSlop || Math.abs(dy) > touchSlop) {
|
||||||
|
linkPressed = false
|
||||||
|
_lastTouchedLinks = null
|
||||||
|
parent?.requestDisallowInterceptTouchEvent(false)
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_UP -> {
|
||||||
|
if (linkPressed && _lastTouchedLinks != null) {
|
||||||
|
val dx = event.x - downX
|
||||||
|
val dy = event.y - downY
|
||||||
|
if (Math.abs(dx) <= touchSlop && Math.abs(dy) <= touchSlop) {
|
||||||
|
runBlocking {
|
||||||
|
for (link in _lastTouchedLinks!!) {
|
||||||
|
Logger.i(PlatformLinkMovementMethod.TAG) { "Link clicked '${link.url}'." }
|
||||||
|
val c = context
|
||||||
|
if (c is MainActivity) {
|
||||||
|
if (c.handleUrl(link.url)) continue
|
||||||
|
if (timestampRegex.matches(link.url)) {
|
||||||
|
val tokens = link.url.split(':')
|
||||||
|
var time_s = -1L
|
||||||
|
when (tokens.size) {
|
||||||
|
2 -> time_s = tokens[0].toLong() * 60 + tokens[1].toLong()
|
||||||
|
3 -> time_s = tokens[0].toLong() * 3600 +
|
||||||
|
tokens[1].toLong() * 60 +
|
||||||
|
tokens[2].toLong()
|
||||||
|
}
|
||||||
|
if (time_s != -1L) {
|
||||||
|
MediaControlReceiver.onSeekToReceived.emit(time_s * 1000)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(link.url)))
|
||||||
|
} else {
|
||||||
|
c.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(link.url)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_lastTouchedLinks = null
|
||||||
|
linkPressed = false
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
linkPressed = false
|
||||||
|
_lastTouchedLinks = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onTouchEvent(event)
|
MotionEvent.ACTION_CANCEL -> {
|
||||||
return false
|
linkPressed = false
|
||||||
|
_lastTouchedLinks = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onTouchEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
import com.futo.platformplayer.api.media.models.comments.IPlatformComment
|
import com.futo.platformplayer.api.media.models.comments.IPlatformComment
|
||||||
|
import com.futo.platformplayer.api.media.models.comments.LazyComment
|
||||||
import com.futo.platformplayer.api.media.models.comments.PolycentricPlatformComment
|
import com.futo.platformplayer.api.media.models.comments.PolycentricPlatformComment
|
||||||
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
||||||
import com.futo.platformplayer.api.media.structures.IAsyncPager
|
import com.futo.platformplayer.api.media.structures.IAsyncPager
|
||||||
@ -267,9 +268,13 @@ class CommentsList : ConstraintLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun replaceComment(c: PolycentricPlatformComment, newComment: PolycentricPlatformComment) {
|
fun replaceComment(c: PolycentricPlatformComment, newComment: PolycentricPlatformComment) {
|
||||||
val index = _comments.indexOf(c);
|
val index = _comments.indexOfFirst { it == c || (it is LazyComment && it.getUnderlyingComment() == c) };
|
||||||
|
if (index >= 0) {
|
||||||
_comments[index] = newComment;
|
_comments[index] = newComment;
|
||||||
_adapterComments.notifyItemChanged(_adapterComments.childToParentPosition(index));
|
_adapterComments.notifyItemChanged(_adapterComments.childToParentPosition(index));
|
||||||
|
} else {
|
||||||
|
Logger.w(TAG, "Parent comment not found")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -592,6 +592,11 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
|||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
fun setFullScreen(fullScreen: Boolean) {
|
fun setFullScreen(fullScreen: Boolean) {
|
||||||
|
// prevent fullscreen before the video has loaded to make sure we know whether it's a vertical or horizontal video
|
||||||
|
if(exoPlayer?.player?.videoSize?.height ?: 0 == 0 && fullScreen){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
updateRotateLock()
|
updateRotateLock()
|
||||||
|
|
||||||
if (isFullScreen == fullScreen) {
|
if (isFullScreen == fullScreen) {
|
||||||
|
@ -199,6 +199,7 @@
|
|||||||
<string name="previous">Previous</string>
|
<string name="previous">Previous</string>
|
||||||
<string name="next">Next</string>
|
<string name="next">Next</string>
|
||||||
<string name="comment">Comment</string>
|
<string name="comment">Comment</string>
|
||||||
|
<string name="not_empty_close">Comment is not empty, close anyway?</string>
|
||||||
<string name="str_import">Import</string>
|
<string name="str_import">Import</string>
|
||||||
<string name="my_playlist_name">My Playlist Name</string>
|
<string name="my_playlist_name">My Playlist Name</string>
|
||||||
<string name="do_you_want_to_import_this_store">Do you want to import this store?</string>
|
<string name="do_you_want_to_import_this_store">Do you want to import this store?</string>
|
||||||
|
@ -15,6 +15,7 @@ touch $DOCUMENT_ROOT/maintenance.file
|
|||||||
|
|
||||||
# Swap over the content
|
# Swap over the content
|
||||||
echo "Deploying content..."
|
echo "Deploying content..."
|
||||||
|
cp ./app/build/outputs/bundle/playstoreRelease/app-playstore-release.aab $DOCUMENT_ROOT/app-playstore-release.aab
|
||||||
aws s3 cp ./app/build/outputs/bundle/playstoreRelease/app-playstore-release.aab s3://artifacts-grayjay-app/app-playstore-release.aab
|
aws s3 cp ./app/build/outputs/bundle/playstoreRelease/app-playstore-release.aab s3://artifacts-grayjay-app/app-playstore-release.aab
|
||||||
|
|
||||||
# Notify Cloudflare to wipe the CDN cache
|
# Notify Cloudflare to wipe the CDN cache
|
||||||
|
Loading…
x
Reference in New Issue
Block a user