diff --git a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt index a3399d2a80885395bb59408d8ed1d10938c26bf0..0ebf26bd490101fa212040f3260f6b8959cdd0ca 100644 --- a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt +++ b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt @@ -37,6 +37,7 @@ import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateBackup import com.futo.platformplayer.stores.v2.ManagedStore +import com.futo.platformplayer.views.ToastView import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -398,13 +399,28 @@ class UIDialogs { StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) { try { StateApp.withContext { - Toast.makeText(it, text, if (long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT).show(); + toast(it, text, long); } } catch (e: Throwable) { 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) { //TODO: Is not actually clickable... diff --git a/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt b/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt index f846cd9710a1d8c38aa16e237f05bbfb67b2524d..01f2f48b12fe2868525b280e6b2b5a1bd982f701 100644 --- a/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt +++ b/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt @@ -343,7 +343,7 @@ class UISlideOverlays { videoSources.filter { it is IVideoUrlSource && it.isDownloadable() }.asIterable(), Settings.instance.downloads.getDefaultVideoQualityPixels(), FutoVideoPlayerBase.PREFERED_VIDEO_CONTAINERS - ) as IVideoUrlSource; + ) as IVideoUrlSource?; } if (audioSources != null) { diff --git a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt index 294c811f1fb8e39fb79937e12cb6299714a2d275..665b7624c7f397c31a470f8a8a8956180a5c9f24 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt @@ -1,7 +1,6 @@ package com.futo.platformplayer.activities import android.annotation.SuppressLint -import android.app.NotificationManager import android.content.Context import android.content.Intent 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.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentContainerView import androidx.lifecycle.Lifecycle @@ -45,6 +45,7 @@ import com.futo.platformplayer.states.* import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.SubscriptionStorage import com.futo.platformplayer.stores.v2.ManagedStore +import com.futo.platformplayer.views.ToastView import com.google.gson.JsonParser import com.google.zxing.integration.android.IntentIntegrator import kotlinx.coroutines.* @@ -54,6 +55,7 @@ import java.io.PrintWriter import java.io.StringWriter import java.lang.reflect.InvocationTargetException import java.util.* +import java.util.concurrent.ConcurrentLinkedQueue class MainActivity : AppCompatActivity, IWithResultLauncher { @@ -65,6 +67,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { lateinit var rootView : MotionLayout; private lateinit var _overlayContainer: FrameLayout; + private lateinit var _toastView: ToastView; //Segment Containers private lateinit var _fragContainerTopBar: FragmentContainerView; @@ -207,7 +210,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { _fragContainerVideoDetail = findViewById(R.id.fragment_overlay); _fragContainerOverlay = findViewById(R.id.fragment_overlay_container); _overlayContainer = findViewById(R.id.overlay_container); - //_overlayContainer.visibility = View.GONE; + _toastView = findViewById(R.id.toast_view); //Initialize fragments @@ -478,21 +481,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { } _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() { @@ -864,7 +852,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { _orientationManager.disable(); StateApp.instance.mainAppDestroyed(this); - StateSaved.instance.setVideoToOpenBlocking(null); } 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. diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt index 3881b92aea754bd8b19426550ca9bec448336f47..88fc76dc6382c7f6ea39558967de533a74a2ef39 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SubscriptionsFeedFragment.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.futo.platformplayer.* 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.IPlatformContent import com.futo.platformplayer.api.media.models.video.IPlatformVideo @@ -427,7 +428,7 @@ class SubscriptionsFeedFragment : MainFragment() { context?.let { fragment.lifecycleScope.launch(Dispatchers.Main) { try { - if (exs.size <= 8) { + if (exs.size <= 3) { for (ex in exs) { var toShow = ex; var channel: String? = null; @@ -437,12 +438,11 @@ class SubscriptionsFeedFragment : MainFragment() { } Logger.e(TAG, "Channel [${channel}] failed", ex); if (toShow is PluginException) - UIDialogs.toast( - it, + UIDialogs.appToast( context.getString(R.string.plugin_pluginname_failed_message).replace("{pluginName}", toShow.config.name).replace("{message}", toShow.message ?: "") ); else - UIDialogs.toast(it, ex.message ?: ""); + UIDialogs.appToast(ex.message ?: ""); } } else { @@ -453,7 +453,7 @@ class SubscriptionsFeedFragment : MainFragment() { .map { it!! } .toList(); 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) { Logger.e(TAG, "Failed to handle exceptions", e) 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 cb0abbed83cdce530c2b5ce0334c72109b52b79d..487832333ec0270d9e2a25cbce0587fbba056591 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 @@ -25,8 +25,6 @@ import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.models.PlatformVideoWithTime import com.futo.platformplayer.models.UrlVideoWithTime 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 class VideoDetailFragment : MainFragment { @@ -372,11 +370,6 @@ class VideoDetailFragment : MainFragment { Logger.v(TAG, "shouldStop: $shouldStop"); if(shouldStop) { - _viewDetail?.let { - val v = it.video ?: return@let; - StateSaved.instance.setVideoToOpenBlocking(VideoToOpen(v.url, (it.lastPositionMilliseconds / 1000.0f).toLong())); - } - _viewDetail?.onStop(); StateCasting.instance.onStop(); Logger.v(TAG, "called onStop() shouldStop: $shouldStop"); 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 7d7d58540f41e5a91c2279ee6ddcf76ba71dcfb3..695edab30ab3159c1317e3773338d9ddca528a8c 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 @@ -149,6 +149,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import userpackage.Protocol import java.time.OffsetDateTime @@ -853,14 +855,19 @@ class VideoDetailView : ConstraintLayout { } } } + + + private val _historyIndexLock = Mutex(false); suspend fun getHistoryIndex(video: IPlatformVideo): DBHistory.Index = withContext(Dispatchers.IO){ - val current = _historyIndex; - if(current == null || current.url != video.url) { - val index = StateHistory.instance.getHistoryByVideo(video, true)!!; - _historyIndex = index; - return@withContext index; + _historyIndexLock.withLock { + val current = _historyIndex; + if(current == null || current.url != video.url) { + val index = StateHistory.instance.getHistoryByVideo(video, true)!!; + _historyIndex = index; + return@withContext index; + } + return@withContext current; } - return@withContext current; } @@ -1121,7 +1128,7 @@ class VideoDetailView : ConstraintLayout { switchContentView(_container_content_main); } - @OptIn(ExperimentalCoroutinesApi::class) + //@OptIn(ExperimentalCoroutinesApi::class) fun setVideoDetails(videoDetail: IPlatformVideoDetails, newVideo: Boolean = false) { Logger.i(TAG, "setVideoDetails (${videoDetail.name})") diff --git a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt index f6fb28798de4935c74b4fae44c99069f13571ac4..77125de8fd140f7ba7eb9aeb4114c6a9417dbc47 100644 --- a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt +++ b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt @@ -143,7 +143,7 @@ class MediaPlaybackService : Service() { override fun onDestroy() { Logger.v(TAG, "onDestroy"); _instance = null; - MediaControlReceiver.onCloseReceived.emit(); + MediaControlReceiver.onPauseReceived.emit(); super.onDestroy(); } 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 14c75210a2fcbc9d8c721ed01cf94b392dc85b7b..36551de65da1be903b4c3b68cfc0ffe55aa4b52c 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateApp.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateApp.kt @@ -5,6 +5,7 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.graphics.Color import android.media.AudioManager import android.net.ConnectivityManager import android.net.Network @@ -38,6 +39,7 @@ import com.futo.platformplayer.receivers.AudioNoisyReceiver import com.futo.platformplayer.services.DownloadService import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.v2.ManagedStore +import com.futo.platformplayer.views.ToastView import kotlinx.coroutines.* import java.io.File import java.time.OffsetDateTime @@ -380,8 +382,6 @@ class StateApp { Logger.i(TAG, "MainApp Starting: Initializing [Polycentric]"); StatePolycentric.instance.load(context); - Logger.i(TAG, "MainApp Starting: Initializing [Saved]"); - StateSaved.instance.load(); Logger.i(TAG, "MainApp Starting: Initializing [Connectivity]"); displayMetrics = context.resources.displayMetrics; @@ -568,19 +568,36 @@ class StateApp { StateAnnouncement.instance.deleteAnnouncement("plugin-update") scopeOrNull?.launch(Dispatchers.IO) { - val updateAvailableCount = StatePlatform.instance.checkForUpdates() + val updateAvailable = StatePlatform.instance.checkForUpdates() 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( "plugin-update", "Plugin updates available", - "There are $updateAvailableCount plugin updates available.", + "There are ${updateAvailable.size} plugin updates available.", 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) { diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt index fe06e78d6c41d79c53f03eb8dbdfbf1386d2e4fb..5c8f77c511575e9b6d75a7cef70ff23fa864e3a4 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt @@ -941,8 +941,8 @@ class StatePlatform { } } - suspend fun checkForUpdates(): Int = withContext(Dispatchers.IO) { - var updateAvailableCount = 0 + suspend fun checkForUpdates(): List<SourcePluginConfig> = withContext(Dispatchers.IO) { + var configs = mutableListOf<SourcePluginConfig>() val updatesAvailableFor = hashSetOf<String>() for (availableClient in getAvailableClients()) { if (availableClient !is JSClient) { @@ -950,13 +950,13 @@ class StatePlatform { } if (checkForUpdates(availableClient.config)) { - updateAvailableCount++ + configs.add(availableClient.config); updatesAvailableFor.add(availableClient.config.id) } } _updatesAvailableMap = updatesAvailableFor - return@withContext updateAvailableCount + return@withContext configs; } fun clearUpdateAvailable(c: SourcePluginConfig) { diff --git a/app/src/main/java/com/futo/platformplayer/states/StateSaved.kt b/app/src/main/java/com/futo/platformplayer/states/StateSaved.kt deleted file mode 100644 index 1bd4df34148cfe69837dc64382ee624e032d43dc..0000000000000000000000000000000000000000 --- a/app/src/main/java/com/futo/platformplayer/states/StateSaved.kt +++ /dev/null @@ -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() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/subscription/SubscriptionsTaskFetchAlgorithm.kt b/app/src/main/java/com/futo/platformplayer/subscription/SubscriptionsTaskFetchAlgorithm.kt index b4b90624963508dd0f79d0ec4386666e4c0371a7..a7b57b74b30c2362a3c309e1c2d393a2889b0ef0 100644 --- a/app/src/main/java/com/futo/platformplayer/subscription/SubscriptionsTaskFetchAlgorithm.kt +++ b/app/src/main/java/com/futo/platformplayer/subscription/SubscriptionsTaskFetchAlgorithm.kt @@ -55,7 +55,7 @@ abstract class SubscriptionsTaskFetchAlgorithm( val clientCacheCount = clientTasks.value.size - clientTaskCount; 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) { - 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)"); } } diff --git a/app/src/main/java/com/futo/platformplayer/views/ToastView.kt b/app/src/main/java/com/futo/platformplayer/views/ToastView.kt new file mode 100644 index 0000000000000000000000000000000000000000..2600dff25c51f0faf502c142385b5d094beba969 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/views/ToastView.kt @@ -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 + ); +} \ No newline at end of file diff --git a/app/src/main/res/drawable/background_toast.xml b/app/src/main/res/drawable/background_toast.xml new file mode 100644 index 0000000000000000000000000000000000000000..85dbffb768f89f2359759a38a9b25154d68b6e2d --- /dev/null +++ b/app/src/main/res/drawable/background_toast.xml @@ -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> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e7c51891c566d9217e21c22b1c46844d7a4dcbd2..815d9f73d5d77c6057ea4240e7c9f86a6fa76d80 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -70,4 +70,13 @@ android:visibility="gone" android:elevation="15dp"> </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> \ No newline at end of file diff --git a/app/src/main/res/layout/toast.xml b/app/src/main/res/layout/toast.xml new file mode 100644 index 0000000000000000000000000000000000000000..9ecd8c0e3059f2df6b08246f3ab2c7e936068e09 --- /dev/null +++ b/app/src/main/res/layout/toast.xml @@ -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> \ No newline at end of file