Library continuation, disable auto backup ask, minor tweaks.

This commit is contained in:
Kelvin
2025-11-08 19:02:38 +01:00
parent 9b97e05e3b
commit 347ef855b3
18 changed files with 746 additions and 61 deletions
@@ -951,7 +951,7 @@ class Settings : FragmentedStorageFileJson() {
class Backup {
@Serializable(with = OffsetDateTimeSerializer::class)
var lastAutoBackupTime: OffsetDateTime = OffsetDateTime.MIN;
var didAskAutoBackup: Boolean = false;
var didAskAutoBackup: Boolean = true;
var autoBackupPassword: String? = null;
fun shouldAutomaticBackup() = autoBackupPassword != null;
@@ -32,6 +32,7 @@ import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.Album
import com.futo.platformplayer.states.Artist
import com.futo.platformplayer.states.ArtistOrdering
import com.futo.platformplayer.states.StateLibrary
import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.StringStorage
@@ -82,7 +83,7 @@ class LibraryArtistsFragment : MainFragment() {
constructor(fragment: LibraryArtistsFragment, inflater: LayoutInflater) : super(fragment, inflater)
fun onShown() {
val intialArtists = StateLibrary.instance.getArtists();
val intialArtists = StateLibrary.instance.getArtists(ArtistOrdering.Alphabethic);
Logger.i(TAG, "Initial album count: " + intialArtists.size);
setPager(AdhocPager<Artist>({ listOf(); }, intialArtists));
@@ -91,7 +92,7 @@ class LibraryArtistsFragment : MainFragment() {
override fun reload() {
try {
setLoading(true);
val intialArtists = StateLibrary.instance.getArtists();
val intialArtists = StateLibrary.instance.getArtists(ArtistOrdering.Alphabethic);
Logger.i(TAG, "Initial album count: " + intialArtists.size);
setPager(AdhocPager<Artist>({ listOf(); }, intialArtists));
@@ -7,16 +7,37 @@ import android.provider.MediaStore
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.dp
import com.futo.platformplayer.states.Album
import com.futo.platformplayer.states.Artist
import com.futo.platformplayer.states.ArtistOrdering
import com.futo.platformplayer.states.FileEntry
import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StateLibrary
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
import com.futo.platformplayer.views.AnyInsertedAdapterView
import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithTop
import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithViews
import com.futo.platformplayer.views.LibrarySection
import com.futo.platformplayer.views.adapters.AnyAdapter
import com.futo.platformplayer.views.adapters.InsertedViewAdapter
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
import com.futo.platformplayer.views.adapters.viewholders.ArtistTileViewHolder
import com.futo.platformplayer.views.adapters.viewholders.LocalVideoTileViewHolder
import com.futo.platformplayer.views.buttons.BigButton
@@ -117,13 +138,17 @@ class LibraryFragment : MainFragment() {
class FragView: ConstraintLayout {
val fragment: LibraryFragment;
var buttonArtists: BigButton;
var buttonAlbums: BigButton;
var buttonVideos: BigButton;
var buttonPlaylists: BigButton;
var buttonFiles: BigButton;
var sectionArtists: LibrarySection;
var sectionAlbums: LibrarySection;
var sectionVideos: LibrarySection;
var sectionFiles: LibrarySection;
//var buttonFiles: BigButton;
var metaInfo: TextView;
val recycler: RecyclerView;
val adapterFiles: AnyInsertedAdapterView<FileEntry,LibraryFilesFragment.FileViewHolder>;
//var metaInfo: TextView;
var allowMusic: Boolean = false;
var allowVideo: Boolean = false;
@@ -131,61 +156,135 @@ class LibraryFragment : MainFragment() {
constructor(fragment: LibraryFragment, allowMusic: Boolean?, allowVideo: Boolean?) : super(fragment.requireContext()) {
inflate(context, R.layout.fragview_library, this);
this.fragment = fragment;
buttonArtists = findViewById<BigButton>(R.id.button_artists);
buttonAlbums = findViewById<BigButton>(R.id.button_albums);
buttonVideos = findViewById<BigButton>(R.id.button_videos);
buttonPlaylists = findViewById<BigButton>(R.id.button_playlists);
buttonFiles = findViewById<BigButton>(R.id.button_files);
metaInfo = findViewById(R.id.meta_info);
recycler = findViewById(R.id.recycler);
sectionArtists = LibrarySection(context)//findViewById<LibrarySection>(R.id.section_artists);
sectionArtists.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 155.dp(resources)).apply {
this.setMargins(0,10.dp(resources), 0, 0);
}
sectionAlbums = LibrarySection(context)//findViewById(R.id.section_albums);
sectionAlbums.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 185.dp(resources)).apply {
this.setMargins(0,0, 0, 0);
}
sectionVideos = LibrarySection(context)//findViewById(R.id.section_videos);
sectionVideos.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 170.dp(resources)).apply {
this.setMargins(0,0, 0, 0);
}
sectionFiles = LibrarySection(context)//findViewById(R.id.section_videos);
sectionFiles.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 40.dp(resources)).apply {
this.setMargins(0,0, 0, 0);
}
sectionFiles.setSection("Directories") {
StateLibrary.instance.addFileDirectory {
reloadFiles();
}
}
sectionFiles.setNavIcon(R.drawable.ic_add);
//buttonFiles = findViewById<BigButton>(R.id.button_files);
//metaInfo = findViewById(R.id.meta_info);
this.allowMusic = allowMusic ?: false;
this.allowVideo = allowVideo ?: false;
buttonArtists.onClick.subscribe {
sectionArtists.setSection("Artists", {
if(this.allowMusic)
fragment.navigate<LibraryArtistsFragment>();
else
fragment.requestPermissionMusic();
}
buttonAlbums.onClick.subscribe {
});
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", {
if(this.allowMusic)
fragment.navigate<LibraryAlbumsFragment>();
else
fragment.requestPermissionMusic();
}
buttonVideos.onClick.subscribe {
});
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", {
if(this.allowVideo)
fragment.navigate<LibraryVideosFragment>();
else
fragment.requestPermissionVideo();
}
buttonPlaylists.onClick.subscribe {
fragment.navigate<PlaylistsFragment>();
}
});
val adapterVideos = sectionVideos.getAnyAdapter<IPlatformVideo, LocalVideoTileViewHolder>({
it.onClick.subscribe {
if(it != null)
fragment.navigate<VideoDetailFragment>(it);
}
});
val videos = StateLibrary.instance.getRecentVideos(null, 20);
adapterVideos.setData(videos);
adapterFiles = recycler.asAnyWithViews<FileEntry, LibraryFilesFragment.FileViewHolder>(
arrayListOf(
sectionArtists,
sectionAlbums,
sectionVideos,
sectionFiles
), arrayListOf(), RecyclerView.VERTICAL, false, {
it.onClick.subscribe {
if(it != null)
fragment.navigate<LibraryFilesFragment>(it);
}
it.onDelete.subscribe {
if(it != null) {
StateLibrary.instance.deleteFileDirectory(it.path);
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) {
allowMusic = access;
buttonAlbums.setButtonEnabled(access);
buttonArtists.setButtonEnabled(access);
metaInfo.text = listOf(
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
).filterNotNull().joinToString("\n");
sectionAlbums.setContentEmptyMessage(R.drawable.ic_library, "No mediastore permissions");
sectionArtists.setContentEmptyMessage(R.drawable.ic_library, "No mediastore permissions");
//buttonArtists.setButtonEnabled(access);
//metaInfo.text = listOf(
// 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
//).filterNotNull().joinToString("\n");
}
fun setVideoPermissions(access: Boolean) {
allowVideo = access;
buttonVideos.setButtonEnabled(access);
metaInfo.text = listOf(
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
).filterNotNull().joinToString("\n");
sectionVideos.setContentEmptyMessage(R.drawable.ic_library, "No video permissions");
//metaInfo.text = listOf(
// 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
//).filterNotNull().joinToString("\n");
}
fun onShown() {
@@ -593,7 +593,9 @@ class StateApp {
scheduleBackgroundWork(context, interval != 0, interval);
Logger.i(TAG, "MainApp Started: Initialize [AutoBackup]");
Settings.instance.backup.didAskAutoBackup = true; //Some users have issues with it
if(!Settings.instance.backup.didAskAutoBackup && !Settings.instance.backup.shouldAutomaticBackup()) {
/*
StateAnnouncement.instance.registerAnnouncement("backup", "Set Automatic Backup", "Configure daily backups of your data to restore in case of catastrophic failure.", AnnouncementType.SESSION, null, null, "Configure", {
if(context is IWithResultLauncher && !Settings.instance.storage.isStorageMainValid(context)) {
UIDialogs.toast("Missing general directory");
@@ -610,6 +612,7 @@ class StateApp {
Settings.instance.backup.didAskAutoBackup = true;
Settings.instance.save();
});
*/
}
else if(Settings.instance.backup.didAskAutoBackup && Settings.instance.backup.shouldAutomaticBackup() && !Settings.instance.storage.isStorageMainValid(context)) {
if(context is IWithResultLauncher) {
@@ -100,8 +100,8 @@ class StateLibrary {
return Album.getAlbum(id);
}
fun getArtists(): List<Artist> {
return Artist.getArtists();
fun getArtists(ordering: ArtistOrdering): List<Artist> {
return Artist.getArtists(ordering);
}
fun getArtist(str: String): Artist? {
val idLong = str.toLongOrNull();
@@ -114,8 +114,9 @@ class StateLibrary {
}
fun getVideos(buckets: List<String>? = null): IPager<IPlatformContent> {
var query = if(buckets != null) "${MediaStore.Video.Media.BUCKET_DISPLAY_NAME} IN " + "(" + buckets.map { "'${it}'" }.joinToString(",") + ")" else null;
val cursor = StateApp.instance.contextOrNull?.contentResolver?.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, PROJECTION_VIDEO,
if(buckets != null) "${MediaStore.Video.Media.BUCKET_DISPLAY_NAME} IN " + "(" + buckets.map { "'${it}'" }.joinToString(",") + ")" else null,
query,
null,
MediaStore.Video.Media.DATE_ADDED + " DESC") ?: return EmptyPager();
cursor.moveToFirst();
@@ -134,6 +135,16 @@ class StateLibrary {
return@AdhocPager list;
}, list);
}
fun getRecentVideos(buckets: List<String>? = null, count: Int = 20): List<IPlatformVideo> {
val videoPager = getVideos(buckets);
val items = mutableListOf<IPlatformVideo>();
while(videoPager.getResults().size > 0 && items.size < count) {
items.addAll(videoPager.getResults().filter { it is IPlatformVideo }.map { it as IPlatformVideo });
if(videoPager.hasMorePages())
videoPager.nextPage();
}
return items;
}
private var _cacheBucketNames: List<Bucket>? = null;
fun getVideoBucketNames(): List<Bucket> {
@@ -363,6 +374,12 @@ class StateLibrary {
class Bucket(val id: Long, val name: String);
enum class ArtistOrdering {
Alphabethic,
TrackCount,
AlbumCount
}
class Artist {
val id: String;
val name: String;
@@ -424,9 +441,18 @@ class Artist {
return null;
return Artist.fromCursor(cursor);
}
fun getArtists(): List<Artist> {
val cursor = StateApp.instance.contextOrNull?.contentResolver?.query(Artists.EXTERNAL_CONTENT_URI, PROJECTION, null, null,
Artists.ARTIST + " ASC") ?: return listOf();
fun getArtists(ordering: ArtistOrdering = ArtistOrdering.Alphabethic): List<Artist> {
val ordering = when(ordering) {
ArtistOrdering.Alphabethic -> Artists.ARTIST + " ASC";
ArtistOrdering.AlbumCount -> Artists.NUMBER_OF_ALBUMS + " DESC";
ArtistOrdering.TrackCount -> Artists.NUMBER_OF_TRACKS + " DESC";
else -> null
}
val cursor = StateApp.instance.contextOrNull?.contentResolver?.query(Artists.EXTERNAL_CONTENT_URI, PROJECTION,
null,
null,
ordering) ?: return listOf();
cursor.moveToFirst();
val list = mutableListOf<Artist>()
while(!cursor.isAfterLast) {
@@ -72,9 +72,9 @@ class AnyInsertedAdapterView<I, T>(view: RecyclerView, adapter: BaseAnyAdapter<I
= this.asAnyWithViews(arrayListOf(view), arrayListOf(), orientation, reversed, onCreate);
inline fun<I, reified T : AnyAdapter.AnyViewHolder<I>> RecyclerView.asAnyWithViews(prepend: ArrayList<View> = arrayListOf(), append: ArrayList<View> = arrayListOf(), orientation: Int = RecyclerView.VERTICAL, reversed: Boolean = false, noinline onCreate: ((T)->Unit)? = null) : AnyInsertedAdapterView<I, T> {
for(view in prepend)
(view.parent as ViewGroup).removeView(view);
(view.parent as ViewGroup?)?.removeView(view);
for(view in append)
(view.parent as ViewGroup).removeView(view);
(view.parent as ViewGroup?)?.removeView(view);
return AnyInsertedAdapterView(this, AnyInsertedAdapter.create(prepend, append, onCreate), orientation, reversed);
}
inline fun<I, reified T : AnyAdapter.AnyViewHolder<I>> RecyclerView.asAnyWithViews(list: ArrayList<I>, prepend: ArrayList<View> = arrayListOf(), append: ArrayList<View> = arrayListOf(), orientation: Int = RecyclerView.VERTICAL, reversed: Boolean = false, noinline onCreate: ((T)->Unit)? = null) : AnyInsertedAdapterView<I, T> {
@@ -0,0 +1,49 @@
package com.futo.platformplayer.views
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintLayout.GONE
import androidx.constraintlayout.widget.ConstraintLayout.inflate
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.futo.platformplayer.R
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
import com.futo.platformplayer.views.adapters.AnyAdapter
import com.futo.platformplayer.views.adapters.AnyAdapter.AnyViewHolder
import com.google.android.material.imageview.ShapeableImageView
class LibrarySection: ConstraintLayout {
val textName: TextView;
val imageNavigate: ImageView;
val recycler: RecyclerView;
constructor(context: Context, attr: AttributeSet? = null) : super(context, attr) {
inflate(context, R.layout.view_library_section, this);
textName = findViewById(R.id.text_label)
imageNavigate = findViewById(R.id.image_nav)
recycler = findViewById(R.id.recycler_collection);
}
fun setNavIcon(resId: Int) {
imageNavigate.setImageResource(resId);
}
fun setContentEmptyMessage(icon: Int, msg: String) {
}
inline fun <T, reified V: AnyViewHolder<T>> getAnyAdapter(noinline onCreate: ((V)->Unit)? = null, orientation: Int = RecyclerView.HORIZONTAL): AnyAdapterView<T, V> {
return recycler.asAny<T, V>(orientation, false, onCreate);
}
inline fun setSection(title: String, crossinline onOpen: (()->Unit)) {
textName.text = title;
imageNavigate.setOnClickListener { onOpen.invoke() };
}
}
@@ -0,0 +1,52 @@
package com.futo.platformplayer.views.adapters.viewholders
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.states.Album
import com.futo.platformplayer.views.adapters.AnyAdapter
class AlbumTileViewHolder(val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<Album>(
LayoutInflater.from(_viewGroup.context).inflate(
R.layout.list_album_tile,
_viewGroup, false)) {
val onClick = Event1<Album?>();
protected var _album: Album? = null;
protected val _imageThumbnail: ImageView
protected val _textName: TextView
protected val _textMetadata: TextView
init {
_imageThumbnail = _view.findViewById(R.id.image_thumbnail);
_textName = _view.findViewById(R.id.text_name);
_textMetadata = _view.findViewById(R.id.text_metadata);
_view.setOnClickListener { onClick.emit(_album) };
}
override fun bind(album: Album) {
_album = album;
_imageThumbnail?.let {
if (album.thumbnail != null)
Glide.with(it)
.load(album.thumbnail)
.placeholder(R.drawable.unknown_music)
.into(it)
else
Glide.with(it).load(R.drawable.unknown_music).into(it);
};
_textName.text = album.name;
_textMetadata.text = album.artist ?: "";
}
}
@@ -0,0 +1,53 @@
package com.futo.platformplayer.views.adapters.viewholders
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.states.Album
import com.futo.platformplayer.states.Artist
import com.futo.platformplayer.views.adapters.AnyAdapter
class ArtistTileViewHolder(val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<Artist>(
LayoutInflater.from(_viewGroup.context).inflate(
R.layout.list_artist_tile,
_viewGroup, false)) {
val onClick = Event1<Artist?>();
protected var _artist: Artist? = null;
protected val _imageThumbnail: ImageView
protected val _textName: TextView
protected val _textMetadata: TextView
init {
_imageThumbnail = _view.findViewById(R.id.image_thumbnail);
_textName = _view.findViewById(R.id.text_name);
_textMetadata = _view.findViewById(R.id.text_metadata);
_view.setOnClickListener { onClick.emit(_artist) };
}
override fun bind(artist: Artist) {
_artist = artist;
_imageThumbnail?.let {
if (artist.thumbnail != null)
Glide.with(it)
.load(artist.thumbnail)
.placeholder(R.drawable.unknown_music)
.into(it)
else
Glide.with(it).load(R.drawable.unknown_music).into(it);
};
_textName.text = artist.name;
_textMetadata.text = ""// "${artist.countTracks} tracks";
}
}
@@ -0,0 +1,63 @@
package com.futo.platformplayer.views.adapters.viewholders
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible
import com.bumptech.glide.Glide
import com.futo.platformplayer.R
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.states.Album
import com.futo.platformplayer.states.Artist
import com.futo.platformplayer.toHumanNowDiffString
import com.futo.platformplayer.toHumanTime
import com.futo.platformplayer.views.adapters.AnyAdapter
import com.google.android.material.imageview.ShapeableImageView
class LocalVideoTileViewHolder(val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<IPlatformVideo>(
LayoutInflater.from(_viewGroup.context).inflate(
R.layout.list_video_thumbnail_tile,
_viewGroup, false)) {
val onClick = Event1<IPlatformVideo?>();
protected var _content: IPlatformVideo? = null;
protected val _imageThumbnail: ShapeableImageView
protected val _textDuration: TextView;
protected val _textName: TextView
protected val _textMetadata: TextView
init {
_imageThumbnail = _view.findViewById(R.id.image_video_thumbnail);
_textDuration = _view.findViewById(R.id.thumbnail_duration);
_textName = _view.findViewById(R.id.text_video_name);
_textMetadata = _view.findViewById(R.id.text_video_metadata);
_view.setOnClickListener { onClick.emit(_content) };
}
override fun bind(content: IPlatformVideo) {
_content = content;
_imageThumbnail?.let {
if (content.thumbnails.getHQThumbnail() != null)
Glide.with(it)
.load(content.thumbnails.getHQThumbnail())
.placeholder(R.drawable.unknown_music)
.into(it)
else
Glide.with(it).load(R.drawable.unknown_music).into(it);
};
_textName.text = content.name;
_textMetadata.text = content.datetime?.toHumanNowDiffString();
_textDuration.text = content.duration.toHumanTime(false) + " ago";
_textDuration.isVisible = content.duration > 0;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

+25 -12
View File
@@ -9,22 +9,31 @@
android:background="@drawable/bottom_menu_border"
android:id="@+id/root"
android:clickable="true">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!--
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.futo.platformplayer.views.buttons.BigButton
android:id="@+id/button_artists"
<com.futo.platformplayer.views.LibrarySection
android:id="@+id/section_artists"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:buttonIcon="@drawable/ic_creators"
app:buttonSubText="All artists known on this phone"
app:buttonText="Artists"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="8dp"/>
android:layout_height="165dp" />
<com.futo.platformplayer.views.LibrarySection
android:id="@+id/section_albums"
android:layout_width="match_parent"
android:layout_height="190dp" />
<com.futo.platformplayer.views.LibrarySection
android:id="@+id/section_videos"
android:layout_width="match_parent"
android:layout_marginTop="0dp"
android:layout_height="180dp" />
<com.futo.platformplayer.views.buttons.BigButton
android:id="@+id/button_albums"
android:layout_width="match_parent"
@@ -34,7 +43,8 @@
app:buttonText="Albums"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="8dp" />
android:layout_marginBottom="8dp" /> -->
<!--
<com.futo.platformplayer.views.buttons.BigButton
android:id="@+id/button_playlists"
android:layout_width="match_parent"
@@ -54,7 +64,8 @@
app:buttonSubText="All local videos on this phone"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="8dp" />
android:layout_marginBottom="8dp" /> -->
<!--
<com.futo.platformplayer.views.buttons.BigButton
android:id="@+id/button_files"
android:layout_width="match_parent"
@@ -62,6 +73,7 @@
app:buttonIcon="@drawable/ic_gallery"
app:buttonText="Files"
app:buttonSubText="Browse files on your phone"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="8dp" />
@@ -82,5 +94,6 @@
android:text=""
android:textColor="#AAAAAA" />
</LinearLayout>
-->
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,67 @@
<?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="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:id="@+id/root"
android:clickable="true">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_thumbnail"
android:layout_height="86dp"
android:layout_width="86dp"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/roundedCorners_4dp"
app:srcCompat="@drawable/unknown_music"
android:background="@drawable/video_thumbnail_outline"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/text_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@color/white"
android:fontFamily="@font/inter_medium"
tools:text="The Beetles"
android:maxLines="2"
app:layout_constraintLeft_toRightOf="@id/image_thumbnail"
app:layout_constraintTop_toBottomOf="@id/image_thumbnail"
app:layout_constraintLeft_toLeftOf="@id/image_thumbnail"
app:layout_constraintRight_toRightOf="@id/image_thumbnail"
android:layout_marginTop="5dp"
/>
<TextView
android:id="@+id/text_metadata"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="#75FFFFFF"
android:fontFamily="@font/inter_regular"
tools:text="3 videos"
android:maxLines="2"
app:layout_constraintTop_toBottomOf="@id/text_name"
app:layout_constraintLeft_toLeftOf="@id/text_name"
app:layout_constraintRight_toRightOf="@id/text_name" />
<!--
<ImageButton
android:id="@+id/button_play"
android:layout_width="34dp"
android:layout_height="34dp"
app:srcCompat="@drawable/ic_play_white_nopad"
android:scaleType="fitCenter"
android:padding="8dp"
app:layout_constraintRight_toLeftOf="@id/button_trash"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="10dp"/> -->
</androidx.constraintlayout.widget.ConstraintLayout>
+17 -5
View File
@@ -6,10 +6,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
android:id="@+id/root"
android:clickable="true">
android:clickable="true"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingBottom="10dp"
android:paddingTop="10dp">
<TextView
android:id="@+id/text_name"
@@ -19,8 +23,8 @@
android:textSize="13dp"
android:textColor="@color/white"
android:fontFamily="@font/inter_light"
tools:text="Legendary grant recipient: Marvin Wißfeld Very Long Title That is Long"
android:maxLines="2"
tools:text="Example Artist"
android:maxLines="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
@@ -55,4 +59,12 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="10dp"/> -->
<View
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#181818" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,70 @@
<?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="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:id="@+id/root"
android:clickable="true">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_thumbnail"
android:layout_height="66dp"
android:layout_width="66dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/roundedCorners_33dp"
app:srcCompat="@drawable/unknown_music"
android:background="@drawable/video_thumbnail_outline"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/text_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@color/white"
android:fontFamily="@font/inter_medium"
android:textAlignment="center"
tools:text="The Beetles"
android:maxLines="2"
app:layout_constraintTop_toBottomOf="@id/image_thumbnail"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="7dp"
/>
<TextView
android:id="@+id/text_metadata"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="#75FFFFFF"
android:fontFamily="@font/inter_regular"
android:textAlignment="center"
tools:text="3 videos"
android:maxLines="2"
app:layout_constraintTop_toBottomOf="@id/text_name"
app:layout_constraintLeft_toLeftOf="@id/text_name"
app:layout_constraintRight_toRightOf="@id/text_name" />
<!--
<ImageButton
android:id="@+id/button_play"
android:layout_width="34dp"
android:layout_height="34dp"
app:srcCompat="@drawable/ic_play_white_nopad"
android:scaleType="fitCenter"
android:padding="8dp"
app:layout_constraintRight_toLeftOf="@id/button_trash"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="10dp"/> -->
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,113 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="120dp"
android:layout_height="160dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:orientation="vertical">
<FrameLayout
android:id="@+id/player_container"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="120dp"
android:layout_height="66dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_video_thumbnail"
android:layout_width="120dp"
android:layout_height="66dp"
android:scaleType="centerCrop"
android:contentDescription="@string/thumbnail"
app:shapeAppearanceOverlay="@style/roundedCorners_4dp"
app:srcCompat="@drawable/placeholder_video_thumbnail"
android:background="@drawable/video_thumbnail_outline" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/thumbnail_duration_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_gravity="end"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="4dp"
android:paddingTop="0dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:background="@drawable/background_thumbnail_duration">
<TextView
android:id="@+id/thumbnail_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:textColor="#FFFFFF"
android:textSize="12dp"
tools:text="0:00"
android:layout_gravity="center"
android:textStyle="normal" />
</LinearLayout>
</RelativeLayout>
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="70dp"
app:layout_constraintTop_toBottomOf="@id/player_container"
app:layout_constraintLeft_toLeftOf="@id/player_container"
app:layout_constraintRight_toRightOf="@id/player_container"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp">
<TextView
android:id="@+id/text_video_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="2dp"
android:layout_marginTop="5dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:textSize="11dp"
android:textColor="@color/white"
android:fontFamily="@font/inter_light"
tools:text="Legendary grant recipient: Marvin Wißfeld of MicroG Very loong title fff"
android:maxLines="2"
android:ellipsize="end"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/text_video_metadata"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:gravity="center_vertical"
android:textSize="10dp"
android:textColor="@color/gray_e0"
android:fontFamily="@font/inter_extra_light"
tools:text="57K views • 1 day ago"
app:layout_constraintLeft_toLeftOf="@id/text_video_name"
app:layout_constraintTop_toBottomOf="@id/text_video_name"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginStart="0dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,56 @@
<?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="5dp"
android:layout_marginBottom="5dp"
android:id="@+id/root"
android:clickable="true">
<TextView
android:id="@+id/text_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginLeft="10dp"
android:textSize="20sp"
android:text="Albums" />
<ImageView
android:id="@+id/image_nav"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginRight="10dp"
app:layout_constraintTop_toTopOf="@id/text_label"
app:layout_constraintBottom_toBottomOf="@id/text_label"
app:layout_constraintRight_toRightOf="parent"
android:src="@drawable/ic_arrow_right" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_collection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/text_label"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="10dp"
android:orientation="horizontal" />
<!--
<ImageButton
android:id="@+id/button_play"
android:layout_width="34dp"
android:layout_height="34dp"
app:srcCompat="@drawable/ic_play_white_nopad"
android:scaleType="fitCenter"
android:padding="8dp"
app:layout_constraintRight_toLeftOf="@id/button_trash"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="10dp"/> -->
</androidx.constraintlayout.widget.ConstraintLayout>
+8
View File
@@ -23,6 +23,14 @@
<item name="cornerFamily">rounded</item>
<item name="cornerSize">16dp</item>
</style>
<style name="roundedCorners_30dp" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">30dp</item>
</style>
<style name="roundedCorners_33dp" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">33dp</item>
</style>
<style name="roundedCorners_40dp" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">40dp</item>