diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index 5cbfb889..916594e9 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -882,7 +882,10 @@ class Settings : FragmentedStorageFileJson() { @FormFieldWarning(R.string.bypass_rotation_prevention_warning) var bypassRotationPrevention: Boolean = false; - @FormField(R.string.enable_polycentric, FieldForm.TOGGLE, R.string.can_be_disabled_when_you_are_experiencing_issues, 1) + @FormField(R.string.playlist_delete_confirmation, FieldForm.TOGGLE, R.string.playlist_delete_confirmation_description, 2) + var playlistDeleteConfirmation: Boolean = true; + + @FormField(R.string.enable_polycentric, FieldForm.TOGGLE, R.string.can_be_disabled_when_you_are_experiencing_issues, 3) var polycentricEnabled: Boolean = true; } diff --git a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt index 7c975bf0..5e73638c 100644 --- a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt +++ b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt @@ -348,6 +348,13 @@ class UIDialogs { showDialog(context, R.drawable.ic_error, text, null, null, 0, cancelButtonAction, confirmButtonAction) } + fun showConfirmationDialog(context: Context, text: String, action: () -> Unit, cancelAction: (() -> Unit)? = null, doNotAskAgainAction: (() -> Unit)? = null) { + val confirmButtonAction = Action(context.getString(R.string.confirm), action, ActionStyle.PRIMARY) + val cancelButtonAction = Action(context.getString(R.string.cancel), cancelAction ?: {}, ActionStyle.ACCENT) + val doNotAskAgain = Action(context.getString(R.string.do_not_ask_again), doNotAskAgainAction ?: {}, ActionStyle.NONE) + showDialog(context, R.drawable.ic_error, text, null, null, 0, doNotAskAgain, cancelButtonAction, confirmButtonAction) + } + fun showUpdateAvailableDialog(context: Context, lastVersion: Int, hideExceptionButtons: Boolean = false) { val dialog = AutoUpdateDialog(context); registerDialogOpened(dialog); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt index 1530537b..233d9dc4 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt @@ -101,7 +101,7 @@ class VideoDetailFragment : MainFragment { val currentRequestedOrientation = a.requestedOrientation var currentOrientation = if (_currentOrientation == -1) currentRequestedOrientation else _currentOrientation if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT && !Settings.instance.playback.reversePortrait) - currentOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + currentOrientation = currentRequestedOrientation val isAutoRotate = Settings.instance.playback.isAutoRotate() val isFs = isFullscreen @@ -347,7 +347,7 @@ class VideoDetailFragment : MainFragment { return true } - if (state == State.MAXIMIZED && isFullscreen && !Settings.instance.playback.fullscreenPortrait && (_currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || _currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT)) { + if (state == State.MAXIMIZED && isFullscreen && !Settings.instance.playback.fullscreenPortrait && (_currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || (Settings.instance.playback.reversePortrait && _currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT))) { _viewDetail?.setFullscreen(false) return true } diff --git a/app/src/main/java/com/futo/platformplayer/mdns/MDNSListener.kt b/app/src/main/java/com/futo/platformplayer/mdns/MDNSListener.kt index 70268ec0..2b972d87 100644 --- a/app/src/main/java/com/futo/platformplayer/mdns/MDNSListener.kt +++ b/app/src/main/java/com/futo/platformplayer/mdns/MDNSListener.kt @@ -46,7 +46,10 @@ class MDNSListener { } fun start() { - if (_started) throw Exception("Already running.") + if (_started) { + Logger.i(TAG, "Already started.") + return + } _started = true _scope = CoroutineScope(Dispatchers.IO); diff --git a/app/src/main/java/com/futo/platformplayer/mdns/ServiceRecordAggregator.kt b/app/src/main/java/com/futo/platformplayer/mdns/ServiceRecordAggregator.kt index a5337dc5..bb4d1007 100644 --- a/app/src/main/java/com/futo/platformplayer/mdns/ServiceRecordAggregator.kt +++ b/app/src/main/java/com/futo/platformplayer/mdns/ServiceRecordAggregator.kt @@ -100,33 +100,34 @@ class ServiceRecordAggregator { Logger.i(TAG, "$builder")*/ val currentServices: MutableList + ptrRecords.forEach { record -> + val cachedPtrRecord = _cachedPtrRecords.getOrPut(record.first.name) { mutableListOf() } + val newPtrRecord = CachedDnsPtrRecord(Date(System.currentTimeMillis() + record.first.timeToLive.toLong() * 1000L), record.second.domainName) + cachedPtrRecord.replaceOrAdd(newPtrRecord) { it.target == record.second.domainName } + } + + aRecords.forEach { aRecord -> + val cachedARecord = _cachedAddressRecords.getOrPut(aRecord.first.name) { mutableListOf() } + val newARecord = CachedDnsAddressRecord(Date(System.currentTimeMillis() + aRecord.first.timeToLive.toLong() * 1000L), aRecord.second.address) + cachedARecord.replaceOrAdd(newARecord) { it.address == newARecord.address } + } + + aaaaRecords.forEach { aaaaRecord -> + val cachedAaaaRecord = _cachedAddressRecords.getOrPut(aaaaRecord.first.name) { mutableListOf() } + val newAaaaRecord = CachedDnsAddressRecord(Date(System.currentTimeMillis() + aaaaRecord.first.timeToLive.toLong() * 1000L), aaaaRecord.second.address) + cachedAaaaRecord.replaceOrAdd(newAaaaRecord) { it.address == newAaaaRecord.address } + } + + txtRecords.forEach { txtRecord -> + _cachedTxtRecords[txtRecord.first.name] = CachedDnsTxtRecord(Date(System.currentTimeMillis() + txtRecord.first.timeToLive.toLong() * 1000L), txtRecord.second.texts) + } + + srvRecords.forEach { srvRecord -> + _cachedSrvRecords[srvRecord.first.name] = CachedDnsSrvRecord(Date(System.currentTimeMillis() + srvRecord.first.timeToLive.toLong() * 1000L), srvRecord.second) + } + + //TODO: Maybe this can be debounced? synchronized(this._currentServices) { - ptrRecords.forEach { record -> - val cachedPtrRecord = _cachedPtrRecords.getOrPut(record.first.name) { mutableListOf() } - val newPtrRecord = CachedDnsPtrRecord(Date(System.currentTimeMillis() + record.first.timeToLive.toLong() * 1000L), record.second.domainName) - cachedPtrRecord.replaceOrAdd(newPtrRecord) { it.target == record.second.domainName } - } - - aRecords.forEach { aRecord -> - val cachedARecord = _cachedAddressRecords.getOrPut(aRecord.first.name) { mutableListOf() } - val newARecord = CachedDnsAddressRecord(Date(System.currentTimeMillis() + aRecord.first.timeToLive.toLong() * 1000L), aRecord.second.address) - cachedARecord.replaceOrAdd(newARecord) { it.address == newARecord.address } - } - - aaaaRecords.forEach { aaaaRecord -> - val cachedAaaaRecord = _cachedAddressRecords.getOrPut(aaaaRecord.first.name) { mutableListOf() } - val newAaaaRecord = CachedDnsAddressRecord(Date(System.currentTimeMillis() + aaaaRecord.first.timeToLive.toLong() * 1000L), aaaaRecord.second.address) - cachedAaaaRecord.replaceOrAdd(newAaaaRecord) { it.address == newAaaaRecord.address } - } - - txtRecords.forEach { txtRecord -> - _cachedTxtRecords[txtRecord.first.name] = CachedDnsTxtRecord(Date(System.currentTimeMillis() + txtRecord.first.timeToLive.toLong() * 1000L), txtRecord.second.texts) - } - - srvRecords.forEach { srvRecord -> - _cachedSrvRecords[srvRecord.first.name] = CachedDnsSrvRecord(Date(System.currentTimeMillis() + srvRecord.first.timeToLive.toLong() * 1000L), srvRecord.second) - } - currentServices = getCurrentServices() this._currentServices.clear() this._currentServices.addAll(currentServices) 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 a9c83202..c36a1942 100644 --- a/app/src/main/java/com/futo/platformplayer/others/PlatformLinkMovementMethod.kt +++ b/app/src/main/java/com/futo/platformplayer/others/PlatformLinkMovementMethod.kt @@ -12,6 +12,8 @@ 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 { @@ -23,6 +25,7 @@ class PlatformLinkMovementMethod : LinkMovementMethod { 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; diff --git a/app/src/main/java/com/futo/platformplayer/states/StateApp.kt b/app/src/main/java/com/futo/platformplayer/states/StateApp.kt index 52103822..b05d25c8 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateApp.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateApp.kt @@ -2,6 +2,8 @@ package com.futo.platformplayer.states import android.annotation.SuppressLint import android.app.Activity +import android.content.ClipData +import android.content.ClipboardManager import android.content.Context import android.content.Intent import android.content.IntentFilter @@ -20,9 +22,13 @@ import androidx.lifecycle.lifecycleScope import androidx.work.* import com.futo.platformplayer.* import com.futo.platformplayer.R +import com.futo.platformplayer.UIDialogs.Action +import com.futo.platformplayer.UIDialogs.ActionStyle +import com.futo.platformplayer.UIDialogs.Companion.showDialog import com.futo.platformplayer.activities.CaptchaActivity import com.futo.platformplayer.activities.IWithResultLauncher import com.futo.platformplayer.activities.MainActivity +import com.futo.platformplayer.activities.SettingsActivity import com.futo.platformplayer.api.media.platforms.js.DevJSClient import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.background.BackgroundWorker @@ -419,8 +425,17 @@ class StateApp { Logger.onLogSubmitted.subscribe { scopeOrNull?.launch(Dispatchers.Main) { try { - if (it != null) { - UIDialogs.toast("Uploaded $it", true); + if (!it.isNullOrEmpty()) { + (SettingsActivity.getActivity() ?: contextOrNull)?.let { c -> + val okButtonAction = Action(c.getString(R.string.ok), {}, ActionStyle.PRIMARY) + val copyButtonAction = Action(c.getString(R.string.copy), { + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("Log id", it) + clipboard.setPrimaryClip(clip) + }, ActionStyle.NONE) + + showDialog(c, R.drawable.ic_error, "Uploaded $it", null, null, 0, copyButtonAction, okButtonAction) + } } else { UIDialogs.toast("Failed to upload"); } diff --git a/app/src/main/java/com/futo/platformplayer/views/lists/VideoListEditorView.kt b/app/src/main/java/com/futo/platformplayer/views/lists/VideoListEditorView.kt index d412a40a..3bfce0be 100644 --- a/app/src/main/java/com/futo/platformplayer/views/lists/VideoListEditorView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/lists/VideoListEditorView.kt @@ -6,6 +6,8 @@ import android.widget.FrameLayout import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.futo.platformplayer.Settings +import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event2 @@ -53,14 +55,30 @@ class VideoListEditorView : FrameLayout { }; adapterVideos.onRemove.subscribe { v -> - synchronized(_videos) { - val index = _videos.indexOf(v); - if(index >= 0) { - _videos.removeAt(index); - onVideoRemoved.emit(v); + val executeDelete = { + synchronized(_videos) { + val index = _videos.indexOf(v); + if(index >= 0) { + _videos.removeAt(index); + onVideoRemoved.emit(v); + } + adapterVideos.notifyItemRemoved(index); } - adapterVideos.notifyItemRemoved(index); } + + if (Settings.instance.other.playlistDeleteConfirmation) { + UIDialogs.showConfirmationDialog(context, "Please confirm to delete", action = { + executeDelete() + }, cancelAction = { + + }, doNotAskAgainAction = { + Settings.instance.other.playlistDeleteConfirmation = false + Settings.instance.save() + }) + } else { + executeDelete() + } + }; adapterVideos.onClick.subscribe(onVideoClicked::emit); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e2588dde..886dc80a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,6 +82,7 @@ Yes No Confirm + Don\'t ask again Are you sure you want to delete this playlist? Are you sure you want to delete this subscription? Removing this source will result in some of your subscriptions not being resolved. @@ -418,6 +419,8 @@ Payment Payment Status Bypass Rotation Prevention + Playlist Delete Confirmation + Show confirmation dialog when deleting media from a playlist Enable Polycentric Can be disabled when you are experiencing issues Allows for rotation on non-video views.\nWARNING: Not designed for it diff --git a/app/src/unstable/AndroidManifest.xml b/app/src/unstable/AndroidManifest.xml index a30bb7af..7c47d7bd 100644 --- a/app/src/unstable/AndroidManifest.xml +++ b/app/src/unstable/AndroidManifest.xml @@ -38,6 +38,8 @@ + + @@ -67,6 +69,8 @@ + +