From 81765ecafced6280da2c3cec3405063b1a6ede76 Mon Sep 17 00:00:00 2001 From: Koen Date: Fri, 6 Dec 2024 10:54:36 +0000 Subject: [PATCH 01/13] Update deploy-playstore.sh --- deploy-playstore.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy-playstore.sh b/deploy-playstore.sh index 5d31fcfb..498abfa3 100644 --- a/deploy-playstore.sh +++ b/deploy-playstore.sh @@ -15,6 +15,7 @@ touch $DOCUMENT_ROOT/maintenance.file # Swap over the 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 # Notify Cloudflare to wipe the CDN cache From b5722dba1a71a2ade915a841f5402dee21a4f845 Mon Sep 17 00:00:00 2001 From: Koen J Date: Fri, 6 Dec 2024 14:54:57 +0100 Subject: [PATCH 02/13] MainActivity should be singleInstance. --- app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6c69fb32..b411305c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,7 +52,7 @@ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="true" android:theme="@style/Theme.FutoVideo.NoActionBar" - android:launchMode="singleTask" + android:launchMode="singleInstance" android:resizeableActivity="true" android:supportsPictureInPicture="true"> From 7c9e9d5f524bed1895e18b4fc4a7bd84a6afddb4 Mon Sep 17 00:00:00 2001 From: Koen J Date: Fri, 6 Dec 2024 17:49:18 +0100 Subject: [PATCH 03/13] Should not crash app when StateSync fails to bind. --- .../futo/platformplayer/states/StateSync.kt | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/states/StateSync.kt b/app/src/main/java/com/futo/platformplayer/states/StateSync.kt index 89c3b6c5..6d47bd8f 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateSync.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateSync.kt @@ -112,18 +112,23 @@ class StateSync { Logger.i(TAG, "Sync key pair initialized (public key = ${publicKey})") _thread = Thread { - val serverSocket = ServerSocket(PORT) - _serverSocket = serverSocket + try { + val serverSocket = ServerSocket(PORT) + _serverSocket = serverSocket - Log.i(TAG, "Running on port ${PORT} (TCP)") + Log.i(TAG, "Running on port ${PORT} (TCP)") - while (_started) { - val socket = serverSocket.accept() - val session = createSocketSession(socket, true) { session, socketSession -> + while (_started) { + val socket = serverSocket.accept() + val session = createSocketSession(socket, true) { session, socketSession -> + } + + 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() } From 8f30a45fa88b227d232616469108589813043e85 Mon Sep 17 00:00:00 2001 From: Kai Date: Fri, 6 Dec 2024 11:13:29 -0600 Subject: [PATCH 04/13] enable creator filter clear --- .../mainactivity/main/CreatorsFragment.kt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorsFragment.kt index b62098a7..6efc7a3d 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorsFragment.kt @@ -8,6 +8,7 @@ import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.EditText import android.widget.FrameLayout +import android.widget.ImageButton import android.widget.Spinner import androidx.core.widget.addTextChangedListener import androidx.recyclerview.widget.LinearLayoutManager @@ -25,11 +26,20 @@ class CreatorsFragment : MainFragment() { private var _overlayContainer: FrameLayout? = null; private var _containerSearch: FrameLayout? = null; private var _editSearch: EditText? = null; + private var _buttonClearSearch: ImageButton? = null override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val view = inflater.inflate(R.layout.fragment_creators, container, false); _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)); adapter.onClick.subscribe { platformUser -> navigate(platformUser) }; @@ -51,7 +61,12 @@ class CreatorsFragment : MainFragment() { _spinnerSortBy = spinnerSortBy; _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(R.id.recycler_subscriptions); From 86b6938911b2ea23e649fdfbcd98e5effdc1dcc5 Mon Sep 17 00:00:00 2001 From: Kai Date: Sat, 7 Dec 2024 11:09:48 -0600 Subject: [PATCH 05/13] added video detail check --- .../fragment/mainactivity/main/VideoDetailView.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt index 8ec9c29b..b28cb308 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt @@ -2382,6 +2382,11 @@ class VideoDetailView : ConstraintLayout { var videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width var videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height + if (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0){ + videoSourceWidth = this.video?.video?.videoSources?.get(0)?.width + videoSourceHeight = this.video?.video?.videoSources?.get(0)?.height + } + return if (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0){ null } else{ From 7557e6f6ba50d51bfc42ac83f8b2d8bb50e9fe54 Mon Sep 17 00:00:00 2001 From: Kai Date: Sat, 7 Dec 2024 11:32:28 -0600 Subject: [PATCH 06/13] prevent going full screen before the video has loaded --- .../fragment/mainactivity/main/VideoDetailView.kt | 9 ++------- .../futo/platformplayer/views/video/FutoVideoPlayer.kt | 5 +++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt index b28cb308..7a3c7aa2 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt @@ -2379,13 +2379,8 @@ class VideoDetailView : ConstraintLayout { } fun isLandscapeVideo(): Boolean? { - var videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width - var videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height - - if (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0){ - videoSourceWidth = this.video?.video?.videoSources?.get(0)?.width - videoSourceHeight = this.video?.video?.videoSources?.get(0)?.height - } + val videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width + val videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height return if (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0){ null diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt index 8767031e..740740fa 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt @@ -592,6 +592,11 @@ class FutoVideoPlayer : FutoVideoPlayerBase { @OptIn(UnstableApi::class) 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() if (isFullScreen == fullScreen) { From aa570ac29da338c50ba837a09aa49c7b4128606c Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 10 Dec 2024 12:47:18 +0100 Subject: [PATCH 07/13] Updated submodules. --- app/src/stable/assets/sources/odysee | 2 +- app/src/stable/assets/sources/rumble | 2 +- app/src/stable/assets/sources/youtube | 2 +- app/src/unstable/assets/sources/odysee | 2 +- app/src/unstable/assets/sources/rumble | 2 +- app/src/unstable/assets/sources/youtube | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/stable/assets/sources/odysee b/app/src/stable/assets/sources/odysee index 77b90125..8ddb2e2f 160000 --- a/app/src/stable/assets/sources/odysee +++ b/app/src/stable/assets/sources/odysee @@ -1 +1 @@ -Subproject commit 77b9012590ef98afce8659b154028aff85834eb2 +Subproject commit 8ddb2e2f15bf907bd8523aac4326b92b8e3b0e8e diff --git a/app/src/stable/assets/sources/rumble b/app/src/stable/assets/sources/rumble index cbfe372b..6811ff4b 160000 --- a/app/src/stable/assets/sources/rumble +++ b/app/src/stable/assets/sources/rumble @@ -1 +1 @@ -Subproject commit cbfe372bcc7bf9c339809c30291b85c53bfa2f7d +Subproject commit 6811ff4b412cfb94ff74c3b2b88f7d87b76e8902 diff --git a/app/src/stable/assets/sources/youtube b/app/src/stable/assets/sources/youtube index e1fa4980..59d694b6 160000 --- a/app/src/stable/assets/sources/youtube +++ b/app/src/stable/assets/sources/youtube @@ -1 +1 @@ -Subproject commit e1fa498059f481a81639fef3b62adc2ec05a95dd +Subproject commit 59d694b619e9a60d1c2d7315c87c51672383ffc5 diff --git a/app/src/unstable/assets/sources/odysee b/app/src/unstable/assets/sources/odysee index 77b90125..8ddb2e2f 160000 --- a/app/src/unstable/assets/sources/odysee +++ b/app/src/unstable/assets/sources/odysee @@ -1 +1 @@ -Subproject commit 77b9012590ef98afce8659b154028aff85834eb2 +Subproject commit 8ddb2e2f15bf907bd8523aac4326b92b8e3b0e8e diff --git a/app/src/unstable/assets/sources/rumble b/app/src/unstable/assets/sources/rumble index cbfe372b..6811ff4b 160000 --- a/app/src/unstable/assets/sources/rumble +++ b/app/src/unstable/assets/sources/rumble @@ -1 +1 @@ -Subproject commit cbfe372bcc7bf9c339809c30291b85c53bfa2f7d +Subproject commit 6811ff4b412cfb94ff74c3b2b88f7d87b76e8902 diff --git a/app/src/unstable/assets/sources/youtube b/app/src/unstable/assets/sources/youtube index e1fa4980..59d694b6 160000 --- a/app/src/unstable/assets/sources/youtube +++ b/app/src/unstable/assets/sources/youtube @@ -1 +1 @@ -Subproject commit e1fa498059f481a81639fef3b62adc2ec05a95dd +Subproject commit 59d694b619e9a60d1c2d7315c87c51672383ffc5 From 90a1cd828047ba2e792c9e335ed836278995b362 Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 10 Dec 2024 13:07:03 +0100 Subject: [PATCH 08/13] Placing reply comments works again. --- .../platformplayer/views/segments/CommentsList.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/views/segments/CommentsList.kt b/app/src/main/java/com/futo/platformplayer/views/segments/CommentsList.kt index 2617418b..965d3014 100644 --- a/app/src/main/java/com/futo/platformplayer/views/segments/CommentsList.kt +++ b/app/src/main/java/com/futo/platformplayer/views/segments/CommentsList.kt @@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView import com.futo.platformplayer.R import com.futo.platformplayer.UIDialogs 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.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.structures.IAsyncPager @@ -267,9 +268,13 @@ class CommentsList : ConstraintLayout { } fun replaceComment(c: PolycentricPlatformComment, newComment: PolycentricPlatformComment) { - val index = _comments.indexOf(c); - _comments[index] = newComment; - _adapterComments.notifyItemChanged(_adapterComments.childToParentPosition(index)); + val index = _comments.indexOfFirst { it == c || (it is LazyComment && it.getUnderlyingComment() == c) }; + if (index >= 0) { + _comments[index] = newComment; + _adapterComments.notifyItemChanged(_adapterComments.childToParentPosition(index)); + } else { + Logger.w(TAG, "Parent comment not found") + } } companion object { From 752fc8787d409c367c782a963def5f4b27f6d95b Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 10 Dec 2024 13:29:09 +0100 Subject: [PATCH 09/13] Fixed link scrolling behaviour. --- .../others/PlatformLinkMovementMethod.kt | 127 +++++++++++------ .../views/behavior/NonScrollingTextView.kt | 134 ++++++++++++------ 2 files changed, 170 insertions(+), 91 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/others/PlatformLinkMovementMethod.kt b/app/src/main/java/com/futo/platformplayer/others/PlatformLinkMovementMethod.kt index c36a1942..9faf9433 100644 --- a/app/src/main/java/com/futo/platformplayer/others/PlatformLinkMovementMethod.kt +++ b/app/src/main/java/com/futo/platformplayer/others/PlatformLinkMovementMethod.kt @@ -12,70 +12,109 @@ import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.receivers.MediaControlReceiver import com.futo.platformplayer.timestampRegex -import com.futo.platformplayer.views.behavior.NonScrollingTextView -import com.futo.platformplayer.views.behavior.NonScrollingTextView.Companion import kotlinx.coroutines.runBlocking -class PlatformLinkMovementMethod : LinkMovementMethod { - private val _context: Context; +class PlatformLinkMovementMethod(private val _context: Context) : LinkMovementMethod() { - constructor(context: Context) : super() { - _context = context; - } + private var pressedLinks: Array? = null + 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 { - val action = event.action; - 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 action = event.actionMasked - 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()) { + pressedLinks = links + linkPressed = true + downX = event.x + downY = event.y + widget.parent?.requestDisallowInterceptTouchEvent(true) + return true + } else { + linkPressed = false + pressedLinks = null + } + } - if (links.isNotEmpty()) { - runBlocking { - for (link in links) { - Logger.i(TAG) { "Link clicked '${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 + pressedLinks = null + widget.parent?.requestDisallowInterceptTouchEvent(false) + return false + } + return true + } + } - if (_context is MainActivity) { - if (_context.handleUrl(link.url)) { - continue; - } + MotionEvent.ACTION_UP -> { + if (linkPressed && pressedLinks != null) { + val dx = event.x - downX + val dy = event.y - downY + if (Math.abs(dx) <= touchSlop && Math.abs(dy) <= touchSlop) { + runBlocking { + for (link in pressedLinks!!) { + Logger.i(TAG) { "Link clicked '${link.url}'." } - if (timestampRegex.matches(link.url)) { - val tokens = link.url.split(':'); + 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() + } - 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; + if (time_s != -1L) { + MediaControlReceiver.onSeekToReceived.emit(time_s * 1000) + continue + } + } } + _context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(link.url))) } } - - - _context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(link.url))); + pressedLinks = null + linkPressed = false + return true + } else { + pressedLinks = null + linkPressed = false } } + } - return true; + MotionEvent.ACTION_CANCEL -> { + linkPressed = false + pressedLinks = null } } - return super.onTouchEvent(widget, buffer, event); + return super.onTouchEvent(widget, buffer, event) + } + + private fun findLinksAtTouchPosition(widget: TextView, buffer: Spannable, event: MotionEvent): Array { + 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 { - val TAG = "PlatformLinkMovementMethod"; + const val TAG = "PlatformLinkMovementMethod" } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/futo/platformplayer/views/behavior/NonScrollingTextView.kt b/app/src/main/java/com/futo/platformplayer/views/behavior/NonScrollingTextView.kt index cb5e8638..402940ad 100644 --- a/app/src/main/java/com/futo/platformplayer/views/behavior/NonScrollingTextView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/behavior/NonScrollingTextView.kt @@ -16,73 +16,113 @@ import com.futo.platformplayer.timestampRegex import kotlinx.coroutines.runBlocking class NonScrollingTextView : androidx.appcompat.widget.AppCompatTextView { + private var _lastTouchedLinks: Array? = 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, attrs: AttributeSet) : super(context, attrs) {} constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {} override fun scrollTo(x: Int, y: Int) { - //do nothing + // do nothing } override fun onTouchEvent(event: MotionEvent?): Boolean { - val action = event?.action - Logger.i(TAG, "onTouchEvent (action = $action)"); + val action = event?.actionMasked + if (event == null) return super.onTouchEvent(event) - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { - val x = event.x.toInt() - val y = event.y.toInt() + when (action) { + MotionEvent.ACTION_DOWN -> { + val x = event.x.toInt() + val y = event.y.toInt() - val layout: Layout? = this.layout - if (layout != null) { - val line = layout.getLineForVertical(y) - val offset = layout.getOffsetForHorizontal(line, x.toFloat()) - - val text = this.text - if (text is Spannable) { + val layout: Layout? = this.layout + if (layout != null && this.text is Spannable) { + val offset = layout.getOffsetForHorizontal(layout.getLineForVertical(y), x.toFloat()) + val text = this.text as Spannable val links = text.getSpans(offset, offset, URLSpan::class.java) if (links.isNotEmpty()) { - runBlocking { - for (link in links) { - 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; - 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))); - } - } - } - + parent?.requestDisallowInterceptTouchEvent(true) + _lastTouchedLinks = links + downX = event.x + downY = event.y + linkPressed = true return true + } else { + linkPressed = false + _lastTouchedLinks = null } } } + + 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 + } + } + + 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 + } + } + } + + MotionEvent.ACTION_CANCEL -> { + linkPressed = false + _lastTouchedLinks = null + } } - super.onTouchEvent(event) - return false + return super.onTouchEvent(event) } companion object { private const val TAG = "NonScrollingTextView" } -} \ No newline at end of file +} From 6a8b9f06c2992739815a746235488bb89c18ebd8 Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 10 Dec 2024 13:52:02 +0100 Subject: [PATCH 10/13] Comment close now requires confirmation. --- .../platformplayer/dialogs/CommentDialog.kt | 35 +++++++++++++++++-- app/src/main/res/values/strings.xml | 1 + 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt b/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt index 580bd508..d142fb9c 100644 --- a/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt +++ b/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt @@ -6,6 +6,7 @@ import android.graphics.Color import android.os.Bundle import android.text.Editable import android.text.TextWatcher +import android.view.KeyEvent import android.view.LayoutInflater import android.view.WindowManager import android.view.inputmethod.InputMethodManager @@ -57,12 +58,21 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol _editComment = findViewById(R.id.edit_comment); _textCharacterCount = findViewById(R.id.character_count); _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 { override fun afterTextChanged(s: Editable?) = Unit override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - _textCharacterCount.text = count.toString(); + _textCharacterCount.text = (s?.length ?: 0).toString(); if (count > PolycentricPlatformComment.MAX_COMMENT_SIZE) { _textCharacterCount.setTextColor(Color.RED); @@ -79,10 +89,13 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol _inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager; _buttonCancel.setOnClickListener { - clearFocus(); - dismiss(); + handleCloseAttempt() }; + setOnCancelListener { + handleCloseAttempt() + } + _buttonCreate.setOnClickListener { clearFocus(); @@ -134,6 +147,22 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol 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() { _editComment.requestFocus(); window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ddf228b7..33ca1593 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -199,6 +199,7 @@ Previous Next Comment + Comment is not empty, close anyway? Import My Playlist Name Do you want to import this store? From e1882f19e81a476f7b599507d7a3f9f34cd75815 Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 10 Dec 2024 13:52:02 +0100 Subject: [PATCH 11/13] Comment close now requires confirmation. Fixed comment character counter. --- .../platformplayer/dialogs/CommentDialog.kt | 35 +++++++++++++++++-- app/src/main/res/values/strings.xml | 1 + 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt b/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt index 580bd508..d142fb9c 100644 --- a/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt +++ b/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt @@ -6,6 +6,7 @@ import android.graphics.Color import android.os.Bundle import android.text.Editable import android.text.TextWatcher +import android.view.KeyEvent import android.view.LayoutInflater import android.view.WindowManager import android.view.inputmethod.InputMethodManager @@ -57,12 +58,21 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol _editComment = findViewById(R.id.edit_comment); _textCharacterCount = findViewById(R.id.character_count); _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 { override fun afterTextChanged(s: Editable?) = Unit override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - _textCharacterCount.text = count.toString(); + _textCharacterCount.text = (s?.length ?: 0).toString(); if (count > PolycentricPlatformComment.MAX_COMMENT_SIZE) { _textCharacterCount.setTextColor(Color.RED); @@ -79,10 +89,13 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol _inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager; _buttonCancel.setOnClickListener { - clearFocus(); - dismiss(); + handleCloseAttempt() }; + setOnCancelListener { + handleCloseAttempt() + } + _buttonCreate.setOnClickListener { clearFocus(); @@ -134,6 +147,22 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol 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() { _editComment.requestFocus(); window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ddf228b7..33ca1593 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -199,6 +199,7 @@ Previous Next Comment + Comment is not empty, close anyway? Import My Playlist Name Do you want to import this store? From 11f5f0dfe10cbcf300d70713c91372714ae91522 Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 10 Dec 2024 13:54:02 +0100 Subject: [PATCH 12/13] Fixed comment character counter. --- .../java/com/futo/platformplayer/dialogs/CommentDialog.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt b/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt index d142fb9c..9d0282f0 100644 --- a/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt +++ b/app/src/main/java/com/futo/platformplayer/dialogs/CommentDialog.kt @@ -71,8 +71,9 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol _editComment.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) = Unit override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - _textCharacterCount.text = (s?.length ?: 0).toString(); + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, c: Int) { + val count = s?.length ?: 0; + _textCharacterCount.text = count.toString(); if (count > PolycentricPlatformComment.MAX_COMMENT_SIZE) { _textCharacterCount.setTextColor(Color.RED); From ce66937429ee24d9b15a9fe69089be7e4891d892 Mon Sep 17 00:00:00 2001 From: Koen J Date: Tue, 10 Dec 2024 14:01:34 +0100 Subject: [PATCH 13/13] Offline playback toast now doesn't show more than once every 5 seconds. --- .../fragment/mainactivity/main/VideoDetailView.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt index 8ec9c29b..c90c1da6 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt @@ -1799,8 +1799,13 @@ class VideoDetailView : ConstraintLayout { private fun onSourceChanged(videoSource: IVideoSource?, audioSource: IAudioSource?, resume: Boolean){ Logger.i(TAG, "onSourceChanged(videoSource=$videoSource, audioSource=$audioSource, resume=$resume)") - if((videoSource == null || videoSource is LocalVideoSource) && (audioSource == null || audioSource is LocalAudioSource)) - UIDialogs.toast(context, context.getString(R.string.offline_playback), false); + 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); + _lastOfflinePlaybackToastTime = System.currentTimeMillis() + } + } //If LiveStream, set to end if(videoSource is IDashManifestSource || videoSource is IHLSManifestSource) { if (video?.isLive == true) { @@ -3030,8 +3035,6 @@ class VideoDetailView : ConstraintLayout { const val TAG_MORE = "MORE"; private val _buttonPinStore = FragmentedStorage.get("videoPinnedButtons"); - - - + private var _lastOfflinePlaybackToastTime: Long = 0 } } \ No newline at end of file