mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-16 04:52:39 +02:00
New notification ui
This commit is contained in:
@@ -110,6 +110,7 @@ import com.futo.platformplayer.stores.StringStorage
|
|||||||
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.futo.platformplayer.views.ToastView
|
||||||
|
import com.futo.platformplayer.views.notification.NotificationOverlayView
|
||||||
import com.futo.polycentric.core.ApiMethods
|
import com.futo.polycentric.core.ApiMethods
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
import com.google.zxing.integration.android.IntentIntegrator
|
import com.google.zxing.integration.android.IntentIntegrator
|
||||||
@@ -201,6 +202,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
lateinit var _fragLibraryVideos: LibraryVideosFragment;
|
lateinit var _fragLibraryVideos: LibraryVideosFragment;
|
||||||
lateinit var _fragLibrarySearch: LibrarySearchFragment;
|
lateinit var _fragLibrarySearch: LibrarySearchFragment;
|
||||||
lateinit var _fragLibraryFiles: LibraryFilesFragment;
|
lateinit var _fragLibraryFiles: LibraryFilesFragment;
|
||||||
|
lateinit var _fragNotifications: NotificationOverlayView.Frag;
|
||||||
lateinit var _fragSettings: SettingsFragment;
|
lateinit var _fragSettings: SettingsFragment;
|
||||||
lateinit var _fragDeveloper: DeveloperFragment;
|
lateinit var _fragDeveloper: DeveloperFragment;
|
||||||
lateinit var _fragLogin: LoginFragment;
|
lateinit var _fragLogin: LoginFragment;
|
||||||
@@ -389,6 +391,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
_fragLibraryVideos = LibraryVideosFragment.newInstance();
|
_fragLibraryVideos = LibraryVideosFragment.newInstance();
|
||||||
_fragLibraryFiles = LibraryFilesFragment.newInstance();
|
_fragLibraryFiles = LibraryFilesFragment.newInstance();
|
||||||
_fragLibrarySearch = LibrarySearchFragment.newInstance();
|
_fragLibrarySearch = LibrarySearchFragment.newInstance();
|
||||||
|
_fragNotifications = NotificationOverlayView.Frag();
|
||||||
_fragSettings = SettingsFragment.newInstance();
|
_fragSettings = SettingsFragment.newInstance();
|
||||||
_fragDeveloper = DeveloperFragment.newInstance();
|
_fragDeveloper = DeveloperFragment.newInstance();
|
||||||
_fragLogin = LoginFragment.newInstance();
|
_fragLogin = LoginFragment.newInstance();
|
||||||
@@ -538,6 +541,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
_fragLibrarySearch.topBar = _fragTopBarSearch;
|
_fragLibrarySearch.topBar = _fragTopBarSearch;
|
||||||
_fragSettings.topBar = _fragTopBarNavigation;
|
_fragSettings.topBar = _fragTopBarNavigation;
|
||||||
_fragDeveloper.topBar = _fragTopBarNavigation;
|
_fragDeveloper.topBar = _fragTopBarNavigation;
|
||||||
|
_fragNotifications.topBar = _fragTopBarGeneral;
|
||||||
|
|
||||||
_fragBrowser.topBar = _fragTopBarNavigation;
|
_fragBrowser.topBar = _fragTopBarNavigation;
|
||||||
|
|
||||||
@@ -1368,6 +1372,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
LibraryVideosFragment::class -> _fragLibraryVideos as T;
|
LibraryVideosFragment::class -> _fragLibraryVideos as T;
|
||||||
LibraryFilesFragment::class -> _fragLibraryFiles as T;
|
LibraryFilesFragment::class -> _fragLibraryFiles as T;
|
||||||
LibrarySearchFragment::class -> _fragLibrarySearch as T;
|
LibrarySearchFragment::class -> _fragLibrarySearch as T;
|
||||||
|
NotificationOverlayView.Frag::class -> _fragNotifications as T;
|
||||||
SettingsFragment:: class -> _fragSettings as T;
|
SettingsFragment:: class -> _fragSettings as T;
|
||||||
DeveloperFragment::class -> _fragDeveloper as T;
|
DeveloperFragment::class -> _fragDeveloper as T;
|
||||||
LoginFragment::class -> _fragLogin as T;
|
LoginFragment::class -> _fragLogin as T;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
|||||||
private val _progressBar: ProgressBar;
|
private val _progressBar: ProgressBar;
|
||||||
private val _spinnerSortBy: Spinner;
|
private val _spinnerSortBy: Spinner;
|
||||||
private val _containerSortBy: LinearLayout;
|
private val _containerSortBy: LinearLayout;
|
||||||
private val _announcementView: AnnouncementView;
|
//private val _announcementView: AnnouncementView;
|
||||||
private val _tagsView: TagsView;
|
private val _tagsView: TagsView;
|
||||||
private val _textCentered: TextView;
|
private val _textCentered: TextView;
|
||||||
private val _emptyPagerContainer: FrameLayout;
|
private val _emptyPagerContainer: FrameLayout;
|
||||||
@@ -87,7 +87,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
|||||||
_textCentered = findViewById(R.id.text_centered);
|
_textCentered = findViewById(R.id.text_centered);
|
||||||
_emptyPagerContainer = findViewById(R.id.empty_pager_container);
|
_emptyPagerContainer = findViewById(R.id.empty_pager_container);
|
||||||
_progressBar = findViewById(R.id.progress_bar);
|
_progressBar = findViewById(R.id.progress_bar);
|
||||||
_announcementView = findViewById(R.id.announcement_view)
|
//_announcementView = findViewById(R.id.announcement_view)
|
||||||
_progressBar.inactiveColor = Color.TRANSPARENT;
|
_progressBar.inactiveColor = Color.TRANSPARENT;
|
||||||
|
|
||||||
_swipeRefresh = findViewById(R.id.swipe_refresh);
|
_swipeRefresh = findViewById(R.id.swipe_refresh);
|
||||||
@@ -192,7 +192,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun showAnnouncementView() {
|
protected fun showAnnouncementView() {
|
||||||
_announcementView.visibility = View.VISIBLE
|
//_announcementView.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ensureEnoughContentVisible(filteredResults: List<TConverted>) {
|
private fun ensureEnoughContentVisible(filteredResults: List<TConverted>) {
|
||||||
|
|||||||
+49
@@ -7,6 +7,9 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.CreatorsFragment
|
import com.futo.platformplayer.fragment.mainactivity.main.CreatorsFragment
|
||||||
@@ -17,18 +20,54 @@ import com.futo.platformplayer.fragment.mainactivity.main.PlaylistsFragment
|
|||||||
import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragment
|
import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragmentData
|
import com.futo.platformplayer.fragment.mainactivity.main.SuggestionsFragmentData
|
||||||
import com.futo.platformplayer.models.SearchType
|
import com.futo.platformplayer.models.SearchType
|
||||||
|
import com.futo.platformplayer.states.StateAnnouncement
|
||||||
import com.futo.platformplayer.views.casting.CastButton
|
import com.futo.platformplayer.views.casting.CastButton
|
||||||
|
import com.futo.platformplayer.views.notification.NotificationOverlayView
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class GeneralTopBarFragment : TopFragment() {
|
class GeneralTopBarFragment : TopFragment() {
|
||||||
private var _buttonSearch: ImageButton? = null;
|
private var _buttonSearch: ImageButton? = null;
|
||||||
private var _buttonCast: CastButton? = null;
|
private var _buttonCast: CastButton? = null;
|
||||||
|
|
||||||
|
private var _buttonNotifs: ConstraintLayout? = null;
|
||||||
|
private var _buttonNotifIcon: ImageView? = null;
|
||||||
|
private var _buttonNotifCount: TextView? = null;
|
||||||
|
|
||||||
|
init {
|
||||||
|
StateAnnouncement.instance.onAnnouncementChanged.subscribe {
|
||||||
|
lifecycleScope?.launch(Dispatchers.Main) {
|
||||||
|
updateNotifCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateNotifCount() {
|
||||||
|
val currentAnnouncements = StateAnnouncement.instance.getVisibleAnnouncements();
|
||||||
|
if(currentAnnouncements.any())
|
||||||
|
_buttonNotifCount?.let {
|
||||||
|
it.text = currentAnnouncements.size.toString();
|
||||||
|
it.visibility = View.VISIBLE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_buttonNotifCount?.let {
|
||||||
|
it.text = currentAnnouncements.size.toString();
|
||||||
|
it.visibility = View.GONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onShown(parameter: Any?) {
|
override fun onShown(parameter: Any?) {
|
||||||
if(currentMain is CreatorsFragment) {
|
if(currentMain is CreatorsFragment) {
|
||||||
_buttonSearch?.setImageResource(R.drawable.ic_person_search_300w);
|
_buttonSearch?.setImageResource(R.drawable.ic_person_search_300w);
|
||||||
} else {
|
} else {
|
||||||
_buttonSearch?.setImageResource(R.drawable.ic_search_300w);
|
_buttonSearch?.setImageResource(R.drawable.ic_search_300w);
|
||||||
}
|
}
|
||||||
|
if(currentMain is NotificationOverlayView.Frag) {
|
||||||
|
_buttonNotifIcon?.setImageResource(R.drawable.ic_notifications_filled)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_buttonNotifIcon?.setImageResource(R.drawable.ic_notifications)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
override fun onHide() {
|
override fun onHide() {
|
||||||
|
|
||||||
@@ -44,6 +83,16 @@ class GeneralTopBarFragment : TopFragment() {
|
|||||||
val buttonSearch: ImageButton = view.findViewById(R.id.button_search);
|
val buttonSearch: ImageButton = view.findViewById(R.id.button_search);
|
||||||
_buttonCast = view.findViewById(R.id.button_cast);
|
_buttonCast = view.findViewById(R.id.button_cast);
|
||||||
|
|
||||||
|
_buttonNotifs = view.findViewById(R.id.button_notifs);
|
||||||
|
_buttonNotifIcon = view.findViewById(R.id.button_notifs_icon);
|
||||||
|
_buttonNotifCount = view.findViewById(R.id.button_notifs_count);
|
||||||
|
|
||||||
|
updateNotifCount();
|
||||||
|
|
||||||
|
_buttonNotifs?.setOnClickListener {
|
||||||
|
navigate<NotificationOverlayView.Frag>();
|
||||||
|
}
|
||||||
|
|
||||||
buttonSearch.setOnClickListener {
|
buttonSearch.setOnClickListener {
|
||||||
if(currentMain is CreatorsFragment) {
|
if(currentMain is CreatorsFragment) {
|
||||||
navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.CREATOR));
|
navigate<SuggestionsFragment>(SuggestionsFragmentData("", SearchType.CREATOR));
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
package com.futo.platformplayer.states
|
package com.futo.platformplayer.states
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
import com.futo.platformplayer.api.http.ManagedHttpClient
|
||||||
|
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
|
||||||
import com.futo.platformplayer.constructs.Event0
|
import com.futo.platformplayer.constructs.Event0
|
||||||
|
import com.futo.platformplayer.dialogs.PluginUpdateDialog
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import com.futo.platformplayer.models.ImageVariable
|
||||||
import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer
|
import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer
|
||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
import com.futo.platformplayer.stores.StringHashSetStorage
|
import com.futo.platformplayer.stores.StringHashSetStorage
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@@ -110,6 +117,45 @@ class StateAnnouncement {
|
|||||||
onAnnouncementChanged.emit();
|
onAnnouncementChanged.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Special Announcements
|
||||||
|
fun registerPluginUpdate(oldConfig: SourcePluginConfig, newConfig: SourcePluginConfig) {
|
||||||
|
registerAnnouncementSession(SessionAnnouncement(
|
||||||
|
"update-plugin-" + UUID.randomUUID().toString(),
|
||||||
|
"${newConfig.name} update v${newConfig.version} available!",
|
||||||
|
"An update is available to upgrade from ${oldConfig.version} to ${newConfig.version}.",
|
||||||
|
AnnouncementType.SESSION,
|
||||||
|
null, "updates", "Update", StateAnnouncement.ACTION_UPDATE_PLUGIN,
|
||||||
|
null, null,oldConfig.id,
|
||||||
|
newConfig?.absoluteIconUrl?.let { ImageVariable.fromUrl(it) }
|
||||||
|
).withExtraAction("Changelog", StateAnnouncement.ACTION_CHANGELOG, oldConfig.id));
|
||||||
|
}
|
||||||
|
fun registerPluginUpdated(newConfig: SourcePluginConfig) {
|
||||||
|
registerAnnouncementSession(SessionAnnouncement(
|
||||||
|
"updated-plugin-" + UUID.randomUUID().toString(),
|
||||||
|
"${newConfig.name} updated to v${newConfig.version}!",
|
||||||
|
"You have succesfully been updater to v${newConfig.version}.",
|
||||||
|
AnnouncementType.SESSION,
|
||||||
|
null, "updates", null, null,
|
||||||
|
null, null,null,
|
||||||
|
newConfig?.absoluteIconUrl?.let { ImageVariable.fromUrl(it) }
|
||||||
|
).withExtraAction("Changelog", StateAnnouncement.ACTION_CHANGELOG, newConfig.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerLoading(title: String, description: String, icon: ImageVariable? = null): String {
|
||||||
|
val id = "loading-" + UUID.randomUUID().toString();
|
||||||
|
registerAnnouncementSession(SessionAnnouncement(
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
AnnouncementType.ONGOING,
|
||||||
|
null, "loading", null, null,
|
||||||
|
null, null,null, icon
|
||||||
|
));
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun getVisibleAnnouncements(category: String? = null): List<Announcement> {
|
fun getVisibleAnnouncements(category: String? = null): List<Announcement> {
|
||||||
synchronized(_lock) {
|
synchronized(_lock) {
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
@@ -122,7 +168,9 @@ class StateAnnouncement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun closeAnnouncement(id: String) {
|
fun closeAnnouncement(id: String?) {
|
||||||
|
if(id == null)
|
||||||
|
return;
|
||||||
val item: Announcement?;
|
val item: Announcement?;
|
||||||
synchronized(_lock) {
|
synchronized(_lock) {
|
||||||
item = _announcementsStore.findItem { it.id == id };
|
item = _announcementsStore.findItem { it.id == id };
|
||||||
@@ -164,6 +212,7 @@ class StateAnnouncement {
|
|||||||
cancelAction?.invoke(item);
|
cancelAction?.invoke(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onAnnouncementChanged?.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteAllAnnouncements() {
|
fun deleteAllAnnouncements() {
|
||||||
@@ -194,7 +243,9 @@ class StateAnnouncement {
|
|||||||
|
|
||||||
onAnnouncementChanged.emit();
|
onAnnouncementChanged.emit();
|
||||||
}
|
}
|
||||||
fun neverAnnouncement(id: String) {
|
fun neverAnnouncement(id: String?) {
|
||||||
|
if(id == null)
|
||||||
|
return;
|
||||||
synchronized(_lock) {
|
synchronized(_lock) {
|
||||||
val item = _announcementsStore.findItem { it.id == id };
|
val item = _announcementsStore.findItem { it.id == id };
|
||||||
if (item != null && !_announcementsNever.contains(id))
|
if (item != null && !_announcementsNever.contains(id))
|
||||||
@@ -208,19 +259,26 @@ class StateAnnouncement {
|
|||||||
_announcementsNever.save();
|
_announcementsNever.save();
|
||||||
onAnnouncementChanged.emit();
|
onAnnouncementChanged.emit();
|
||||||
}
|
}
|
||||||
fun actionAnnouncement(id: String) {
|
fun actionAnnouncement(id: String?, extra: Boolean = false) {
|
||||||
|
if(id == null)
|
||||||
|
return;
|
||||||
val item = _announcementsStore.findItem { it.id == id } ?: _sessionAnnouncements[id];
|
val item = _announcementsStore.findItem { it.id == id } ?: _sessionAnnouncements[id];
|
||||||
if(item != null)
|
if(item != null)
|
||||||
actionAnnouncement(item);
|
actionAnnouncement(item, extra);
|
||||||
}
|
}
|
||||||
fun actionAnnouncement(item: Announcement) {
|
fun actionAnnouncement(item: Announcement, extra: Boolean = false) {
|
||||||
|
val actionId = if(!extra) item.actionId else if(item is SessionAnnouncement) item.extraActionId else null;
|
||||||
|
val actionData = if(!extra) item.actionData else if(item is SessionAnnouncement) item.extraActionData else null;
|
||||||
|
|
||||||
val action = _sessionActions[item.id];
|
val action = _sessionActions[item.id];
|
||||||
if (action != null) {
|
if (action != null) {
|
||||||
action(item);
|
action(item);
|
||||||
} else {
|
} else {
|
||||||
when (item.actionId) {
|
when (actionId) {
|
||||||
ACTION_NEVER -> neverAnnouncement(item.id);
|
ACTION_NEVER -> neverAnnouncement(item.id);
|
||||||
ACTION_SOMETHING -> actionSomething();
|
ACTION_SOMETHING -> actionSomething();
|
||||||
|
ACTION_CHANGELOG -> actionChangelog(actionData);
|
||||||
|
ACTION_UPDATE_PLUGIN -> actionUpdatePlugin(item.id, actionData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,6 +309,83 @@ class StateAnnouncement {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun actionChangelog(id: String?) {
|
||||||
|
if(id == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
StateApp.instance.contextOrNull?.let { context ->
|
||||||
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||||
|
val plugin = StatePlugins.instance.getPlugin(id);
|
||||||
|
if (plugin == null)
|
||||||
|
return@launch
|
||||||
|
val update = StatePlugins.instance.checkForUpdates(plugin.config);
|
||||||
|
if(update == null)
|
||||||
|
return@launch;
|
||||||
|
|
||||||
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
|
||||||
|
UIDialogs.showChangelogDialog(context, update.version, update.changelog!!.filterKeys { it.toIntOrNull() != null }
|
||||||
|
.mapKeys { it.key.toInt() }
|
||||||
|
.mapValues { update.getChangelogString(it.key.toString()) ?: "" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun actionUpdatePlugin(notifId: String?, id: String?) {
|
||||||
|
if(id == null)
|
||||||
|
return;
|
||||||
|
val plugin = StatePlugins.instance.getPlugin(id);
|
||||||
|
if (plugin == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
closeAnnouncement(notifId);
|
||||||
|
val loadingId = registerLoading("Updating ${plugin.config.name}..", "An update is in progress for ${plugin.config.name}.",
|
||||||
|
if(plugin.config.absoluteIconUrl != null) ImageVariable.fromUrl(plugin.config.absoluteIconUrl!!) else null);
|
||||||
|
|
||||||
|
|
||||||
|
StateApp.instance.contextOrNull?.let { context ->
|
||||||
|
|
||||||
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val update = StatePlugins.instance.checkForUpdates(plugin.config);
|
||||||
|
if (update == null)
|
||||||
|
return@launch;
|
||||||
|
|
||||||
|
val client = ManagedHttpClient();
|
||||||
|
client.setTimeout(10000);
|
||||||
|
val script = StatePlugins.instance.getScript(plugin.config.id) ?: "";
|
||||||
|
val newScript = client.get(update.absoluteScriptUrl)?.body?.string();
|
||||||
|
if(newScript.isNullOrEmpty())
|
||||||
|
throw IllegalStateException("No script found");
|
||||||
|
|
||||||
|
if(true || plugin.config.isLowRiskUpdate(script, update, newScript)) {
|
||||||
|
StatePlugins.instance.installPluginBackground(context, StateApp.instance.scope, update, newScript,
|
||||||
|
{ text: String, progress: Double -> },
|
||||||
|
{ ex ->
|
||||||
|
if(ex == null) {
|
||||||
|
registerPluginUpdated(update);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UIDialogs.appToast("Update for ${update.name} failed\n" + ex.message);
|
||||||
|
}
|
||||||
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
|
||||||
|
closeAnnouncement(loadingId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
|
||||||
|
closeAnnouncement(loadingId);
|
||||||
|
UIDialogs.showPluginUpdateDialog(context, plugin.config, update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(ex: Throwable) {
|
||||||
|
Logger.e(TAG, "Failed to trigger update from announcement", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun registerDefaultHandlerAnnouncement() {
|
fun registerDefaultHandlerAnnouncement() {
|
||||||
registerAnnouncement(
|
registerAnnouncement(
|
||||||
"default-url-handler",
|
"default-url-handler",
|
||||||
@@ -279,6 +414,8 @@ class StateAnnouncement {
|
|||||||
|
|
||||||
|
|
||||||
const val ACTION_SOMETHING = "SOMETHING";
|
const val ACTION_SOMETHING = "SOMETHING";
|
||||||
|
const val ACTION_CHANGELOG = "CHANGELOG";
|
||||||
|
const val ACTION_UPDATE_PLUGIN = "UPDATE_PLUGIN";
|
||||||
const val ACTION_NEVER = "NEVER";
|
const val ACTION_NEVER = "NEVER";
|
||||||
private const val TAG = "StateAnnouncement";
|
private const val TAG = "StateAnnouncement";
|
||||||
}
|
}
|
||||||
@@ -294,7 +431,8 @@ open class Announcement(
|
|||||||
val time: OffsetDateTime? = null,
|
val time: OffsetDateTime? = null,
|
||||||
val category: String? = null,
|
val category: String? = null,
|
||||||
val actionName: String? = null,
|
val actionName: String? = null,
|
||||||
val actionId: String? = null
|
val actionId: String? = null,
|
||||||
|
val actionData: String? = null
|
||||||
);
|
);
|
||||||
class SessionAnnouncement(
|
class SessionAnnouncement(
|
||||||
id: String,
|
id: String,
|
||||||
@@ -306,7 +444,9 @@ class SessionAnnouncement(
|
|||||||
actionName: String? = null,
|
actionName: String? = null,
|
||||||
actionId: String? = null,
|
actionId: String? = null,
|
||||||
val cancelName: String? = null,
|
val cancelName: String? = null,
|
||||||
val cancelActionId: String? = null
|
val cancelActionId: String? = null,
|
||||||
|
actionData: String? = null,
|
||||||
|
val icon: ImageVariable? = null
|
||||||
): Announcement(
|
): Announcement(
|
||||||
id= id,
|
id= id,
|
||||||
title = title,
|
title = title,
|
||||||
@@ -315,13 +455,26 @@ class SessionAnnouncement(
|
|||||||
time = time,
|
time = time,
|
||||||
category = category,
|
category = category,
|
||||||
actionName = actionName,
|
actionName = actionName,
|
||||||
actionId = actionId
|
actionId = actionId,
|
||||||
);
|
actionData = actionData
|
||||||
|
) {
|
||||||
|
var extraActionName: String? = null;
|
||||||
|
var extraActionId: String? = null;
|
||||||
|
var extraActionData: String? = null;
|
||||||
|
|
||||||
|
fun withExtraAction(name: String, id: String, data: String? = null): SessionAnnouncement {
|
||||||
|
extraActionName = name;
|
||||||
|
extraActionId = id;
|
||||||
|
extraActionData = data;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class AnnouncementType(val value : Int) {
|
enum class AnnouncementType(val value : Int) {
|
||||||
DELETABLE(0), //Close button deletes announcement (generally for actions)
|
DELETABLE(0), //Close button deletes announcement (generally for actions)
|
||||||
RECURRING(1), //Shows up till never is pressed (generally for patchnotes etc)
|
RECURRING(1), //Shows up till never is pressed (generally for patchnotes etc)
|
||||||
PERMANENT(2), //Shows up until deleted through other means (action)
|
PERMANENT(2), //Shows up until deleted through other means (action)
|
||||||
SESSION(3), //Not persistent, only during this session
|
SESSION(3), //Not persistent, only during this session
|
||||||
SESSION_RECURRING(4); //Not persistent, only during this session, recurring id
|
SESSION_RECURRING(4), //Not persistent, only during this session, recurring id
|
||||||
|
ONGOING(5);
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,7 @@ import com.futo.platformplayer.logging.AndroidLogConsumer
|
|||||||
import com.futo.platformplayer.logging.FileLogConsumer
|
import com.futo.platformplayer.logging.FileLogConsumer
|
||||||
import com.futo.platformplayer.logging.LogLevel
|
import com.futo.platformplayer.logging.LogLevel
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import com.futo.platformplayer.models.ImageVariable
|
||||||
import com.futo.platformplayer.receivers.AudioNoisyReceiver
|
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
|
||||||
@@ -732,8 +733,10 @@ class StateApp {
|
|||||||
));
|
));
|
||||||
|
|
||||||
for(update in updateAvailable)
|
for(update in updateAvailable)
|
||||||
if(StatePlatform.instance.isClientEnabled(update.first.id))
|
if(StatePlatform.instance.isClientEnabled(update.first.id)) {
|
||||||
UIDialogs.showPluginUpdateDialog(context, update.first, update.second);
|
//UIDialogs.showPluginUpdateDialog(context, update.first, update.second);
|
||||||
|
StateAnnouncement.instance.registerPluginUpdate(update.first, update.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class StatePlugins {
|
|||||||
_updatesAvailableMap = updatesAvailableFor
|
_updatesAvailableMap = updatesAvailableFor
|
||||||
return@withContext configs;
|
return@withContext configs;
|
||||||
}
|
}
|
||||||
private suspend fun checkForUpdates(c: SourcePluginConfig): SourcePluginConfig? = withContext(Dispatchers.IO) {
|
suspend fun checkForUpdates(c: SourcePluginConfig): SourcePluginConfig? = withContext(Dispatchers.IO) {
|
||||||
val sourceUrl = c.sourceUrl ?: return@withContext null;
|
val sourceUrl = c.sourceUrl ?: return@withContext null;
|
||||||
|
|
||||||
Logger.i(TAG, "Check for source updates '${c.name}'.");
|
Logger.i(TAG, "Check for source updates '${c.name}'.");
|
||||||
|
|||||||
@@ -6,12 +6,10 @@ import android.view.View
|
|||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
import androidx.lifecycle.findViewTreeLifecycleOwner
|
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.constructs.Event0
|
import com.futo.platformplayer.constructs.Event0
|
||||||
import com.futo.platformplayer.dp
|
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.states.Announcement
|
import com.futo.platformplayer.states.Announcement
|
||||||
import com.futo.platformplayer.states.AnnouncementType
|
import com.futo.platformplayer.states.AnnouncementType
|
||||||
@@ -162,6 +160,10 @@ class AnnouncementView : LinearLayout {
|
|||||||
_textClose.visibility = View.VISIBLE;
|
_textClose.visibility = View.VISIBLE;
|
||||||
_textNever.visibility = View.VISIBLE;
|
_textNever.visibility = View.VISIBLE;
|
||||||
}
|
}
|
||||||
|
AnnouncementType.ONGOING -> {
|
||||||
|
_textClose.visibility = View.GONE;
|
||||||
|
_textNever.visibility = View.GONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (announcement.time != null) {
|
if (announcement.time != null) {
|
||||||
|
|||||||
+212
@@ -0,0 +1,212 @@
|
|||||||
|
package com.futo.platformplayer.views.notification
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.fragment.mainactivity.main.MainFragment
|
||||||
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import com.futo.platformplayer.states.Announcement
|
||||||
|
import com.futo.platformplayer.states.AnnouncementType
|
||||||
|
import com.futo.platformplayer.states.SessionAnnouncement
|
||||||
|
import com.futo.platformplayer.states.StateAnnouncement
|
||||||
|
import com.futo.platformplayer.states.StateApp
|
||||||
|
import com.futo.platformplayer.views.AnyAdapterView
|
||||||
|
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||||
|
import com.futo.platformplayer.views.LoaderView
|
||||||
|
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class NotificationOverlayView: ConstraintLayout {
|
||||||
|
|
||||||
|
lateinit var recycler: RecyclerView;
|
||||||
|
var adapterNotifications: AnyAdapterView<Announcement, ViewHolder>;
|
||||||
|
|
||||||
|
constructor(context: Context) : super(context) {
|
||||||
|
inflate(context, R.layout.overlay_notifications, this)
|
||||||
|
|
||||||
|
recycler = findViewById<RecyclerView>(R.id.container_notifications);
|
||||||
|
adapterNotifications = recycler.asAny<Announcement, ViewHolder>(RecyclerView.VERTICAL, false, {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onShown(parameter: Any?) {
|
||||||
|
val announcements = StateAnnouncement.instance.getVisibleAnnouncements();
|
||||||
|
adapterNotifications.adapter.setData(announcements);
|
||||||
|
|
||||||
|
StateAnnouncement.instance.onAnnouncementChanged.subscribe(this) {
|
||||||
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
|
||||||
|
Logger.i("NotificationOverlayView", "Announcements Changed");
|
||||||
|
val adapter = adapterNotifications;
|
||||||
|
val announcements = StateAnnouncement.instance.getVisibleAnnouncements();
|
||||||
|
adapter.adapter.setData(announcements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onResume() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPause() {
|
||||||
|
StateAnnouncement.instance.onAnnouncementChanged.remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<Announcement>(
|
||||||
|
LayoutInflater.from(_viewGroup.context).inflate(
|
||||||
|
R.layout.list_announcement,
|
||||||
|
_viewGroup, false)) {
|
||||||
|
|
||||||
|
protected var _announcement: Announcement? = null;
|
||||||
|
protected val _textName: TextView
|
||||||
|
protected val _textMetadata: TextView;
|
||||||
|
protected val _icon: ImageView;
|
||||||
|
protected val _buttonIgnore: ImageView
|
||||||
|
protected val _buttonNever: LinearLayout
|
||||||
|
protected val _buttonAction: LinearLayout
|
||||||
|
protected val _buttonActionText: TextView
|
||||||
|
protected val _buttonExtra: LinearLayout
|
||||||
|
protected val _buttonExtraText: TextView
|
||||||
|
protected val _loader: LoaderView;
|
||||||
|
|
||||||
|
init {
|
||||||
|
_textName = _view.findViewById(R.id.text_name);
|
||||||
|
_textMetadata = _view.findViewById(R.id.text_metadata);
|
||||||
|
_buttonIgnore = _view.findViewById(R.id.button_ignore);
|
||||||
|
_buttonNever = _view.findViewById(R.id.button_never);
|
||||||
|
_buttonAction = _view.findViewById(R.id.button_action);
|
||||||
|
_buttonActionText = _view.findViewById(R.id.button_action_text);
|
||||||
|
_buttonExtra = _view.findViewById(R.id.button_extra);
|
||||||
|
_buttonExtraText = _view.findViewById(R.id.button_extra_text);
|
||||||
|
_icon = _view.findViewById(R.id.icon);
|
||||||
|
_loader = _view.findViewById(R.id.loader);
|
||||||
|
|
||||||
|
_buttonIgnore.setOnClickListener {
|
||||||
|
_announcement.let {
|
||||||
|
StateAnnouncement.instance.closeAnnouncement(it?.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_buttonNever.setOnClickListener {
|
||||||
|
_announcement.let {
|
||||||
|
StateAnnouncement.instance.neverAnnouncement(it?.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_buttonExtra.setOnClickListener {
|
||||||
|
_announcement.let {
|
||||||
|
StateAnnouncement.instance.actionAnnouncement(it?.id, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_buttonAction.setOnClickListener {
|
||||||
|
_announcement.let {
|
||||||
|
StateAnnouncement.instance.actionAnnouncement(it?.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override fun bind(value: Announcement) {
|
||||||
|
_announcement = value;
|
||||||
|
|
||||||
|
_textName.text = value.title;
|
||||||
|
_textMetadata.text = value.msg;
|
||||||
|
|
||||||
|
if(value is SessionAnnouncement) {
|
||||||
|
if(value.icon != null) {
|
||||||
|
value.icon.setImageView(_icon);
|
||||||
|
_icon.visibility = View.VISIBLE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_icon.visibility = View.GONE;
|
||||||
|
if(value.extraActionName != null && value.extraActionId != null) {
|
||||||
|
_buttonExtraText.text = value.extraActionName;
|
||||||
|
_buttonExtra.visibility = View.VISIBLE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_buttonExtra.visibility = View.GONE;
|
||||||
|
|
||||||
|
if(value.announceType == AnnouncementType.ONGOING) {
|
||||||
|
_buttonIgnore.visibility = View.GONE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_buttonIgnore.visibility = View.VISIBLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_buttonExtra.visibility = View.GONE;
|
||||||
|
_icon.visibility = View.GONE;
|
||||||
|
_buttonIgnore.visibility = View.VISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(value.announceType == AnnouncementType.ONGOING) {
|
||||||
|
_loader.visibility = View.VISIBLE;
|
||||||
|
_loader.start();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_loader.visibility = View.GONE;
|
||||||
|
_loader.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
_buttonNever.visibility =
|
||||||
|
if (value.announceType == AnnouncementType.RECURRING || value.announceType == AnnouncementType.SESSION_RECURRING)
|
||||||
|
View.VISIBLE
|
||||||
|
else
|
||||||
|
View.GONE;
|
||||||
|
|
||||||
|
_buttonAction.visibility =
|
||||||
|
if(value.actionId != null && value.actionName != null)
|
||||||
|
View.VISIBLE;
|
||||||
|
else View.GONE;
|
||||||
|
|
||||||
|
if(value.actionId != null && value.actionName != null) {
|
||||||
|
_buttonActionText.text = value.actionName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Frag : MainFragment() {
|
||||||
|
override val isMainView : Boolean = true;
|
||||||
|
override val isTab: Boolean = true;
|
||||||
|
override val hasBottomBar: Boolean get() = true;
|
||||||
|
|
||||||
|
private var _view: NotificationOverlayView? = null;
|
||||||
|
|
||||||
|
override fun onShownWithView(parameter: Any?, isBack: Boolean) {
|
||||||
|
super.onShownWithView(parameter, isBack);
|
||||||
|
_view?.onShown(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
val view = NotificationOverlayView(requireContext());
|
||||||
|
_view = view;
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyMainView() {
|
||||||
|
super.onDestroyMainView();
|
||||||
|
_view = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
_view?.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
_view?.onPause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#2D63ED" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M160,760L160,680L240,680L240,400Q240,317 290,252.5Q340,188 420,168L420,140Q420,115 437.5,97.5Q455,80 480,80Q505,80 522.5,97.5Q540,115 540,140L540,168Q620,188 670,252.5Q720,317 720,400L720,680L800,680L800,760L160,760ZM480,880Q447,880 423.5,856.5Q400,833 400,800L560,800Q560,833 536.5,856.5Q513,880 480,880Z"/>
|
||||||
|
</vector>
|
||||||
@@ -38,11 +38,12 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!--
|
||||||
<com.futo.platformplayer.views.announcements.AnnouncementView
|
<com.futo.platformplayer.views.announcements.AnnouncementView
|
||||||
android:id="@+id/announcement_view"
|
android:id="@+id/announcement_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" /> -->
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/container_sort_by"
|
android:id="@+id/container_sort_by"
|
||||||
|
|||||||
@@ -46,6 +46,42 @@
|
|||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
app:srcCompat="@drawable/ic_cast_white_25dp" />
|
app:srcCompat="@drawable/ic_cast_white_25dp" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/button_notifs">
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_notifs_icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:contentDescription="@string/cd_button_notifs"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:paddingTop="11dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:clickable="false"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_notifications" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_notifs_count"
|
||||||
|
android:layout_width="18dp"
|
||||||
|
android:layout_height="18dp"
|
||||||
|
android:text="5"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:background="@drawable/background_primary_round_20dp"
|
||||||
|
android:layout_marginTop="3dp"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
|
android:clickable="false"
|
||||||
|
/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<!--Back Button-->
|
<!--Back Button-->
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/button_search"
|
android:id="@+id/button_search"
|
||||||
|
|||||||
@@ -30,11 +30,12 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!--
|
||||||
<com.futo.platformplayer.views.announcements.AnnouncementView
|
<com.futo.platformplayer.views.announcements.AnnouncementView
|
||||||
android:id="@+id/announcement_view"
|
android:id="@+id/announcement_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" /> -->
|
||||||
|
|
||||||
<com.futo.platformplayer.views.others.RadioGroupView
|
<com.futo.platformplayer.views.others.RadioGroupView
|
||||||
android:id="@+id/radio_group"
|
android:id="@+id/radio_group"
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginTop="0dp"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
|
android:id="@+id/root"
|
||||||
|
android:clickable="true"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:paddingRight="10dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/text_metadata" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="13dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_regular"
|
||||||
|
tools:text="Example Artist"
|
||||||
|
android:maxLines="1"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/icon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/button_ignore"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/text_metadata"
|
||||||
|
android:layout_marginStart="10dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_metadata"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:fontFamily="@font/inter_regular"
|
||||||
|
tools:text="3 videos"
|
||||||
|
android:maxLines="2"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/text_name"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/icon"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/button_ignore"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:layout_marginStart="10dp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/button_ignore"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:src="@drawable/ic_close"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/text_metadata" />
|
||||||
|
|
||||||
|
<com.futo.platformplayer.views.LoaderView
|
||||||
|
android:id="@+id/loader"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/text_metadata" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_buttons"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/text_metadata"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/separator"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_never"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/background_button_accent"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="5dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:paddingStart="28dp"
|
||||||
|
android:paddingEnd="28dp"
|
||||||
|
android:text="Never" />
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_extra"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/background_button_accent"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="5dp">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_extra_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:paddingStart="28dp"
|
||||||
|
android:paddingEnd="28dp"
|
||||||
|
android:text="Extra" />
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_action"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="5dp"
|
||||||
|
android:layout_marginRight="5dp"
|
||||||
|
android:background="@drawable/background_button_primary"
|
||||||
|
android:clickable="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_action_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Action"
|
||||||
|
android:textSize="14dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_regular"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:paddingStart="28dp"
|
||||||
|
android:paddingEnd="28dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/separator"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/container_buttons"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1px"
|
||||||
|
android:background="#181818" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<View
|
||||||
|
android:id="@+id/overlay_slide_up_menu_background"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="#C9000000" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/separator"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1px"
|
||||||
|
android:background="#181818" />
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/container_notifications"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/separator"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -896,6 +896,7 @@
|
|||||||
<string name="cd_creator_thumbnail">Creator thumbnail</string>
|
<string name="cd_creator_thumbnail">Creator thumbnail</string>
|
||||||
<string name="cd_button_clear_search">Clear search</string>
|
<string name="cd_button_clear_search">Clear search</string>
|
||||||
<string name="cd_button_search">Search</string>
|
<string name="cd_button_search">Search</string>
|
||||||
|
<string name="cd_button_notifs">Notifications</string>
|
||||||
<string name="cd_search_icon">Search icon</string>
|
<string name="cd_search_icon">Search icon</string>
|
||||||
<string name="cd_button_back">Back button</string>
|
<string name="cd_button_back">Back button</string>
|
||||||
<string name="cd_app_icon">App icon</string>
|
<string name="cd_app_icon">App icon</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user