mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-05-24 18:42:10 +02:00
Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay
This commit is contained in:
commit
f4cb1719e0
@ -37,6 +37,7 @@ import com.futo.platformplayer.logging.Logger
|
|||||||
import com.futo.platformplayer.states.StateApp
|
import com.futo.platformplayer.states.StateApp
|
||||||
import com.futo.platformplayer.states.StateBackup
|
import com.futo.platformplayer.states.StateBackup
|
||||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
import com.futo.platformplayer.stores.v2.ManagedStore
|
||||||
|
import com.futo.platformplayer.views.ToastView
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -398,13 +399,28 @@ class UIDialogs {
|
|||||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
|
||||||
try {
|
try {
|
||||||
StateApp.withContext {
|
StateApp.withContext {
|
||||||
Toast.makeText(it, text, if (long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT).show();
|
toast(it, text, long);
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.e(TAG, "Failed to show toast.", e);
|
Logger.e(TAG, "Failed to show toast.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fun appToast(text: String, long: Boolean = false) {
|
||||||
|
appToast(ToastView.Toast(text, long))
|
||||||
|
}
|
||||||
|
fun appToastError(text: String, long: Boolean) {
|
||||||
|
StateApp.withContext {
|
||||||
|
appToast(ToastView.Toast(text, long, it.getColor(R.color.pastel_red)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fun appToast(toast: ToastView.Toast) {
|
||||||
|
StateApp.withContext {
|
||||||
|
if(it is MainActivity) {
|
||||||
|
it.showAppToast(toast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun showClickableToast(context: Context, text: String, onClick: () -> Unit, isLongDuration: Boolean = false) {
|
fun showClickableToast(context: Context, text: String, onClick: () -> Unit, isLongDuration: Boolean = false) {
|
||||||
//TODO: Is not actually clickable...
|
//TODO: Is not actually clickable...
|
||||||
|
@ -343,7 +343,7 @@ class UISlideOverlays {
|
|||||||
videoSources.filter { it is IVideoUrlSource && it.isDownloadable() }.asIterable(),
|
videoSources.filter { it is IVideoUrlSource && it.isDownloadable() }.asIterable(),
|
||||||
Settings.instance.downloads.getDefaultVideoQualityPixels(),
|
Settings.instance.downloads.getDefaultVideoQualityPixels(),
|
||||||
FutoVideoPlayerBase.PREFERED_VIDEO_CONTAINERS
|
FutoVideoPlayerBase.PREFERED_VIDEO_CONTAINERS
|
||||||
) as IVideoUrlSource;
|
) as IVideoUrlSource?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioSources != null) {
|
if (audioSources != null) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.futo.platformplayer.activities
|
package com.futo.platformplayer.activities
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
@ -24,6 +23,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentContainerView
|
import androidx.fragment.app.FragmentContainerView
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@ -45,6 +45,7 @@ import com.futo.platformplayer.states.*
|
|||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
import com.futo.platformplayer.stores.SubscriptionStorage
|
import com.futo.platformplayer.stores.SubscriptionStorage
|
||||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
import com.futo.platformplayer.stores.v2.ManagedStore
|
||||||
|
import com.futo.platformplayer.views.ToastView
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
import com.google.zxing.integration.android.IntentIntegrator
|
import com.google.zxing.integration.android.IntentIntegrator
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
@ -54,6 +55,7 @@ import java.io.PrintWriter
|
|||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity, IWithResultLauncher {
|
class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
|
|
||||||
@ -65,6 +67,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
lateinit var rootView : MotionLayout;
|
lateinit var rootView : MotionLayout;
|
||||||
|
|
||||||
private lateinit var _overlayContainer: FrameLayout;
|
private lateinit var _overlayContainer: FrameLayout;
|
||||||
|
private lateinit var _toastView: ToastView;
|
||||||
|
|
||||||
//Segment Containers
|
//Segment Containers
|
||||||
private lateinit var _fragContainerTopBar: FragmentContainerView;
|
private lateinit var _fragContainerTopBar: FragmentContainerView;
|
||||||
@ -207,7 +210,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
_fragContainerVideoDetail = findViewById(R.id.fragment_overlay);
|
_fragContainerVideoDetail = findViewById(R.id.fragment_overlay);
|
||||||
_fragContainerOverlay = findViewById(R.id.fragment_overlay_container);
|
_fragContainerOverlay = findViewById(R.id.fragment_overlay_container);
|
||||||
_overlayContainer = findViewById(R.id.overlay_container);
|
_overlayContainer = findViewById(R.id.overlay_container);
|
||||||
//_overlayContainer.visibility = View.GONE;
|
_toastView = findViewById(R.id.toast_view);
|
||||||
|
|
||||||
//Initialize fragments
|
//Initialize fragments
|
||||||
|
|
||||||
@ -478,21 +481,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_isVisible = true;
|
_isVisible = true;
|
||||||
val videoToOpen = StateSaved.instance.videoToOpen;
|
|
||||||
|
|
||||||
if (_wasStopped) {
|
|
||||||
_wasStopped = false;
|
|
||||||
|
|
||||||
if (videoToOpen != null && _fragVideoDetail.state == VideoDetailFragment.State.CLOSED) {
|
|
||||||
Logger.i(TAG, "onResume videoToOpen=$videoToOpen");
|
|
||||||
if (StatePlatform.instance.hasEnabledVideoClient(videoToOpen.url)) {
|
|
||||||
navigate(_fragVideoDetail, UrlVideoWithTime(videoToOpen.url, videoToOpen.timeSeconds, false));
|
|
||||||
_fragVideoDetail.maximizeVideoDetail(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
StateSaved.instance.setVideoToOpenNonBlocking(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
@ -864,7 +852,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
_orientationManager.disable();
|
_orientationManager.disable();
|
||||||
|
|
||||||
StateApp.instance.mainAppDestroyed(this);
|
StateApp.instance.mainAppDestroyed(this);
|
||||||
StateSaved.instance.setVideoToOpenBlocking(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T> isFragmentActive(): Boolean {
|
inline fun <reified T> isFragmentActive(): Boolean {
|
||||||
@ -1052,6 +1039,43 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _toastQueue = ConcurrentLinkedQueue<ToastView.Toast>();
|
||||||
|
private var _toastJob: Job? = null;
|
||||||
|
fun showAppToast(toast: ToastView.Toast) {
|
||||||
|
synchronized(_toastQueue) {
|
||||||
|
_toastQueue.add(toast);
|
||||||
|
if(_toastJob?.isActive != true)
|
||||||
|
_toastJob = lifecycleScope.launch(Dispatchers.Default) {
|
||||||
|
launchAppToastJob();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private suspend fun launchAppToastJob() {
|
||||||
|
Logger.i(TAG, "Starting appToast loop");
|
||||||
|
while(!_toastQueue.isEmpty()) {
|
||||||
|
val toast = _toastQueue.poll() ?: continue;
|
||||||
|
Logger.i(TAG, "Showing next toast (${toast.msg})");
|
||||||
|
|
||||||
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
if (!_toastView.isVisible) {
|
||||||
|
Logger.i(TAG, "First showing toast");
|
||||||
|
_toastView.setToast(toast);
|
||||||
|
_toastView.show(true);
|
||||||
|
} else {
|
||||||
|
_toastView.setToastAnimated(toast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(toast.long)
|
||||||
|
delay(5000);
|
||||||
|
else
|
||||||
|
delay(3000);
|
||||||
|
}
|
||||||
|
Logger.i(TAG, "Ending appToast loop");
|
||||||
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
_toastView.hide(true) {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//TODO: Only calls last handler due to missing request codes on ActivityResultLaunchers.
|
//TODO: Only calls last handler due to missing request codes on ActivityResultLaunchers.
|
||||||
|
@ -10,6 +10,7 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.futo.platformplayer.*
|
import com.futo.platformplayer.*
|
||||||
import com.futo.platformplayer.activities.MainActivity
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
|
import com.futo.platformplayer.api.media.IPlatformClient
|
||||||
import com.futo.platformplayer.api.media.models.contents.ContentType
|
import com.futo.platformplayer.api.media.models.contents.ContentType
|
||||||
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
||||||
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
||||||
@ -427,7 +428,7 @@ class SubscriptionsFeedFragment : MainFragment() {
|
|||||||
context?.let {
|
context?.let {
|
||||||
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
||||||
try {
|
try {
|
||||||
if (exs.size <= 8) {
|
if (exs.size <= 3) {
|
||||||
for (ex in exs) {
|
for (ex in exs) {
|
||||||
var toShow = ex;
|
var toShow = ex;
|
||||||
var channel: String? = null;
|
var channel: String? = null;
|
||||||
@ -437,12 +438,11 @@ class SubscriptionsFeedFragment : MainFragment() {
|
|||||||
}
|
}
|
||||||
Logger.e(TAG, "Channel [${channel}] failed", ex);
|
Logger.e(TAG, "Channel [${channel}] failed", ex);
|
||||||
if (toShow is PluginException)
|
if (toShow is PluginException)
|
||||||
UIDialogs.toast(
|
UIDialogs.appToast(
|
||||||
it,
|
|
||||||
context.getString(R.string.plugin_pluginname_failed_message).replace("{pluginName}", toShow.config.name).replace("{message}", toShow.message ?: "")
|
context.getString(R.string.plugin_pluginname_failed_message).replace("{pluginName}", toShow.config.name).replace("{message}", toShow.message ?: "")
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
UIDialogs.toast(it, ex.message ?: "");
|
UIDialogs.appToast(ex.message ?: "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -453,7 +453,7 @@ class SubscriptionsFeedFragment : MainFragment() {
|
|||||||
.map { it!! }
|
.map { it!! }
|
||||||
.toList();
|
.toList();
|
||||||
for(distinctPluginFail in failedPlugins)
|
for(distinctPluginFail in failedPlugins)
|
||||||
UIDialogs.toast(it, context.getString(R.string.plugin_pluginname_failed_message).replace("{pluginName}", distinctPluginFail.config.name).replace("{message}", distinctPluginFail.message ?: ""));
|
UIDialogs.appToast(context.getString(R.string.plugin_pluginname_failed_message).replace("{pluginName}", distinctPluginFail.config.name).replace("{message}", distinctPluginFail.message ?: ""));
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.e(TAG, "Failed to handle exceptions", e)
|
Logger.e(TAG, "Failed to handle exceptions", e)
|
||||||
|
@ -25,8 +25,6 @@ import com.futo.platformplayer.logging.Logger
|
|||||||
import com.futo.platformplayer.models.PlatformVideoWithTime
|
import com.futo.platformplayer.models.PlatformVideoWithTime
|
||||||
import com.futo.platformplayer.models.UrlVideoWithTime
|
import com.futo.platformplayer.models.UrlVideoWithTime
|
||||||
import com.futo.platformplayer.states.StatePlayer
|
import com.futo.platformplayer.states.StatePlayer
|
||||||
import com.futo.platformplayer.states.StateSaved
|
|
||||||
import com.futo.platformplayer.states.VideoToOpen
|
|
||||||
import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout
|
import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout
|
||||||
|
|
||||||
class VideoDetailFragment : MainFragment {
|
class VideoDetailFragment : MainFragment {
|
||||||
@ -372,11 +370,6 @@ class VideoDetailFragment : MainFragment {
|
|||||||
|
|
||||||
Logger.v(TAG, "shouldStop: $shouldStop");
|
Logger.v(TAG, "shouldStop: $shouldStop");
|
||||||
if(shouldStop) {
|
if(shouldStop) {
|
||||||
_viewDetail?.let {
|
|
||||||
val v = it.video ?: return@let;
|
|
||||||
StateSaved.instance.setVideoToOpenBlocking(VideoToOpen(v.url, (it.lastPositionMilliseconds / 1000.0f).toLong()));
|
|
||||||
}
|
|
||||||
|
|
||||||
_viewDetail?.onStop();
|
_viewDetail?.onStop();
|
||||||
StateCasting.instance.onStop();
|
StateCasting.instance.onStop();
|
||||||
Logger.v(TAG, "called onStop() shouldStop: $shouldStop");
|
Logger.v(TAG, "called onStop() shouldStop: $shouldStop");
|
||||||
|
@ -149,6 +149,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import userpackage.Protocol
|
import userpackage.Protocol
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
@ -853,7 +855,11 @@ class VideoDetailView : ConstraintLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val _historyIndexLock = Mutex(false);
|
||||||
suspend fun getHistoryIndex(video: IPlatformVideo): DBHistory.Index = withContext(Dispatchers.IO){
|
suspend fun getHistoryIndex(video: IPlatformVideo): DBHistory.Index = withContext(Dispatchers.IO){
|
||||||
|
_historyIndexLock.withLock {
|
||||||
val current = _historyIndex;
|
val current = _historyIndex;
|
||||||
if(current == null || current.url != video.url) {
|
if(current == null || current.url != video.url) {
|
||||||
val index = StateHistory.instance.getHistoryByVideo(video, true)!!;
|
val index = StateHistory.instance.getHistoryByVideo(video, true)!!;
|
||||||
@ -862,6 +868,7 @@ class VideoDetailView : ConstraintLayout {
|
|||||||
}
|
}
|
||||||
return@withContext current;
|
return@withContext current;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Lifecycle
|
//Lifecycle
|
||||||
@ -1121,7 +1128,7 @@ class VideoDetailView : ConstraintLayout {
|
|||||||
|
|
||||||
switchContentView(_container_content_main);
|
switchContentView(_container_content_main);
|
||||||
}
|
}
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
//@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun setVideoDetails(videoDetail: IPlatformVideoDetails, newVideo: Boolean = false) {
|
fun setVideoDetails(videoDetail: IPlatformVideoDetails, newVideo: Boolean = false) {
|
||||||
Logger.i(TAG, "setVideoDetails (${videoDetail.name})")
|
Logger.i(TAG, "setVideoDetails (${videoDetail.name})")
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ class MediaPlaybackService : Service() {
|
|||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Logger.v(TAG, "onDestroy");
|
Logger.v(TAG, "onDestroy");
|
||||||
_instance = null;
|
_instance = null;
|
||||||
MediaControlReceiver.onCloseReceived.emit();
|
MediaControlReceiver.onPauseReceived.emit();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
|
import android.graphics.Color
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.Network
|
import android.net.Network
|
||||||
@ -38,6 +39,7 @@ import com.futo.platformplayer.receivers.AudioNoisyReceiver
|
|||||||
import com.futo.platformplayer.services.DownloadService
|
import com.futo.platformplayer.services.DownloadService
|
||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
import com.futo.platformplayer.stores.v2.ManagedStore
|
||||||
|
import com.futo.platformplayer.views.ToastView
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
@ -380,8 +382,6 @@ class StateApp {
|
|||||||
|
|
||||||
Logger.i(TAG, "MainApp Starting: Initializing [Polycentric]");
|
Logger.i(TAG, "MainApp Starting: Initializing [Polycentric]");
|
||||||
StatePolycentric.instance.load(context);
|
StatePolycentric.instance.load(context);
|
||||||
Logger.i(TAG, "MainApp Starting: Initializing [Saved]");
|
|
||||||
StateSaved.instance.load();
|
|
||||||
|
|
||||||
Logger.i(TAG, "MainApp Starting: Initializing [Connectivity]");
|
Logger.i(TAG, "MainApp Starting: Initializing [Connectivity]");
|
||||||
displayMetrics = context.resources.displayMetrics;
|
displayMetrics = context.resources.displayMetrics;
|
||||||
@ -568,19 +568,36 @@ class StateApp {
|
|||||||
StateAnnouncement.instance.deleteAnnouncement("plugin-update")
|
StateAnnouncement.instance.deleteAnnouncement("plugin-update")
|
||||||
|
|
||||||
scopeOrNull?.launch(Dispatchers.IO) {
|
scopeOrNull?.launch(Dispatchers.IO) {
|
||||||
val updateAvailableCount = StatePlatform.instance.checkForUpdates()
|
val updateAvailable = StatePlatform.instance.checkForUpdates()
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
if (updateAvailableCount > 0) {
|
if (updateAvailable.isNotEmpty()) {
|
||||||
|
UIDialogs.appToast(
|
||||||
|
ToastView.Toast(updateAvailable
|
||||||
|
.map { " - " + it.name }
|
||||||
|
.joinToString("\n"),
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
"Plugin updates available"
|
||||||
|
));
|
||||||
|
|
||||||
StateAnnouncement.instance.registerAnnouncement(
|
StateAnnouncement.instance.registerAnnouncement(
|
||||||
"plugin-update",
|
"plugin-update",
|
||||||
"Plugin updates available",
|
"Plugin updates available",
|
||||||
"There are $updateAvailableCount plugin updates available.",
|
"There are ${updateAvailable.size} plugin updates available.",
|
||||||
AnnouncementType.SESSION_RECURRING
|
AnnouncementType.SESSION_RECURRING
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
UIDialogs.appToast("This is a test", false);
|
||||||
|
UIDialogs.appToast("This is a test 2", false);
|
||||||
|
UIDialogs.appToastError("This is a test 3 (Error)", false);
|
||||||
|
UIDialogs.appToast(ToastView.Toast("This is a test 4, with title", false, Color.WHITE, "Test title"));
|
||||||
|
UIDialogs.appToast("This is a test 5 Long text\nWith enters\nasdh asfh fds h rwe h fxh sdfh sdf h dsfh sdf hasdfhsdhg ads as", true);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mainAppStartedWithExternalFiles(context: Context) {
|
fun mainAppStartedWithExternalFiles(context: Context) {
|
||||||
|
@ -941,8 +941,8 @@ class StatePlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkForUpdates(): Int = withContext(Dispatchers.IO) {
|
suspend fun checkForUpdates(): List<SourcePluginConfig> = withContext(Dispatchers.IO) {
|
||||||
var updateAvailableCount = 0
|
var configs = mutableListOf<SourcePluginConfig>()
|
||||||
val updatesAvailableFor = hashSetOf<String>()
|
val updatesAvailableFor = hashSetOf<String>()
|
||||||
for (availableClient in getAvailableClients()) {
|
for (availableClient in getAvailableClients()) {
|
||||||
if (availableClient !is JSClient) {
|
if (availableClient !is JSClient) {
|
||||||
@ -950,13 +950,13 @@ class StatePlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (checkForUpdates(availableClient.config)) {
|
if (checkForUpdates(availableClient.config)) {
|
||||||
updateAvailableCount++
|
configs.add(availableClient.config);
|
||||||
updatesAvailableFor.add(availableClient.config.id)
|
updatesAvailableFor.add(availableClient.config.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_updatesAvailableMap = updatesAvailableFor
|
_updatesAvailableMap = updatesAvailableFor
|
||||||
return@withContext updateAvailableCount
|
return@withContext configs;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearUpdateAvailable(c: SourcePluginConfig) {
|
fun clearUpdateAvailable(c: SourcePluginConfig) {
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
package com.futo.platformplayer.states
|
|
||||||
|
|
||||||
import com.futo.platformplayer.api.media.Serializer
|
|
||||||
import com.futo.platformplayer.logging.Logger
|
|
||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
|
||||||
import com.futo.platformplayer.stores.StringStorage
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
|
||||||
data class VideoToOpen(val url: String, val timeSeconds: Long);
|
|
||||||
|
|
||||||
class StateSaved {
|
|
||||||
var videoToOpen: VideoToOpen? = null;
|
|
||||||
|
|
||||||
private val _videoToOpen = FragmentedStorage.get<StringStorage>("videoToOpen")
|
|
||||||
|
|
||||||
fun load() {
|
|
||||||
val videoToOpenString = _videoToOpen.value;
|
|
||||||
if (videoToOpenString.isNotEmpty()) {
|
|
||||||
try {
|
|
||||||
val v = Serializer.json.decodeFromString<VideoToOpen>(videoToOpenString);
|
|
||||||
videoToOpen = v;
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Logger.w(TAG, "Failed to load video to open", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.i(TAG, "loaded videoToOpen=$videoToOpen");
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setVideoToOpenNonBlocking(v: VideoToOpen? = null) {
|
|
||||||
Logger.i(TAG, "set videoToOpen=$v");
|
|
||||||
|
|
||||||
videoToOpen = v;
|
|
||||||
_videoToOpen.setAndSave(if (v != null) Serializer.json.encodeToString(v) else "");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun setVideoToOpenBlocking(v: VideoToOpen? = null) {
|
|
||||||
Logger.i(TAG, "set videoToOpen=$v");
|
|
||||||
|
|
||||||
videoToOpen = v;
|
|
||||||
_videoToOpen.setAndSaveBlocking(if (v != null) Serializer.json.encodeToString(v) else "");
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG = "StateSaved"
|
|
||||||
|
|
||||||
val instance: StateSaved = StateSaved()
|
|
||||||
}
|
|
||||||
}
|
|
@ -55,7 +55,7 @@ abstract class SubscriptionsTaskFetchAlgorithm(
|
|||||||
val clientCacheCount = clientTasks.value.size - clientTaskCount;
|
val clientCacheCount = clientTasks.value.size - clientTaskCount;
|
||||||
val limit = clientTasks.key.getSubscriptionRateLimit();
|
val limit = clientTasks.key.getSubscriptionRateLimit();
|
||||||
if(clientCacheCount > 0 && clientTaskCount > 0 && limit != null && clientTaskCount >= limit && StateApp.instance.contextOrNull?.let { it is MainActivity && it.isFragmentActive<SubscriptionsFeedFragment>() } == true) {
|
if(clientCacheCount > 0 && clientTaskCount > 0 && limit != null && clientTaskCount >= limit && StateApp.instance.contextOrNull?.let { it is MainActivity && it.isFragmentActive<SubscriptionsFeedFragment>() } == true) {
|
||||||
UIDialogs.toast("[${clientTasks.key.name}] only updating ${clientTaskCount} most urgent channels (rqs). (${clientCacheCount} cached)");
|
UIDialogs.appToast("[${clientTasks.key.name}] only updating ${clientTaskCount} most urgent channels (rqs). (${clientCacheCount} cached)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
91
app/src/main/java/com/futo/platformplayer/views/ToastView.kt
Normal file
91
app/src/main/java/com/futo/platformplayer/views/ToastView.kt
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package com.futo.platformplayer.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.dp
|
||||||
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
|
||||||
|
class ToastView : LinearLayout {
|
||||||
|
private val root: LinearLayout;
|
||||||
|
private val title: TextView;
|
||||||
|
private val text: TextView;
|
||||||
|
init {
|
||||||
|
inflate(context, R.layout.toast, this);
|
||||||
|
root = findViewById(R.id.root);
|
||||||
|
title = findViewById(R.id.title);
|
||||||
|
text = findViewById(R.id.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
|
||||||
|
setToast(ToastView.Toast("", false))
|
||||||
|
root.visibility = GONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hide(animate: Boolean, onFinished: (()->Unit)? = null) {
|
||||||
|
Logger.i("MainActivity", "Hiding toast");
|
||||||
|
if(!animate) {
|
||||||
|
root.visibility = GONE;
|
||||||
|
alpha = 0f;
|
||||||
|
onFinished?.invoke();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
animate()
|
||||||
|
.alpha(0f)
|
||||||
|
.setDuration(700)
|
||||||
|
.translationY(20.dp(context.resources).toFloat())
|
||||||
|
.withEndAction { root.visibility = GONE; onFinished?.invoke(); }
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun show(animate: Boolean) {
|
||||||
|
Logger.i("MainActivity", "Showing toast");
|
||||||
|
if(!animate) {
|
||||||
|
root.visibility = VISIBLE;
|
||||||
|
alpha = 1f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alpha = 0f;
|
||||||
|
root.visibility = VISIBLE;
|
||||||
|
translationY = 20.dp(context.resources).toFloat();
|
||||||
|
animate()
|
||||||
|
.alpha(1f)
|
||||||
|
.setDuration(700)
|
||||||
|
.translationY(0f)
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setToast(toast: Toast) {
|
||||||
|
if(toast.title.isNullOrEmpty())
|
||||||
|
title.isVisible = false;
|
||||||
|
else {
|
||||||
|
title.text = toast.title;
|
||||||
|
title.isVisible = true;
|
||||||
|
}
|
||||||
|
text.text = toast.msg;
|
||||||
|
if(toast.color != null)
|
||||||
|
text.setTextColor(toast.color);
|
||||||
|
else
|
||||||
|
text.setTextColor(Color.WHITE);
|
||||||
|
}
|
||||||
|
fun setToastAnimated(toast: Toast) {
|
||||||
|
hide(true) {
|
||||||
|
setToast(toast);
|
||||||
|
show(true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Toast(
|
||||||
|
val msg: String,
|
||||||
|
val long: Boolean,
|
||||||
|
val color: Int? = null,
|
||||||
|
val title: String? = null
|
||||||
|
);
|
||||||
|
}
|
6
app/src/main/res/drawable/background_toast.xml
Normal file
6
app/src/main/res/drawable/background_toast.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#EE202020" />
|
||||||
|
<corners android:radius="10dp" />
|
||||||
|
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||||
|
</shape>
|
@ -70,4 +70,13 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:elevation="15dp">
|
android:elevation="15dp">
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
<com.futo.platformplayer.views.ToastView
|
||||||
|
android:id="@+id/toast_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="50dp"
|
||||||
|
android:elevation="30dp"
|
||||||
|
app:layout_constraintLeft_toLeftOf="@id/fragment_main"
|
||||||
|
app:layout_constraintRight_toRightOf="@id/fragment_main"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/fragment_main" />
|
||||||
</androidx.constraintlayout.motion.widget.MotionLayout>
|
</androidx.constraintlayout.motion.widget.MotionLayout>
|
41
app/src/main/res/layout/toast.xml
Normal file
41
app/src/main/res/layout/toast.xml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:toolNs="http://schemas.android.com/tools"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:id="@+id/root"
|
||||||
|
android:padding="10dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/background_toast"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingRight="15dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
toolNs:text="Some Title"
|
||||||
|
android:fontFamily="@font/inter_bold"
|
||||||
|
android:textSize="15dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/inter_light"
|
||||||
|
toolNs:text="This is a test" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
Loading…
x
Reference in New Issue
Block a user