mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-16 04:52:39 +02:00
Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay
This commit is contained in:
+175
-7
@@ -8,18 +8,25 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.Settings
|
import com.futo.platformplayer.Settings
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
import com.futo.platformplayer.activities.MainActivity
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.dp
|
import com.futo.platformplayer.dp
|
||||||
import com.futo.platformplayer.fragment.mainactivity.MainActivityFragment
|
import com.futo.platformplayer.fragment.mainactivity.MainActivityFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.*
|
import com.futo.platformplayer.fragment.mainactivity.main.*
|
||||||
@@ -27,6 +34,10 @@ import com.futo.platformplayer.logging.Logger
|
|||||||
import com.futo.platformplayer.states.StateApp
|
import com.futo.platformplayer.states.StateApp
|
||||||
import com.futo.platformplayer.states.StatePayment
|
import com.futo.platformplayer.states.StatePayment
|
||||||
import com.futo.platformplayer.states.StateSubscriptions
|
import com.futo.platformplayer.states.StateSubscriptions
|
||||||
|
import com.futo.platformplayer.views.AnyAdapterView
|
||||||
|
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||||
|
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||||
|
import com.futo.platformplayer.views.pills.RoundButton
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
@@ -69,9 +80,15 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
private val _inflater: LayoutInflater;
|
private val _inflater: LayoutInflater;
|
||||||
private val _subscribedActivity: MainActivity?;
|
private val _subscribedActivity: MainActivity?;
|
||||||
|
|
||||||
|
private val _containerMoreHeader: ConstraintLayout;
|
||||||
|
private val _toggleAirplaneMode: LinearLayout;
|
||||||
|
private val _togglePrivacy: LinearLayout;
|
||||||
|
|
||||||
private var _overlayMore: FrameLayout;
|
private var _overlayMore: FrameLayout;
|
||||||
private var _overlayMoreBackground: FrameLayout;
|
private var _overlayMoreBackground: FrameLayout;
|
||||||
private var _layoutMoreButtons: LinearLayout;
|
private var _layoutMoreButtons: RecyclerView;
|
||||||
|
private val _layoutMoreButtonItems = arrayListOf<MenuButtonItem>();
|
||||||
|
private var _layoutMoreButtonsAdapter: AnyAdapterView<MenuButtonItem, MenuButtonItemViewHolder>;
|
||||||
private var _layoutBottomBarButtons: LinearLayout;
|
private var _layoutBottomBarButtons: LinearLayout;
|
||||||
|
|
||||||
private var _moreVisible = false;
|
private var _moreVisible = false;
|
||||||
@@ -90,10 +107,71 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
_inflater = inflater;
|
_inflater = inflater;
|
||||||
inflater.inflate(R.layout.fragment_overview_bottom_bar, this);
|
inflater.inflate(R.layout.fragment_overview_bottom_bar, this);
|
||||||
|
|
||||||
|
_containerMoreHeader = findViewById(R.id.container_more_options);
|
||||||
|
_toggleAirplaneMode = findViewById(R.id.container_toggle_airplane);
|
||||||
|
_togglePrivacy = findViewById(R.id.container_toggle_privacy);
|
||||||
|
|
||||||
|
_toggleAirplaneMode.isVisible = false //TODO: Remove when airplane mode implemented
|
||||||
|
|
||||||
|
StateApp.instance.airplaneModeChanged.subscribe {
|
||||||
|
if(!StateApp.instance.airplaneMode)
|
||||||
|
_toggleAirplaneMode.setBackgroundResource(R.drawable.background_menu_toggle)
|
||||||
|
else
|
||||||
|
_toggleAirplaneMode.setBackgroundResource(R.drawable.background_menu_toggle_active)
|
||||||
|
}
|
||||||
|
if(!StateApp.instance.airplaneMode)
|
||||||
|
_toggleAirplaneMode.setBackgroundResource(R.drawable.background_menu_toggle)
|
||||||
|
else
|
||||||
|
_toggleAirplaneMode.setBackgroundResource(R.drawable.background_menu_toggle_active)
|
||||||
|
_toggleAirplaneMode.setOnClickListener {
|
||||||
|
if(StateApp.instance.airplaneMode) {
|
||||||
|
StateApp.instance.setAirMode(false);
|
||||||
|
UIDialogs.appToast("Airplane mode disabled");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
StateApp.instance.setAirMode(true);
|
||||||
|
UIDialogs.appToast("Airplane mode enabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateApp.instance.privateModeChanged.subscribe {
|
||||||
|
if(!StateApp.instance.privateMode)
|
||||||
|
_togglePrivacy.setBackgroundResource(R.drawable.background_menu_toggle)
|
||||||
|
else
|
||||||
|
_togglePrivacy.setBackgroundResource(R.drawable.background_menu_toggle_active)
|
||||||
|
}
|
||||||
|
if(!StateApp.instance.privateMode)
|
||||||
|
_togglePrivacy.setBackgroundResource(R.drawable.background_menu_toggle)
|
||||||
|
else
|
||||||
|
_togglePrivacy.setBackgroundResource(R.drawable.background_menu_toggle_active)
|
||||||
|
_togglePrivacy.setOnClickListener {
|
||||||
|
if(StateApp.instance.privateMode) {
|
||||||
|
StateApp.instance.setPrivacyMode(false);
|
||||||
|
UIDialogs.appToast("Privacy mode disabled");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
StateApp.instance.setPrivacyMode(true);
|
||||||
|
UIDialogs.appToast("Privacy mode enabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_overlayMore = findViewById(R.id.more_overlay);
|
_overlayMore = findViewById(R.id.more_overlay);
|
||||||
_overlayMoreBackground = findViewById(R.id.more_overlay_background);
|
_overlayMoreBackground = findViewById(R.id.more_overlay_background);
|
||||||
_layoutMoreButtons = findViewById(R.id.more_menu_buttons);
|
_layoutMoreButtons = findViewById(R.id.more_menu_buttons);
|
||||||
_layoutBottomBarButtons = findViewById(R.id.bottom_bar_buttons)
|
_layoutBottomBarButtons = findViewById(R.id.bottom_bar_buttons);
|
||||||
|
|
||||||
|
val totalWidthDp = resources.displayMetrics.widthPixels / resources.displayMetrics.density;
|
||||||
|
val columns = MenuButtonItemViewHolder.getAutoSizeColumns(totalWidthDp);
|
||||||
|
_layoutMoreButtonsAdapter = _layoutMoreButtons.asAny<MenuButtonItem, MenuButtonItemViewHolder>(_layoutMoreButtonItems,
|
||||||
|
RecyclerView.VERTICAL, false, { button ->
|
||||||
|
button.setAutoSize(totalWidthDp);
|
||||||
|
button.parentFragment = this@MenuBottomBarView._fragment;
|
||||||
|
button.onClick.subscribe {
|
||||||
|
setMoreVisible(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
val layoutManager = GridLayoutManager(context, columns, GridLayoutManager.VERTICAL, true);
|
||||||
|
_layoutMoreButtons.layoutManager = layoutManager;
|
||||||
|
|
||||||
_overlayMoreBackground.setOnClickListener { setMoreVisible(false); };
|
_overlayMoreBackground.setOnClickListener { setMoreVisible(false); };
|
||||||
|
|
||||||
@@ -120,6 +198,8 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setMoreVisible(visible: Boolean) {
|
private fun setMoreVisible(visible: Boolean) {
|
||||||
|
|
||||||
|
//TODO: issues with these bools
|
||||||
if (_moreVisibleAnimating) {
|
if (_moreVisibleAnimating) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -128,9 +208,12 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
val height = _moreButtons.firstOrNull()?.let {
|
val height = _moreButtons.firstOrNull()?.let {
|
||||||
it.height.toFloat() + (it.layoutParams as MarginLayoutParams).bottomMargin
|
it.height.toFloat() + (it.layoutParams as MarginLayoutParams).bottomMargin
|
||||||
} ?: return
|
} ?: return
|
||||||
|
*/
|
||||||
|
|
||||||
_moreVisibleAnimating = true
|
_moreVisibleAnimating = true
|
||||||
val moreOverlayBackground = _overlayMoreBackground
|
val moreOverlayBackground = _overlayMoreBackground
|
||||||
@@ -142,14 +225,17 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
moreOverlay.visibility = VISIBLE
|
moreOverlay.visibility = VISIBLE
|
||||||
val animations = arrayListOf<Animator>()
|
val animations = arrayListOf<Animator>()
|
||||||
animations.add(ObjectAnimator.ofFloat(moreOverlayBackground, "alpha", 0.0f, 1.0f).setDuration(duration))
|
animations.add(ObjectAnimator.ofFloat(moreOverlayBackground, "alpha", 0.0f, 1.0f).setDuration(duration))
|
||||||
|
animations.add(ObjectAnimator.ofFloat(_layoutMoreButtons, "alpha", 0.0f, 1.0f).setDuration(duration))
|
||||||
|
animations.add(ObjectAnimator.ofFloat(_containerMoreHeader, "alpha", 0.0f, 1.0f).setDuration(duration))
|
||||||
_bottomButtons.find { it.definition.id == 99 }?.let {
|
_bottomButtons.find { it.definition.id == 99 }?.let {
|
||||||
animations.add(ObjectAnimator.ofFloat(it, "alpha", 0.5f, 1.0f)
|
animations.add(ObjectAnimator.ofFloat(it, "alpha", 0.5f, 1.0f)
|
||||||
.setDuration(duration));
|
.setDuration(duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
animations.add(ObjectAnimator.ofFloat(_layoutMoreButtons, "translationY", resources.displayMetrics.heightPixels.toFloat(), 0.0f).setDuration(duration))
|
||||||
for ((index, button) in _moreButtons.withIndex()) {
|
for ((index, button) in _moreButtons.withIndex()) {
|
||||||
val i = _moreButtons.size - index
|
val i = _moreButtons.size - index
|
||||||
animations.add(ObjectAnimator.ofFloat(button, "translationY", height * staggerFactor * (i + 1), 0.0f).setDuration(duration))
|
//animations.add(ObjectAnimator.ofFloat(button, "translationY", height * staggerFactor * (i + 1), 0.0f).setDuration(duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
val animatorSet = AnimatorSet()
|
val animatorSet = AnimatorSet()
|
||||||
@@ -164,14 +250,21 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
animations
|
animations
|
||||||
.add(ObjectAnimator.ofFloat(moreOverlayBackground, "alpha", 1.0f, 0.0f)
|
.add(ObjectAnimator.ofFloat(moreOverlayBackground, "alpha", 1.0f, 0.0f)
|
||||||
.setDuration(duration))
|
.setDuration(duration))
|
||||||
|
animations
|
||||||
|
.add(ObjectAnimator.ofFloat(_layoutMoreButtons, "alpha", 1.0f, 0.0f)
|
||||||
|
.setDuration(duration))
|
||||||
|
animations
|
||||||
|
.add(ObjectAnimator.ofFloat(_containerMoreHeader, "alpha", 1.0f, 0.0f)
|
||||||
|
.setDuration(duration))
|
||||||
_bottomButtons.find { it.definition.id == 99 }?.let {
|
_bottomButtons.find { it.definition.id == 99 }?.let {
|
||||||
animations.add(ObjectAnimator.ofFloat(it, "alpha", 1.0f, 0.5f)
|
animations.add(ObjectAnimator.ofFloat(it, "alpha", 1.0f, 0.5f)
|
||||||
.setDuration(duration));
|
.setDuration(duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
animations.add(ObjectAnimator.ofFloat(_layoutMoreButtons, "translationY", 0.0f, resources.displayMetrics.heightPixels.toFloat()).setDuration(duration))
|
||||||
for ((index, button) in _moreButtons.withIndex()) {
|
for ((index, button) in _moreButtons.withIndex()) {
|
||||||
val i = _moreButtons.size - index
|
val i = _moreButtons.size - index
|
||||||
animations.add(ObjectAnimator.ofFloat(button, "translationY", 0.0f, height * staggerFactor * (i + 1)).setDuration(duration))
|
//animations.add(ObjectAnimator.ofFloat(button, "translationY", 0.0f, height * staggerFactor * (i + 1)).setDuration(duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
val animatorSet = AnimatorSet()
|
val animatorSet = AnimatorSet()
|
||||||
@@ -183,11 +276,12 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
animatorSet.playTogether(animations)
|
animatorSet.playTogether(animations)
|
||||||
animatorSet.start()
|
animatorSet.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateBottomMenuButtons(buttons: MutableList<ButtonDefinition>, hasMore: Boolean) {
|
private fun updateBottomMenuButtons(buttons: MutableList<ButtonDefinition>, hasMore: Boolean) {
|
||||||
if (hasMore) {
|
if (hasMore) {
|
||||||
buttons.add(ButtonDefinition(99, R.drawable.ic_more, R.drawable.ic_more, R.string.more, canToggle = false, { false }, { setMoreVisible(true) }))
|
buttons.add(ButtonDefinition(99, R.drawable.ic_more, R.drawable.ic_more, R.string.more, canToggle = false, { false }, { setMoreVisible(!_moreVisible) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
_bottomButtons.clear();
|
_bottomButtons.clear();
|
||||||
@@ -252,7 +346,9 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
insertedButtons++;
|
insertedButtons++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val newButtons = mutableListOf<MenuButtonItem>();
|
||||||
for (data in buttons) {
|
for (data in buttons) {
|
||||||
|
/*
|
||||||
val button = MenuButton(context, data, _fragment, true);
|
val button = MenuButton(context, data, _fragment, true);
|
||||||
button.setOnClickListener {
|
button.setOnClickListener {
|
||||||
updateMenuIcons()
|
updateMenuIcons()
|
||||||
@@ -262,7 +358,12 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
|
|
||||||
_moreButtons.add(button);
|
_moreButtons.add(button);
|
||||||
_layoutMoreButtons.addView(button);
|
_layoutMoreButtons.addView(button);
|
||||||
|
*/
|
||||||
|
val buttonItem = MenuButtonItem(data);
|
||||||
|
newButtons.add(buttonItem);
|
||||||
}
|
}
|
||||||
|
_layoutMoreButtonsAdapter.setData(newButtons);
|
||||||
|
_layoutMoreButtonsAdapter.notifyContentChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateMenuIcons() {
|
private fun updateMenuIcons() {
|
||||||
@@ -350,6 +451,71 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MenuButtonItem(val def: ButtonDefinition);
|
||||||
|
class MenuButtonItemViewHolder(private val _viewGroup: ViewGroup): AnyAdapter.AnyViewHolder<MenuButtonItem>(
|
||||||
|
LayoutInflater.from(_viewGroup.context).inflate(R.layout.list_menu_tile,
|
||||||
|
_viewGroup, false)) {
|
||||||
|
|
||||||
|
val onClick = Event1<MenuButtonItem>();
|
||||||
|
|
||||||
|
val root: ConstraintLayout;
|
||||||
|
val imageIcon: ImageView;
|
||||||
|
val textName: TextView;
|
||||||
|
|
||||||
|
|
||||||
|
var button: MenuButtonItem? = null;
|
||||||
|
|
||||||
|
var parentFragment: MenuBottomBarFragment? = null;
|
||||||
|
|
||||||
|
init {
|
||||||
|
root = _view.findViewById(R.id.root);
|
||||||
|
imageIcon = _view.findViewById(R.id.image_icon);
|
||||||
|
textName = _view.findViewById(R.id.text_name);
|
||||||
|
|
||||||
|
root.setOnClickListener {
|
||||||
|
button?.let {
|
||||||
|
it.def.action(parentFragment ?: return@let);
|
||||||
|
onClick.emit(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun bind(value: MenuButtonItem) {
|
||||||
|
button = value;
|
||||||
|
textName.text = _view.context.getString(value.def.string);
|
||||||
|
imageIcon.setImageResource(value.def.iconActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setWidth(dp: Int) {
|
||||||
|
root.updateLayoutParams {
|
||||||
|
this.width = (dp - 6).dp(_viewGroup.context.resources);
|
||||||
|
this.height = (dp - 6).dp(_viewGroup.context.resources);
|
||||||
|
}
|
||||||
|
imageIcon.updateLayoutParams {
|
||||||
|
this.width = (dp - 54).dp(_viewGroup.context.resources);
|
||||||
|
this.height = (dp - 54).dp(_viewGroup.context.resources);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAutoSize(totalWidth: Float) {
|
||||||
|
val dpWidth = totalWidth;
|
||||||
|
val columns = Math.max(((dpWidth) / viewWidthDp).toInt(), 1);
|
||||||
|
val remainder = dpWidth - columns * viewWidthDp;
|
||||||
|
val targetSize = viewWidthDp + (remainder / columns).toInt();
|
||||||
|
setWidth(targetSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val viewWidthDp = 90;
|
||||||
|
fun getAutoSizeColumns(totalWidth: Float): Int {
|
||||||
|
val dpWidth = totalWidth;
|
||||||
|
val columns = Math.max(((dpWidth) / viewWidthDp).toInt(), 1);
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
class MenuButton: LinearLayout {
|
class MenuButton: LinearLayout {
|
||||||
val definition: ButtonDefinition;
|
val definition: ButtonDefinition;
|
||||||
|
|
||||||
@@ -413,7 +579,9 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
ButtonDefinition(1, R.drawable.ic_subscriptions, R.drawable.ic_subscriptions_filled, R.string.subscriptions, canToggle = true, { it.currentMain is SubscriptionsFeedFragment }, { it.navigate<SubscriptionsFeedFragment>(withHistory = false) }),
|
ButtonDefinition(1, R.drawable.ic_subscriptions, R.drawable.ic_subscriptions_filled, R.string.subscriptions, canToggle = true, { it.currentMain is SubscriptionsFeedFragment }, { it.navigate<SubscriptionsFeedFragment>(withHistory = false) }),
|
||||||
ButtonDefinition(12, R.drawable.ic_library, R.drawable.ic_library, R.string.library, canToggle = false, { it.currentMain is LibraryFragment }, { it.navigate<LibraryFragment>(withHistory = false) }),
|
//if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P)
|
||||||
|
ButtonDefinition(12, R.drawable.ic_library, R.drawable.ic_library, R.string.library, canToggle = false, { it.currentMain is LibraryFragment }, { it.navigate<LibraryFragment>(withHistory = false) })
|
||||||
|
,//else null,
|
||||||
ButtonDefinition(2, R.drawable.ic_creators, R.drawable.ic_creators_filled, R.string.creators, canToggle = false, { it.currentMain is CreatorsFragment }, { it.navigate<CreatorsFragment>(withHistory = false) }),
|
ButtonDefinition(2, R.drawable.ic_creators, R.drawable.ic_creators_filled, R.string.creators, canToggle = false, { it.currentMain is CreatorsFragment }, { it.navigate<CreatorsFragment>(withHistory = false) }),
|
||||||
ButtonDefinition(3, R.drawable.ic_sources, R.drawable.ic_sources_filled, R.string.sources, canToggle = false, { it.currentMain is SourcesFragment }, { it.navigate<SourcesFragment>(withHistory = false) }),
|
ButtonDefinition(3, R.drawable.ic_sources, R.drawable.ic_sources_filled, R.string.sources, canToggle = false, { it.currentMain is SourcesFragment }, { it.navigate<SourcesFragment>(withHistory = false) }),
|
||||||
ButtonDefinition(4, R.drawable.ic_playlist, R.drawable.ic_playlist_filled, R.string.playlists, canToggle = false, { it.currentMain is PlaylistsFragment }, { it.navigate<PlaylistsFragment>(withHistory = false) }),
|
ButtonDefinition(4, R.drawable.ic_playlist, R.drawable.ic_playlist_filled, R.string.playlists, canToggle = false, { it.currentMain is PlaylistsFragment }, { it.navigate<PlaylistsFragment>(withHistory = false) }),
|
||||||
@@ -451,7 +619,7 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
|||||||
//96 is reserved for privacy button
|
//96 is reserved for privacy button
|
||||||
//98 is reserved for buy button
|
//98 is reserved for buy button
|
||||||
//99 is reserved for more button
|
//99 is reserved for more button
|
||||||
);
|
).filterNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ButtonDefinition(
|
data class ButtonDefinition(
|
||||||
|
|||||||
+17
-1
@@ -14,6 +14,7 @@ import android.widget.ImageView
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.widget.AppCompatImageView
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@@ -22,6 +23,7 @@ import androidx.recyclerview.widget.GridLayoutManager
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
import com.futo.platformplayer.UISlideOverlays
|
import com.futo.platformplayer.UISlideOverlays
|
||||||
@@ -359,7 +361,21 @@ class LibraryArtistFragment : MainFragment() {
|
|||||||
(_viewPager.adapter as ArtistViewPagerAdapter).artist = channel
|
(_viewPager.adapter as ArtistViewPagerAdapter).artist = channel
|
||||||
|
|
||||||
|
|
||||||
_viewPager.adapter!!.notifyDataSetChanged()
|
_viewPager.adapter!!.notifyDataSetChanged();
|
||||||
|
|
||||||
|
val artistThumbnail = channel.getThumbnailOrAlbum();
|
||||||
|
if(artistThumbnail != null) {
|
||||||
|
_creatorThumbnail.isVisible = true;
|
||||||
|
_creatorThumbnail.setThumbnail(channel.getThumbnailOrAlbum(), true, true);
|
||||||
|
Glide.with(_imageBanner)
|
||||||
|
.load(artistThumbnail)
|
||||||
|
.into(_imageBanner);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_creatorThumbnail.isVisible = false;
|
||||||
|
Glide.with(_imageBanner).clear(_imageBanner);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.channel = channel
|
this.channel = channel
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -86,6 +86,7 @@ class LibraryFilesFragment : MainFragment() {
|
|||||||
}
|
}
|
||||||
fun loadTop() {
|
fun loadTop() {
|
||||||
var initialDirectories = listOf<FileEntry>();
|
var initialDirectories = listOf<FileEntry>();
|
||||||
|
var path = "";
|
||||||
if(root == null) {
|
if(root == null) {
|
||||||
initialDirectories = StateLibrary.instance.getFileDirectories();
|
initialDirectories = StateLibrary.instance.getFileDirectories();
|
||||||
if (initialDirectories.size == 0) {
|
if (initialDirectories.size == 0) {
|
||||||
@@ -109,9 +110,10 @@ class LibraryFilesFragment : MainFragment() {
|
|||||||
it.isVisible = false;
|
it.isVisible = false;
|
||||||
}
|
}
|
||||||
initialDirectories = root?.getSubFiles() ?: listOf();
|
initialDirectories = root?.getSubFiles() ?: listOf();
|
||||||
|
path = root?.path ?: "";
|
||||||
}
|
}
|
||||||
navStack.clear();
|
navStack.clear();
|
||||||
val entry = FileStack("", initialDirectories);
|
val entry = FileStack(path, initialDirectories);
|
||||||
navStack.add(entry);
|
navStack.add(entry);
|
||||||
openDirectory(navStack.last());
|
openDirectory(navStack.last());
|
||||||
fragment.topBar?.let {
|
fragment.topBar?.let {
|
||||||
|
|||||||
+128
-35
@@ -2,6 +2,7 @@ package com.futo.platformplayer.fragment.mainactivity.main
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@@ -11,11 +12,13 @@ import android.view.ViewGroup
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.collection.emptyLongSet
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
@@ -34,6 +37,7 @@ import com.futo.platformplayer.views.AnyInsertedAdapterView
|
|||||||
import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithTop
|
import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithTop
|
||||||
import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithViews
|
import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithViews
|
||||||
import com.futo.platformplayer.views.LibrarySection
|
import com.futo.platformplayer.views.LibrarySection
|
||||||
|
import com.futo.platformplayer.views.NoResultsView
|
||||||
import com.futo.platformplayer.views.adapters.AnyAdapter
|
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||||
import com.futo.platformplayer.views.adapters.InsertedViewAdapter
|
import com.futo.platformplayer.views.adapters.InsertedViewAdapter
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
|
||||||
@@ -41,6 +45,9 @@ import com.futo.platformplayer.views.adapters.viewholders.ArtistTileViewHolder
|
|||||||
import com.futo.platformplayer.views.adapters.viewholders.FileViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.FileViewHolder
|
||||||
import com.futo.platformplayer.views.adapters.viewholders.LocalVideoTileViewHolder
|
import com.futo.platformplayer.views.adapters.viewholders.LocalVideoTileViewHolder
|
||||||
import com.futo.platformplayer.views.buttons.BigButton
|
import com.futo.platformplayer.views.buttons.BigButton
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import okhttp3.Dispatcher
|
||||||
|
|
||||||
|
|
||||||
class LibraryFragment : MainFragment() {
|
class LibraryFragment : MainFragment() {
|
||||||
@@ -146,11 +153,12 @@ class LibraryFragment : MainFragment() {
|
|||||||
var sectionAlbums: LibrarySection;
|
var sectionAlbums: LibrarySection;
|
||||||
var sectionVideos: LibrarySection;
|
var sectionVideos: LibrarySection;
|
||||||
var sectionFiles: LibrarySection;
|
var sectionFiles: LibrarySection;
|
||||||
|
var noContent: NoResultsView;
|
||||||
//var buttonFiles: BigButton;
|
//var buttonFiles: BigButton;
|
||||||
|
|
||||||
val recycler: RecyclerView;
|
val recycler: RecyclerView;
|
||||||
|
|
||||||
val adapterFiles: AnyInsertedAdapterView<FileEntry, FileViewHolder>;
|
var adapterFiles: AnyInsertedAdapterView<FileEntry, FileViewHolder>? = null;
|
||||||
|
|
||||||
//var metaInfo: TextView;
|
//var metaInfo: TextView;
|
||||||
|
|
||||||
@@ -186,6 +194,9 @@ class LibraryFragment : MainFragment() {
|
|||||||
//buttonFiles = findViewById<BigButton>(R.id.button_files);
|
//buttonFiles = findViewById<BigButton>(R.id.button_files);
|
||||||
//metaInfo = findViewById(R.id.meta_info);
|
//metaInfo = findViewById(R.id.meta_info);
|
||||||
|
|
||||||
|
noContent = NoResultsView(context, "No directories", "No directories have been added.\nAdd them using the (+) icon.", -1, listOf());
|
||||||
|
noContent.isVisible = false;
|
||||||
|
|
||||||
this.allowMusic = allowMusic ?: false;
|
this.allowMusic = allowMusic ?: false;
|
||||||
this.allowVideo = allowVideo ?: false;
|
this.allowVideo = allowVideo ?: false;
|
||||||
|
|
||||||
@@ -195,14 +206,6 @@ class LibraryFragment : MainFragment() {
|
|||||||
else
|
else
|
||||||
fragment.requestPermissionMusic();
|
fragment.requestPermissionMusic();
|
||||||
});
|
});
|
||||||
val adapterArtists = sectionArtists.getAnyAdapter<Artist, ArtistTileViewHolder>({
|
|
||||||
it.onClick.subscribe {
|
|
||||||
if(it != null)
|
|
||||||
fragment.navigate<LibraryArtistFragment>(it);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
val artists = StateLibrary.instance.getArtists(ArtistOrdering.TrackCount);
|
|
||||||
adapterArtists.setData(artists);
|
|
||||||
|
|
||||||
sectionAlbums.setSection("Albums", {
|
sectionAlbums.setSection("Albums", {
|
||||||
if(this.allowMusic)
|
if(this.allowMusic)
|
||||||
@@ -210,14 +213,6 @@ class LibraryFragment : MainFragment() {
|
|||||||
else
|
else
|
||||||
fragment.requestPermissionMusic();
|
fragment.requestPermissionMusic();
|
||||||
});
|
});
|
||||||
val adapterAlbums = sectionAlbums.getAnyAdapter<Album, AlbumTileViewHolder>({
|
|
||||||
it.onClick.subscribe {
|
|
||||||
if(it != null)
|
|
||||||
fragment.navigate<LibraryAlbumFragment>(it);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
val albums = StateLibrary.instance.getAlbums();
|
|
||||||
adapterAlbums.setData(albums);
|
|
||||||
|
|
||||||
|
|
||||||
sectionVideos.setSection("Videos", {
|
sectionVideos.setSection("Videos", {
|
||||||
@@ -226,21 +221,118 @@ class LibraryFragment : MainFragment() {
|
|||||||
else
|
else
|
||||||
fragment.requestPermissionVideo();
|
fragment.requestPermissionVideo();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
reloadLibraryUI();
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
buttonFiles.onClick.subscribe {
|
||||||
|
fragment.navigate<LibraryFilesFragment>()
|
||||||
|
} */
|
||||||
|
//buttonFiles.setButtonEnabled(false);
|
||||||
|
setMusicPermissions(allowMusic ?: false);
|
||||||
|
setVideoPermissions(allowVideo ?: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reloadFiles() {
|
||||||
|
val files = StateLibrary.instance.getFileDirectories();
|
||||||
|
adapterFiles?.setData(files);
|
||||||
|
if(files.size == 0) {
|
||||||
|
noContent.isVisible = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
noContent.isVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reloadLibraryUI() {
|
||||||
|
|
||||||
|
val adapterAlbums = sectionAlbums.getAnyAdapter<Album, AlbumTileViewHolder>({
|
||||||
|
it.onClick.subscribe {
|
||||||
|
if(it != null)
|
||||||
|
fragment.navigate<LibraryAlbumFragment>(it);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
val adapterArtists = sectionArtists.getAnyAdapter<Artist, ArtistTileViewHolder>({
|
||||||
|
it.onClick.subscribe {
|
||||||
|
if(it != null)
|
||||||
|
fragment.navigate<LibraryArtistFragment>(it);
|
||||||
|
}
|
||||||
|
});
|
||||||
val adapterVideos = sectionVideos.getAnyAdapter<IPlatformVideo, LocalVideoTileViewHolder>({
|
val adapterVideos = sectionVideos.getAnyAdapter<IPlatformVideo, LocalVideoTileViewHolder>({
|
||||||
it.onClick.subscribe {
|
it.onClick.subscribe {
|
||||||
if(it != null)
|
if(it != null)
|
||||||
fragment.navigate<VideoDetailFragment>(it);
|
fragment.navigate<VideoDetailFragment>(it);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
val videos = StateLibrary.instance.getRecentVideos(null, 20);
|
|
||||||
adapterVideos.setData(videos);
|
if(this.allowMusic) {
|
||||||
|
val artists = StateLibrary.instance.getArtists(ArtistOrdering.TrackCount);
|
||||||
|
adapterArtists.setData(artists);
|
||||||
|
if (artists.size == 0)
|
||||||
|
sectionArtists.setEmpty(
|
||||||
|
"No artists",
|
||||||
|
"No artists were found on your device",
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
else
|
||||||
|
sectionArtists.clearEmpty();
|
||||||
|
}
|
||||||
|
else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
sectionAlbums.isVisible = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sectionArtists.setEmpty(
|
||||||
|
"No Music Permissions",
|
||||||
|
"You have not granted music access permissions to Grayjay",
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.allowMusic) {
|
||||||
|
val albums = StateLibrary.instance.getAlbums();
|
||||||
|
adapterAlbums.setData(albums);
|
||||||
|
if (albums.size == 0)
|
||||||
|
sectionAlbums.setEmpty("No albums", "No albums were found on your device", -1);
|
||||||
|
else
|
||||||
|
sectionAlbums.clearEmpty();
|
||||||
|
}
|
||||||
|
else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
sectionArtists.isVisible = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sectionAlbums.setEmpty(
|
||||||
|
"No Music Permissions",
|
||||||
|
"You have not granted music access permissions to Grayjay",
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.allowVideo) {
|
||||||
|
val videos = StateLibrary.instance.getRecentVideos(null, 20);
|
||||||
|
adapterVideos.setData(videos);
|
||||||
|
if (videos.size == 0)
|
||||||
|
sectionVideos.setEmpty("No videos", "No videos were found on your device", -1);
|
||||||
|
else
|
||||||
|
sectionVideos.clearEmpty();
|
||||||
|
}
|
||||||
|
else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
sectionVideos.isVisible = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sectionVideos.setEmpty(
|
||||||
|
"No Video Permissions",
|
||||||
|
"You have not granted video access permissions to Grayjay",
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
adapterFiles = recycler.asAnyWithViews<FileEntry, FileViewHolder>(
|
adapterFiles = recycler.asAnyWithViews<FileEntry, FileViewHolder>(
|
||||||
arrayListOf(
|
arrayListOf(
|
||||||
sectionArtists,
|
sectionArtists,
|
||||||
sectionAlbums,
|
sectionAlbums,
|
||||||
sectionVideos,
|
sectionVideos,
|
||||||
sectionFiles
|
sectionFiles,
|
||||||
|
noContent
|
||||||
),
|
),
|
||||||
arrayListOf(View(context).apply { this.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 20.dp(resources)) }),
|
arrayListOf(View(context).apply { this.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 20.dp(resources)) }),
|
||||||
RecyclerView.VERTICAL, false, {
|
RecyclerView.VERTICAL, false, {
|
||||||
@@ -257,23 +349,8 @@ class LibraryFragment : MainFragment() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
reloadFiles();
|
reloadFiles();
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
buttonFiles.onClick.subscribe {
|
|
||||||
fragment.navigate<LibraryFilesFragment>()
|
|
||||||
} */
|
|
||||||
//buttonFiles.setButtonEnabled(false);
|
|
||||||
setMusicPermissions(allowMusic ?: false);
|
|
||||||
setVideoPermissions(allowVideo ?: false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reloadFiles() {
|
|
||||||
val files = StateLibrary.instance.getFileDirectories();
|
|
||||||
adapterFiles.setData(files);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun setMusicPermissions(access: Boolean) {
|
fun setMusicPermissions(access: Boolean) {
|
||||||
allowMusic = access;
|
allowMusic = access;
|
||||||
sectionAlbums.setContentEmptyMessage(R.drawable.ic_library, "No mediastore permissions");
|
sectionAlbums.setContentEmptyMessage(R.drawable.ic_library, "No mediastore permissions");
|
||||||
@@ -283,6 +360,10 @@ class LibraryFragment : MainFragment() {
|
|||||||
// if(!allowMusic) "You did not give access to local music, so these options are disabled" else null,
|
// if(!allowMusic) "You did not give access to local music, so these options are disabled" else null,
|
||||||
// if(!allowVideo) "You did not give access to local videos, so these options are disabled" else null
|
// if(!allowVideo) "You did not give access to local videos, so these options are disabled" else null
|
||||||
//).filterNotNull().joinToString("\n");
|
//).filterNotNull().joinToString("\n");
|
||||||
|
|
||||||
|
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
reloadLibraryUI();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fun setVideoPermissions(access: Boolean) {
|
fun setVideoPermissions(access: Boolean) {
|
||||||
allowVideo = access;
|
allowVideo = access;
|
||||||
@@ -291,10 +372,22 @@ class LibraryFragment : MainFragment() {
|
|||||||
// if(!allowMusic) "You did not give access to local music, so these options are disabled" else null,
|
// if(!allowMusic) "You did not give access to local music, so these options are disabled" else null,
|
||||||
// if(!allowVideo) "You did not give access to local videos, so these options are disabled" else null
|
// if(!allowVideo) "You did not give access to local videos, so these options are disabled" else null
|
||||||
//).filterNotNull().joinToString("\n");
|
//).filterNotNull().joinToString("\n");
|
||||||
|
// }
|
||||||
|
|
||||||
|
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
reloadLibraryUI();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onShown() {
|
fun onShown() {
|
||||||
|
if(didShowAlpha)
|
||||||
|
return;
|
||||||
|
didShowAlpha = true;
|
||||||
UIDialogs.appToast("Library is in alpha\nImprovements are coming to local media playback.")
|
UIDialogs.appToast("Library is in alpha\nImprovements are coming to local media playback.")
|
||||||
}
|
}
|
||||||
|
companion object {
|
||||||
|
var didShowAlpha: Boolean = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+3
-1
@@ -1753,7 +1753,9 @@ class VideoDetailView : ConstraintLayout {
|
|||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.e(TAG, "Failed to get polycentric likes/dislikes.", e);
|
Logger.e(TAG, "Failed to get polycentric likes/dislikes.", e);
|
||||||
_rating.visibility = View.GONE;
|
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
_rating.visibility = View.GONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,20 @@ class StateApp {
|
|||||||
|
|
||||||
val sessionId = UUID.randomUUID().toString();
|
val sessionId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
|
||||||
|
var airplaneMode: Boolean = false
|
||||||
|
get(){
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
private set(value) {
|
||||||
|
field = value;
|
||||||
|
}
|
||||||
|
val airplaneModeChanged = Event1<Boolean>();
|
||||||
|
fun setAirMode(value: Boolean) {
|
||||||
|
airplaneMode = value;
|
||||||
|
airplaneModeChanged.emit(airplaneMode);
|
||||||
|
}
|
||||||
|
|
||||||
var privateMode: Boolean = false
|
var privateMode: Boolean = false
|
||||||
get(){
|
get(){
|
||||||
return field;
|
return field;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.content.ContentUris
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.MediaStore.Audio.Artists
|
import android.provider.MediaStore.Audio.Artists
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
@@ -36,6 +37,8 @@ import java.io.File
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.ConcurrentMap
|
||||||
|
|
||||||
|
|
||||||
class StateLibrary {
|
class StateLibrary {
|
||||||
@@ -192,6 +195,8 @@ class StateLibrary {
|
|||||||
|
|
||||||
private var _cacheBucketNames: List<Bucket>? = null;
|
private var _cacheBucketNames: List<Bucket>? = null;
|
||||||
fun getVideoBucketNames(): List<Bucket> {
|
fun getVideoBucketNames(): List<Bucket> {
|
||||||
|
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
|
||||||
|
return listOf();
|
||||||
if(_cacheBucketNames != null)
|
if(_cacheBucketNames != null)
|
||||||
return _cacheBucketNames ?: listOf();
|
return _cacheBucketNames ?: listOf();
|
||||||
try {
|
try {
|
||||||
@@ -236,7 +241,6 @@ class StateLibrary {
|
|||||||
val PROJECTION_VIDEO = arrayOf(
|
val PROJECTION_VIDEO = arrayOf(
|
||||||
MediaStore.Video.Media._ID,
|
MediaStore.Video.Media._ID,
|
||||||
MediaStore.Video.Media.DISPLAY_NAME,
|
MediaStore.Video.Media.DISPLAY_NAME,
|
||||||
MediaStore.Video.Media.AUTHOR,
|
|
||||||
MediaStore.Video.Media.DATE_ADDED,
|
MediaStore.Video.Media.DATE_ADDED,
|
||||||
MediaStore.Video.Media.MIME_TYPE,
|
MediaStore.Video.Media.MIME_TYPE,
|
||||||
MediaStore.Video.Media.BUCKET_DISPLAY_NAME
|
MediaStore.Video.Media.BUCKET_DISPLAY_NAME
|
||||||
@@ -406,10 +410,10 @@ class StateLibrary {
|
|||||||
fun videoFromCursor(cursor: Cursor): IPlatformVideoDetails {
|
fun videoFromCursor(cursor: Cursor): IPlatformVideoDetails {
|
||||||
val id = cursor.getString(0);
|
val id = cursor.getString(0);
|
||||||
val displayName = cursor.getString(1);
|
val displayName = cursor.getString(1);
|
||||||
val author = cursor.getString(2);
|
val author = null;//cursor.getString(2);
|
||||||
val date = cursor.getLong(3);
|
val date = cursor.getLong(2);
|
||||||
val contentType = cursor.getString(4);
|
val contentType = cursor.getString(3);
|
||||||
val category = cursor.getString(5);
|
val category = cursor.getString(4);
|
||||||
|
|
||||||
val idLong = id.toLongOrNull();
|
val idLong = id.toLongOrNull();
|
||||||
val contentUrl = if(idLong != null )
|
val contentUrl = if(idLong != null )
|
||||||
@@ -486,6 +490,10 @@ class Artist {
|
|||||||
return AdhocPager({ listOf() }, getTracksPager(idLong));
|
return AdhocPager({ listOf() }, getTracksPager(idLong));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getThumbnailOrAlbum(): String? {
|
||||||
|
return thumbnail ?: tryGetArtistThumbnail(id.toLongOrNull());
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val ID_UNKNOWN = "UNKNOWN";
|
val ID_UNKNOWN = "UNKNOWN";
|
||||||
val PROJECTION: Array<String> = arrayOf(Artists._ID,
|
val PROJECTION: Array<String> = arrayOf(Artists._ID,
|
||||||
@@ -493,6 +501,20 @@ class Artist {
|
|||||||
Artists.NUMBER_OF_TRACKS,
|
Artists.NUMBER_OF_TRACKS,
|
||||||
Artists.NUMBER_OF_ALBUMS);
|
Artists.NUMBER_OF_ALBUMS);
|
||||||
|
|
||||||
|
val thumbnailCache = ConcurrentHashMap<Long, String>();
|
||||||
|
|
||||||
|
fun tryGetArtistThumbnail(artistId: Long?): String? {
|
||||||
|
if(artistId == null)
|
||||||
|
return null;
|
||||||
|
if(thumbnailCache.containsKey(artistId))
|
||||||
|
return thumbnailCache.get(artistId);
|
||||||
|
else {
|
||||||
|
val album = Album.getArtistAlbumWithThumbnail(artistId);
|
||||||
|
thumbnailCache.put(artistId, album?.thumbnail ?: "");
|
||||||
|
return album?.thumbnail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun fromCursor(cursor: Cursor): Artist {
|
fun fromCursor(cursor: Cursor): Artist {
|
||||||
val id = cursor.getString(0);
|
val id = cursor.getString(0);
|
||||||
val artist = cursor.getString(1);
|
val artist = cursor.getString(1);
|
||||||
@@ -538,8 +560,11 @@ class Artist {
|
|||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
val list = mutableListOf<Artist>()
|
val list = mutableListOf<Artist>()
|
||||||
while(!cursor.isAfterLast) {
|
while(!cursor.isAfterLast) {
|
||||||
list.add(fromCursor(cursor));
|
val artist = fromCursor(cursor);
|
||||||
cursor.moveToNext();
|
cursor.moveToNext();
|
||||||
|
if(artist.name == "<unknown>")
|
||||||
|
continue; //TODO: Better way of detecting unknown?
|
||||||
|
list.add(artist);
|
||||||
}
|
}
|
||||||
return@use list;
|
return@use list;
|
||||||
}
|
}
|
||||||
@@ -683,6 +708,26 @@ class Album {
|
|||||||
return@use list;
|
return@use list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fun getArtistAlbumWithThumbnail(artistId: Long): Album? {
|
||||||
|
val resolver = StateApp.instance.contextOrNull?.contentResolver;
|
||||||
|
if(resolver == null) {
|
||||||
|
Logger.w(TAG, "Album contentResolver not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
val cursor = resolver?.query(
|
||||||
|
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, PROJECTION, "${MediaStore.Audio.Media.ARTIST_ID} = ?", arrayOf(artistId.toString()),
|
||||||
|
MediaStore.Audio.Albums.ALBUM + " ASC") ?: return null;
|
||||||
|
return cursor.use {
|
||||||
|
cursor.moveToFirst();
|
||||||
|
while(!cursor.isAfterLast) {
|
||||||
|
val album = fromCursor(cursor);
|
||||||
|
if(album.thumbnail != null)
|
||||||
|
return album
|
||||||
|
cursor.moveToNext();
|
||||||
|
}
|
||||||
|
return@use null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,14 @@ class LibrarySection: ConstraintLayout {
|
|||||||
val imageNavigate: ImageView;
|
val imageNavigate: ImageView;
|
||||||
val recycler: RecyclerView;
|
val recycler: RecyclerView;
|
||||||
|
|
||||||
|
val noContent: NoResultsView;
|
||||||
|
|
||||||
constructor(context: Context, attr: AttributeSet? = null) : super(context, attr) {
|
constructor(context: Context, attr: AttributeSet? = null) : super(context, attr) {
|
||||||
inflate(context, R.layout.view_library_section, this);
|
inflate(context, R.layout.view_library_section, this);
|
||||||
textName = findViewById(R.id.text_label)
|
textName = findViewById(R.id.text_label)
|
||||||
imageNavigate = findViewById(R.id.image_nav)
|
imageNavigate = findViewById(R.id.image_nav)
|
||||||
recycler = findViewById(R.id.recycler_collection);
|
recycler = findViewById(R.id.recycler_collection);
|
||||||
|
noContent = findViewById(R.id.container_no_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setNavIcon(resId: Int) {
|
fun setNavIcon(resId: Int) {
|
||||||
@@ -46,4 +47,14 @@ class LibrarySection: ConstraintLayout {
|
|||||||
textName.text = title;
|
textName.text = title;
|
||||||
imageNavigate.setOnClickListener { onOpen.invoke() };
|
imageNavigate.setOnClickListener { onOpen.invoke() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setEmpty(title: String, txt: String, iconId: Int) {
|
||||||
|
noContent.isVisible = true;
|
||||||
|
recycler.isVisible = false;
|
||||||
|
noContent.setText(title, txt, iconId);
|
||||||
|
}
|
||||||
|
fun clearEmpty() {
|
||||||
|
noContent.isVisible = false;
|
||||||
|
recycler.isVisible = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.futo.platformplayer.views
|
package com.futo.platformplayer.views
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
@@ -15,6 +16,13 @@ class NoResultsView: ConstraintLayout {
|
|||||||
val icon: ImageView;
|
val icon: ImageView;
|
||||||
val containerExtraViews: LinearLayout;
|
val containerExtraViews: LinearLayout;
|
||||||
|
|
||||||
|
constructor(context: Context, attributes: AttributeSet? = null) : super(context, attributes){
|
||||||
|
inflate(context, R.layout.view_no_results, this);
|
||||||
|
textTitle = findViewById(R.id.text_title)
|
||||||
|
textCentered = findViewById(R.id.text_centered);
|
||||||
|
icon = findViewById(R.id.icon);
|
||||||
|
containerExtraViews = findViewById(R.id.container_extra_views);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(context: Context, title: String, text: String, iconId: Int, extraViews: List<View>) : super(context) {
|
constructor(context: Context, title: String, text: String, iconId: Int, extraViews: List<View>) : super(context) {
|
||||||
inflate(context, R.layout.view_no_results, this);
|
inflate(context, R.layout.view_no_results, this);
|
||||||
@@ -22,13 +30,21 @@ class NoResultsView: ConstraintLayout {
|
|||||||
textCentered = findViewById(R.id.text_centered);
|
textCentered = findViewById(R.id.text_centered);
|
||||||
icon = findViewById(R.id.icon);
|
icon = findViewById(R.id.icon);
|
||||||
containerExtraViews = findViewById(R.id.container_extra_views);
|
containerExtraViews = findViewById(R.id.container_extra_views);
|
||||||
|
|
||||||
|
setText(title, text, iconId, extraViews);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setText(title: String, text: String, iconId: Int = -1, extraViews: List<View>? = null) {
|
||||||
textTitle.text = title;
|
textTitle.text = title;
|
||||||
textCentered.text = text;
|
textCentered.text = text;
|
||||||
icon.setImageResource(iconId);
|
|
||||||
if(iconId < 0)
|
if(iconId < 0)
|
||||||
icon.visibility = GONE;
|
icon.visibility = GONE;
|
||||||
|
else
|
||||||
|
icon.setImageResource(iconId);
|
||||||
|
|
||||||
for(view in extraViews)
|
if(extraViews != null)
|
||||||
containerExtraViews.addView(view);
|
for(view in extraViews)
|
||||||
|
containerExtraViews.addView(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,7 @@ package com.futo.platformplayer.views.adapters
|
|||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.GestureDetector
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
@@ -46,7 +44,7 @@ class CommentViewHolder : ViewHolder {
|
|||||||
private val _imageLikeIcon: ImageView;
|
private val _imageLikeIcon: ImageView;
|
||||||
private val _textLikes: TextView;
|
private val _textLikes: TextView;
|
||||||
private val _imageDislikeIcon: ImageView;
|
private val _imageDislikeIcon: ImageView;
|
||||||
private val _imageCopy: ImageView;
|
private val _buttonCopy: PillButton;
|
||||||
private val _textDislikes: TextView;
|
private val _textDislikes: TextView;
|
||||||
private val _buttonReplies: PillButton;
|
private val _buttonReplies: PillButton;
|
||||||
private val _layoutRating: LinearLayout;
|
private val _layoutRating: LinearLayout;
|
||||||
@@ -70,7 +68,7 @@ class CommentViewHolder : ViewHolder {
|
|||||||
_textMetadata = itemView.findViewById(R.id.text_metadata);
|
_textMetadata = itemView.findViewById(R.id.text_metadata);
|
||||||
_textBody = itemView.findViewById(R.id.text_body);
|
_textBody = itemView.findViewById(R.id.text_body);
|
||||||
_imageLikeIcon = itemView.findViewById(R.id.image_like_icon);
|
_imageLikeIcon = itemView.findViewById(R.id.image_like_icon);
|
||||||
_imageCopy = itemView.findViewById(R.id.image_copy);
|
_buttonCopy = itemView.findViewById(R.id.image_copy);
|
||||||
_textLikes = itemView.findViewById(R.id.text_likes);
|
_textLikes = itemView.findViewById(R.id.text_likes);
|
||||||
_imageDislikeIcon = itemView.findViewById(R.id.image_dislike_icon);
|
_imageDislikeIcon = itemView.findViewById(R.id.image_dislike_icon);
|
||||||
_textDislikes = itemView.findViewById(R.id.text_dislikes);
|
_textDislikes = itemView.findViewById(R.id.text_dislikes);
|
||||||
@@ -105,7 +103,8 @@ class CommentViewHolder : ViewHolder {
|
|||||||
StatePolycentric.instance.updateLikeMap(c.reference, args.hasLiked, args.hasDisliked)
|
StatePolycentric.instance.updateLikeMap(c.reference, args.hasLiked, args.hasDisliked)
|
||||||
};
|
};
|
||||||
|
|
||||||
_imageCopy.setOnLongClickListener {
|
_buttonCopy.setTransparant()
|
||||||
|
_buttonCopy.onClick.subscribe {
|
||||||
val clipboard = viewGroup.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val clipboard = viewGroup.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val text = comment?.message.orEmpty()
|
val text = comment?.message.orEmpty()
|
||||||
val clip = ClipData.newPlainText("Comment", text)
|
val clip = ClipData.newPlainText("Comment", text)
|
||||||
|
|||||||
+3
-2
@@ -37,9 +37,10 @@ class ArtistTileViewHolder(val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder
|
|||||||
override fun bind(artist: Artist) {
|
override fun bind(artist: Artist) {
|
||||||
_artist = artist;
|
_artist = artist;
|
||||||
_imageThumbnail?.let {
|
_imageThumbnail?.let {
|
||||||
if (artist.thumbnail != null)
|
val thumbnail = artist.getThumbnailOrAlbum();
|
||||||
|
if (thumbnail != null)
|
||||||
Glide.with(it)
|
Glide.with(it)
|
||||||
.load(artist.thumbnail)
|
.load(thumbnail)
|
||||||
.placeholder(R.drawable.ic_artist)
|
.placeholder(R.drawable.ic_artist)
|
||||||
.into(it)
|
.into(it)
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -7,11 +7,15 @@ import android.view.View
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.core.view.isVisible
|
||||||
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.views.LoaderView
|
import com.futo.platformplayer.views.LoaderView
|
||||||
|
|
||||||
class PillButton : LinearLayout {
|
class PillButton : LinearLayout {
|
||||||
|
val root: LinearLayout;
|
||||||
val icon: ImageView;
|
val icon: ImageView;
|
||||||
val text: TextView;
|
val text: TextView;
|
||||||
val loaderView: LoaderView;
|
val loaderView: LoaderView;
|
||||||
@@ -23,6 +27,7 @@ class PillButton : LinearLayout {
|
|||||||
icon = findViewById(R.id.pill_icon);
|
icon = findViewById(R.id.pill_icon);
|
||||||
text = findViewById(R.id.pill_text);
|
text = findViewById(R.id.pill_text);
|
||||||
loaderView = findViewById(R.id.loader)
|
loaderView = findViewById(R.id.loader)
|
||||||
|
root = findViewById<LinearLayout>(R.id.root);
|
||||||
|
|
||||||
val attrArr = context.obtainStyledAttributes(attrs, R.styleable.PillButton, 0, 0);
|
val attrArr = context.obtainStyledAttributes(attrs, R.styleable.PillButton, 0, 0);
|
||||||
val attrIconRef = attrArr.getResourceId(R.styleable.PillButton_pillIcon, -1);
|
val attrIconRef = attrArr.getResourceId(R.styleable.PillButton_pillIcon, -1);
|
||||||
@@ -34,6 +39,13 @@ class PillButton : LinearLayout {
|
|||||||
val attrText = attrArr.getText(R.styleable.PillButton_pillText) ?: "";
|
val attrText = attrArr.getText(R.styleable.PillButton_pillText) ?: "";
|
||||||
text.text = attrText;
|
text.text = attrText;
|
||||||
|
|
||||||
|
if(text.text.isNullOrBlank()) {
|
||||||
|
val dp6 = 6.dp(resources);
|
||||||
|
val dp7 = 7.dp(resources);
|
||||||
|
val dp12 = 12.dp(resources);
|
||||||
|
root.setPadding(dp7, dp6, dp7, dp7)
|
||||||
|
}
|
||||||
|
|
||||||
findViewById<LinearLayout>(R.id.root).setOnClickListener {
|
findViewById<LinearLayout>(R.id.root).setOnClickListener {
|
||||||
if (_isLoading) {
|
if (_isLoading) {
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
@@ -43,6 +55,10 @@ class PillButton : LinearLayout {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setTransparant() {
|
||||||
|
root.setBackgroundColor(0);
|
||||||
|
}
|
||||||
|
|
||||||
fun setLoading(loading: Boolean) {
|
fun setLoading(loading: Boolean) {
|
||||||
if (loading == _isLoading) {
|
if (loading == _isLoading) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#CC000000" />
|
||||||
|
<stroke android:color="#333333" android:width="1dp" />
|
||||||
|
<corners android:radius="1dp" />
|
||||||
|
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#00000000" />
|
||||||
|
<stroke android:color="#000000" android:width="1dp" />
|
||||||
|
<corners android:radius="50dp" />
|
||||||
|
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#000000" />
|
||||||
|
<stroke android:color="#333333" android:width="1dp" />
|
||||||
|
<corners android:radius="50dp" />
|
||||||
|
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#2D63ED" />
|
||||||
|
<stroke android:color="#333333" android:width="1dp" />
|
||||||
|
<corners android:radius="50dp" />
|
||||||
|
<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="M294.23,860L294.23,779.23L411.15,697.15L411.15,538.54L100,663.46L100,564.23L411.15,345.62L411.15,168.85Q411.15,140.46 431.39,120.23Q451.62,100 480,100Q508.38,100 528.61,120.23Q548.85,140.46 548.85,168.85L548.85,345.62L860,564.23L860,663.46L548.85,538.54L548.85,697.15L665.38,779.23L665.38,860L480,803.84L294.23,860Z"/>
|
||||||
|
</vector>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
<com.futo.platformplayer.views.others.CreatorThumbnail
|
<com.futo.platformplayer.views.others.CreatorThumbnail
|
||||||
android:id="@+id/creator_thumbnail"
|
android:id="@+id/creator_thumbnail"
|
||||||
android:background="@drawable/rounded_outline"
|
android:background="@drawable/rounded_outline"
|
||||||
android:layout_width="1dp"
|
android:layout_width="35dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:contentDescription="@string/cd_creator_thumbnail"
|
android:contentDescription="@string/cd_creator_thumbnail"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
|
|||||||
@@ -25,14 +25,95 @@
|
|||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<!--More Menu-->
|
<!--More Menu-->
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/more_menu_buttons"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="match_parent">
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_gravity="bottom|end"
|
android:id="@+id/container_more_options"
|
||||||
android:gravity="end">
|
android:layout_width="match_parent"
|
||||||
</LinearLayout>
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="3dp"
|
||||||
|
android:layout_marginLeft="3dp"
|
||||||
|
android:layout_marginRight="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/more_menu_buttons"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="15dp"
|
||||||
|
android:layout_marginRight="0dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_toggle_airplane"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:background="@drawable/background_menu_toggle"
|
||||||
|
android:layout_marginRight="3dp"
|
||||||
|
android:layout_marginLeft="3dp"
|
||||||
|
android:gravity="center">
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:src="@drawable/ic_flight" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_toggle_privacy"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:background="@drawable/background_menu_toggle"
|
||||||
|
android:layout_marginRight="3dp"
|
||||||
|
android:layout_marginLeft="3dp"
|
||||||
|
android:gravity="center">
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:src="@drawable/ic_disabled_visible" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/button_close"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginRight="0dp"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:gravity="center">
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:src="@drawable/ic_close" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/more_menu_buttons"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
|
||||||
|
android:gravity="end">
|
||||||
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -96,16 +96,6 @@
|
|||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/image_copy"
|
|
||||||
android:layout_width="18dp"
|
|
||||||
android:layout_height="18dp"
|
|
||||||
android:contentDescription="@string/copy"
|
|
||||||
app:srcCompat="@drawable/ic_copy"
|
|
||||||
app:tint="@color/white"
|
|
||||||
android:background="@drawable/background_pill"
|
|
||||||
android:layout_marginStart="9dp" />
|
|
||||||
|
|
||||||
<com.futo.platformplayer.views.pills.PillRatingLikesDislikes
|
<com.futo.platformplayer.views.pills.PillRatingLikesDislikes
|
||||||
android:id="@+id/rating"
|
android:id="@+id/rating"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -160,6 +150,15 @@
|
|||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="13dp" />
|
android:textSize="13dp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.futo.platformplayer.views.pills.PillButton
|
||||||
|
android:id="@+id/image_copy"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:pillIcon="@drawable/ic_copy"
|
||||||
|
app:pillText=""
|
||||||
|
android:layout_marginStart="15dp"
|
||||||
|
android:layout_marginEnd="0dp"/>
|
||||||
<com.futo.platformplayer.views.pills.PillButton
|
<com.futo.platformplayer.views.pills.PillButton
|
||||||
android:id="@+id/button_replies"
|
android:id="@+id/button_replies"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -167,7 +166,7 @@
|
|||||||
android:contentDescription="@string/cd_button_replies"
|
android:contentDescription="@string/cd_button_replies"
|
||||||
app:pillIcon="@drawable/ic_forum"
|
app:pillIcon="@drawable/ic_forum"
|
||||||
app:pillText="55 Replies"
|
app:pillText="55 Replies"
|
||||||
android:layout_marginStart="15dp" />
|
android:layout_marginStart="2dp" />
|
||||||
|
|
||||||
<Space android:layout_width="0dp"
|
<Space android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?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="86dp"
|
||||||
|
android:layout_height="146dp"
|
||||||
|
android:layout_marginTop="3dp"
|
||||||
|
android:layout_marginBottom="3dp"
|
||||||
|
android:layout_marginLeft="3dp"
|
||||||
|
android:layout_marginRight="3dp"
|
||||||
|
android:background="@drawable/background_menu"
|
||||||
|
android:id="@+id/root"
|
||||||
|
android:clickable="true">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_icon"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintDimensionRatio="H,1,1"
|
||||||
|
app:shapeAppearanceOverlay="@style/roundedCorners_4dp"
|
||||||
|
app:srcCompat="@drawable/unknown_music"
|
||||||
|
android:background="@drawable/video_thumbnail_outline"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent" />
|
||||||
|
|
||||||
|
<!--TODO: Fix word wrapping with autosize-->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="11dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:fontFamily="@font/inter_medium"
|
||||||
|
tools:text="The Beetles"
|
||||||
|
android:maxLines="2"
|
||||||
|
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/image_icon"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/image_icon"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginRight="10dp"
|
||||||
|
android:layout_marginTop="0dp"
|
||||||
|
/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -41,6 +41,16 @@
|
|||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:orientation="horizontal" />
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
|
<com.futo.platformplayer.views.NoResultsView
|
||||||
|
android:id="@+id/container_no_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/text_label"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent" />
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/button_play"
|
android:id="@+id/button_play"
|
||||||
|
|||||||
Reference in New Issue
Block a user