From f0be828bbfca870f69e71e17d9b6173288904aa7 Mon Sep 17 00:00:00 2001
From: James Wells <james@jameswells.net>
Date: Thu, 18 Mar 2021 23:23:00 -0400
Subject: [PATCH] Convert SelectAlbumFragment to kotlin

Signed-off-by: James Wells <james@jameswells.net>
---
 .../fragment/SelectAlbumFragment.java         | 1271 -----------------
 .../fragment/SelectAlbumFragment.kt           | 1145 +++++++++++++++
 2 files changed, 1145 insertions(+), 1271 deletions(-)
 delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectAlbumFragment.java
 create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumFragment.kt

diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectAlbumFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectAlbumFragment.java
deleted file mode 100644
index d66eb87d..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SelectAlbumFragment.java
+++ /dev/null
@@ -1,1271 +0,0 @@
-package org.moire.ultrasonic.fragment;
-
-import android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.navigation.Navigation;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import org.jetbrains.annotations.NotNull;
-import org.moire.ultrasonic.R;
-import org.moire.ultrasonic.data.ActiveServerProvider;
-import org.moire.ultrasonic.domain.MusicDirectory;
-import org.moire.ultrasonic.domain.Share;
-import org.moire.ultrasonic.service.DownloadFile;
-import org.moire.ultrasonic.service.MediaPlayerController;
-import org.moire.ultrasonic.service.MusicService;
-import org.moire.ultrasonic.service.MusicServiceFactory;
-import org.moire.ultrasonic.subsonic.DownloadHandler;
-import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
-import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker;
-import org.moire.ultrasonic.subsonic.ShareHandler;
-import org.moire.ultrasonic.subsonic.VideoPlayer;
-import org.moire.ultrasonic.util.AlbumHeader;
-import org.moire.ultrasonic.util.CancellationToken;
-import org.moire.ultrasonic.util.Constants;
-import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator;
-import org.moire.ultrasonic.util.Pair;
-import org.moire.ultrasonic.util.FragmentBackgroundTask;
-import org.moire.ultrasonic.util.Util;
-import org.moire.ultrasonic.view.AlbumView;
-import org.moire.ultrasonic.view.EntryAdapter;
-import org.moire.ultrasonic.view.SongView;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-
-import kotlin.Lazy;
-import timber.log.Timber;
-
-import static org.koin.java.KoinJavaComponent.inject;
-
-/**
- * Displays a group of playable media from the library, which can be an Album, a Playlist, etc.
- */
-public class SelectAlbumFragment extends Fragment {
-
-    public static final String allSongsId = "-1";
-    private SwipeRefreshLayout refreshAlbumListView;
-    private ListView albumListView;
-    private View header;
-    private View albumButtons;
-    private View emptyView;
-    private ImageView selectButton;
-    private ImageView playNowButton;
-    private ImageView playNextButton;
-    private ImageView playLastButton;
-    private ImageView pinButton;
-    private ImageView unpinButton;
-    private ImageView downloadButton;
-    private ImageView deleteButton;
-    private ImageView moreButton;
-    private boolean playAllButtonVisible;
-    private boolean shareButtonVisible;
-    private MenuItem playAllButton;
-    private MenuItem shareButton;
-    private boolean showHeader = true;
-    private final Random random = new java.security.SecureRandom();
-
-    private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
-    private final Lazy<VideoPlayer> videoPlayer = inject(VideoPlayer.class);
-    private final Lazy<DownloadHandler> downloadHandler = inject(DownloadHandler.class);
-    private final Lazy<NetworkAndStorageChecker> networkAndStorageChecker = inject(NetworkAndStorageChecker.class);
-    private final Lazy<ImageLoaderProvider> imageLoaderProvider = inject(ImageLoaderProvider.class);
-    private final Lazy<ShareHandler> shareHandler = inject(ShareHandler.class);
-    private CancellationToken cancellationToken;
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        Util.applyTheme(this.getContext());
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
-        return inflater.inflate(R.layout.select_album, container, false);
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        cancellationToken = new CancellationToken();
-
-        albumButtons = view.findViewById(R.id.menu_album);
-
-        refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh);
-        albumListView = view.findViewById(R.id.select_album_entries_list);
-
-        refreshAlbumListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
-        {
-            @Override
-            public void onRefresh() {
-                updateDisplay(true);
-            }
-        });
-
-        header = LayoutInflater.from(getContext()).inflate(R.layout.select_album_header, albumListView, false);
-
-        albumListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-        albumListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
-        {
-            @Override
-            public void onItemClick(AdapterView<?> parent, View view, int position, long id)
-            {
-                if (position >= 0)
-                {
-                    MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position);
-                    if (entry != null && entry.isDirectory())
-                    {
-                        Bundle bundle = new Bundle();
-                        bundle.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId());
-                        bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, entry.isDirectory());
-                        bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle());
-                        bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.getParent());
-                        Navigation.findNavController(view).navigate(R.id.selectAlbumFragment, bundle);
-                    }
-                    else if (entry != null && entry.isVideo())
-                    {
-                        videoPlayer.getValue().playVideo(entry);
-                    }
-                    else
-                    {
-                        enableButtons();
-                    }
-                }
-            }
-        });
-
-        // TODO: Long click on an item will first try to maximize / collapse the item, even when it fits inside the TextView.
-        // The context menu is only displayed on the second long click... This may be improved somehow, e.g. checking first if the texts fit
-        albumListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener(){
-            @Override
-            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
-                if (view instanceof AlbumView) {
-                    AlbumView albumView = (AlbumView) view;
-                    if (!albumView.isMaximized()) {
-                        albumView.maximizeOrMinimize();
-                        return true;
-                    } else {
-                        return false;
-                    }
-                }
-                if (view instanceof SongView) {
-                    SongView songView = (SongView) view;
-                    songView.maximizeOrMinimize();
-                    return true;
-                }
-                return false;
-            }
-        });
-
-        selectButton = view.findViewById(R.id.select_album_select);
-        playNowButton = view.findViewById(R.id.select_album_play_now);
-        playNextButton = view.findViewById(R.id.select_album_play_next);
-        playLastButton = view.findViewById(R.id.select_album_play_last);
-        pinButton = view.findViewById(R.id.select_album_pin);
-        unpinButton = view.findViewById(R.id.select_album_unpin);
-        downloadButton = view.findViewById(R.id.select_album_download);
-        deleteButton = view.findViewById(R.id.select_album_delete);
-        moreButton = view.findViewById(R.id.select_album_more);
-        emptyView = view.findViewById(R.id.select_album_empty);
-
-        selectButton.setOnClickListener(new View.OnClickListener()
-        {
-            @Override
-            public void onClick(View view)
-            {
-                selectAllOrNone();
-            }
-        });
-        playNowButton.setOnClickListener(new View.OnClickListener()
-        {
-            @Override
-            public void onClick(View view)
-            {
-                playNow(false);
-            }
-        });
-        playNextButton.setOnClickListener(new View.OnClickListener()
-        {
-            @Override
-            public void onClick(View view)
-            {
-                downloadHandler.getValue().download(SelectAlbumFragment.this,true, false, false, true, false, getSelectedSongs(albumListView));
-                selectAll(false, false);
-            }
-        });
-        playLastButton.setOnClickListener(new View.OnClickListener()
-        {
-            @Override
-            public void onClick(View view)
-            {
-                playNow(true);
-            }
-        });
-        pinButton.setOnClickListener(new View.OnClickListener()
-        {
-            @Override
-            public void onClick(View view)
-            {
-                downloadBackground(true);
-                selectAll(false, false);
-            }
-        });
-        unpinButton.setOnClickListener(new View.OnClickListener()
-        {
-            @Override
-            public void onClick(View view)
-            {
-                unpin();
-                selectAll(false, false);
-            }
-        });
-        downloadButton.setOnClickListener(new View.OnClickListener()
-        {
-            @Override
-            public void onClick(View view)
-            {
-                downloadBackground(false);
-                selectAll(false, false);
-            }
-        });
-        deleteButton.setOnClickListener(new View.OnClickListener()
-        {
-            @Override
-            public void onClick(View view)
-            {
-                delete();
-                selectAll(false, false);
-            }
-        });
-
-        registerForContextMenu(albumListView);
-        setHasOptionsMenu(true);
-        enableButtons();
-        updateDisplay(false);
-    }
-
-    private void updateDisplay(boolean refresh)
-    {
-        String id = getArguments().getString(Constants.INTENT_EXTRA_NAME_ID);
-        boolean isAlbum = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false);
-        String name = getArguments().getString(Constants.INTENT_EXTRA_NAME_NAME);
-        String parentId = getArguments().getString(Constants.INTENT_EXTRA_NAME_PARENT_ID);
-        String playlistId = getArguments().getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID);
-        String podcastChannelId = getArguments().getString(Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID);
-        String playlistName = getArguments().getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME);
-        String shareId = getArguments().getString(Constants.INTENT_EXTRA_NAME_SHARE_ID);
-        String shareName = getArguments().getString(Constants.INTENT_EXTRA_NAME_SHARE_NAME);
-        String albumListType = getArguments().getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
-        String genreName = getArguments().getString(Constants.INTENT_EXTRA_NAME_GENRE_NAME);
-        int albumListTitle = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, 0);
-        int getStarredTracks = getArguments().getInt(Constants.INTENT_EXTRA_NAME_STARRED, 0);
-        int getVideos = getArguments().getInt(Constants.INTENT_EXTRA_NAME_VIDEOS, 0);
-        int getRandomTracks = getArguments().getInt(Constants.INTENT_EXTRA_NAME_RANDOM, 0);
-        int albumListSize = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
-        int albumListOffset = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
-
-        if (playlistId != null)
-        {
-            getPlaylist(playlistId, playlistName);
-        }
-        else if (podcastChannelId != null) {
-            getPodcastEpisodes(podcastChannelId);
-        }
-        else if (shareId != null)
-        {
-            getShare(shareId, shareName);
-        }
-        else if (albumListType != null)
-        {
-            getAlbumList(albumListType, albumListTitle, albumListSize, albumListOffset);
-        }
-        else if (genreName != null)
-        {
-            getSongsForGenre(genreName, albumListSize, albumListOffset);
-        }
-        else if (getStarredTracks != 0)
-        {
-            getStarred();
-        }
-        else if (getVideos != 0)
-        {
-            getVideos(refresh);
-        }
-        else if (getRandomTracks != 0)
-        {
-            getRandom(albumListSize);
-        }
-        else
-        {
-            if (!ActiveServerProvider.Companion.isOffline(getActivity()) && Util.getShouldUseId3Tags(getActivity()))
-            {
-                if (isAlbum)
-                {
-                    getAlbum(refresh, id, name, parentId);
-                }
-                else
-                {
-                    getArtist(refresh, id, name);
-                }
-            }
-            else
-            {
-                getMusicDirectory(refresh, id, name, parentId);
-            }
-        }
-    }
-
-    @Override
-    public void onCreateContextMenu(@NotNull ContextMenu menu, @NotNull View view, ContextMenu.ContextMenuInfo menuInfo)
-    {
-        super.onCreateContextMenu(menu, view, menuInfo);
-        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
-
-        MusicDirectory.Entry entry = (MusicDirectory.Entry) albumListView.getItemAtPosition(info.position);
-
-        if (entry != null && entry.isDirectory())
-        {
-            MenuInflater inflater = getActivity().getMenuInflater();
-            inflater.inflate(R.menu.select_album_context, menu);
-        }
-
-        shareButton = menu.findItem(R.id.menu_item_share);
-
-        if (shareButton != null)
-        {
-            shareButton.setVisible(!ActiveServerProvider.Companion.isOffline(getContext()));
-        }
-
-        MenuItem downloadMenuItem = menu.findItem(R.id.album_menu_download);
-
-        if (downloadMenuItem != null)
-        {
-            downloadMenuItem.setVisible(!ActiveServerProvider.Companion.isOffline(getContext()));
-        }
-    }
-
-    @Override
-    public boolean onContextItemSelected(MenuItem menuItem)
-    {
-        Timber.d("onContextItemSelected");
-        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
-
-        if (info == null)
-        {
-            return true;
-        }
-
-        MusicDirectory.Entry entry = (MusicDirectory.Entry) albumListView.getItemAtPosition(info.position);
-
-        if (entry == null)
-        {
-            return true;
-        }
-
-        String entryId = entry.getId();
-
-        int itemId = menuItem.getItemId();
-        if (itemId == R.id.album_menu_play_now) {
-            downloadHandler.getValue().downloadRecursively(this, entryId, false, false, true, false, false, false, false, false);
-        } else if (itemId == R.id.album_menu_play_next) {
-            downloadHandler.getValue().downloadRecursively(this, entryId, false, false, false, false, false, true, false, false);
-        } else if (itemId == R.id.album_menu_play_last) {
-            downloadHandler.getValue().downloadRecursively(this, entryId, false, true, false, false, false, false, false, false);
-        } else if (itemId == R.id.album_menu_pin) {
-            downloadHandler.getValue().downloadRecursively(this, entryId, true, true, false, false, false, false, false, false);
-        } else if (itemId == R.id.album_menu_unpin) {
-            downloadHandler.getValue().downloadRecursively(this, entryId, false, false, false, false, false, false, true, false);
-        } else if (itemId == R.id.album_menu_download) {
-            downloadHandler.getValue().downloadRecursively(this, entryId, false, false, false, false, true, false, false, false);
-        } else if (itemId == R.id.select_album_play_all) {
-            playAll();
-        } else if (itemId == R.id.menu_item_share) {
-            List<MusicDirectory.Entry> entries = new ArrayList<>(1);
-            entries.add(entry);
-            shareHandler.getValue().createShare(this, entries, refreshAlbumListView, cancellationToken);
-            return true;
-        } else {
-            return super.onContextItemSelected(menuItem);
-        }
-        return true;
-    }
-
-    @Override
-    public void onPrepareOptionsMenu(@NotNull Menu menu)
-    {
-        super.onPrepareOptionsMenu(menu);
-        playAllButton = menu.findItem(R.id.select_album_play_all);
-
-        if (playAllButton != null)
-        {
-            playAllButton.setVisible(playAllButtonVisible);
-        }
-
-        shareButton = menu.findItem(R.id.menu_item_share);
-
-        if (shareButton != null)
-        {
-            shareButton.setVisible(shareButtonVisible);
-        }
-    }
-
-    @Override
-    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
-        inflater.inflate(R.menu.select_album, menu);
-        super.onCreateOptionsMenu(menu, inflater);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item)
-    {
-        int itemId = item.getItemId();
-        if (itemId == R.id.select_album_play_all) {
-            playAll();
-            return true;
-        } else if (itemId == R.id.menu_item_share) {
-            shareHandler.getValue().createShare(this, getSelectedSongs(albumListView), refreshAlbumListView, cancellationToken);
-            return true;
-        }
-
-        return false;
-    }
-
-    @Override
-    public void onDestroyView() {
-        cancellationToken.cancel();
-        super.onDestroyView();
-    }
-
-    private void playNow(final boolean append)
-    {
-        List<MusicDirectory.Entry> selectedSongs = getSelectedSongs(albumListView);
-
-        if (!selectedSongs.isEmpty())
-        {
-            downloadHandler.getValue().download(this, append, false, !append, false, false, selectedSongs);
-            selectAll(false, false);
-        }
-        else
-        {
-            playAll(false, append);
-        }
-    }
-
-    private void playAll()
-    {
-        playAll(false, false);
-    }
-
-    private void playAll(final boolean shuffle, final boolean append)
-    {
-        boolean hasSubFolders = false;
-
-        for (int i = 0; i < albumListView.getCount(); i++)
-        {
-            MusicDirectory.Entry entry = (MusicDirectory.Entry) albumListView.getItemAtPosition(i);
-            if (entry != null && entry.isDirectory())
-            {
-                hasSubFolders = true;
-                break;
-            }
-        }
-
-        boolean isArtist = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, false);
-        String id = getArguments().getString(Constants.INTENT_EXTRA_NAME_ID);
-
-        if (hasSubFolders && id != null)
-        {
-            downloadHandler.getValue().downloadRecursively(this, id, false, append, !append, shuffle, false, false, false, isArtist);
-        }
-        else
-        {
-            selectAll(true, false);
-            downloadHandler.getValue().download(this, append, false, !append, false, shuffle, getSelectedSongs(albumListView));
-            selectAll(false, false);
-        }
-    }
-
-    private static List<MusicDirectory.Entry> getSelectedSongs(ListView albumListView)
-    {
-        List<MusicDirectory.Entry> songs = new ArrayList<>(10);
-
-        if (albumListView != null)
-        {
-            int count = albumListView.getCount();
-            for (int i = 0; i < count; i++)
-            {
-                if (albumListView.isItemChecked(i))
-                {
-                    songs.add((MusicDirectory.Entry) albumListView.getItemAtPosition(i));
-                }
-            }
-        }
-
-        return songs;
-    }
-
-    private void getMusicDirectory(final boolean refresh, final String id, final String name, final String parentId)
-    {
-        FragmentTitle.Companion.setTitle(this, name);
-
-        new LoadTask()
-        {
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                MusicDirectory root = new MusicDirectory();
-
-                if (allSongsId.equals(id))
-                {
-                    MusicDirectory musicDirectory = service.getMusicDirectory(parentId, name, refresh, getContext());
-
-                    List<MusicDirectory.Entry> songs = new LinkedList<>();
-                    getSongsRecursively(musicDirectory, songs);
-
-                    for (MusicDirectory.Entry song : songs)
-                    {
-                        if (!song.isDirectory())
-                        {
-                            root.addChild(song);
-                        }
-                    }
-                }
-                else
-                {
-                    MusicDirectory musicDirectory = service.getMusicDirectory(id, name, refresh, getContext());
-
-                    if (Util.getShouldShowAllSongsByArtist(getContext()) && musicDirectory.findChild(allSongsId) == null && musicDirectory.getChildren(true, false).size() == musicDirectory.getChildren(true, true).size())
-                    {
-                        MusicDirectory.Entry allSongs = new MusicDirectory.Entry();
-
-                        allSongs.setDirectory(true);
-                        allSongs.setArtist(name);
-                        allSongs.setParent(id);
-                        allSongs.setId(allSongsId);
-                        allSongs.setTitle(String.format(getResources().getString(R.string.select_album_all_songs), name));
-
-                        root.addChild(allSongs);
-
-                        List<MusicDirectory.Entry> children = musicDirectory.getChildren();
-
-                        if (children != null)
-                        {
-                            root.addAll(children);
-                        }
-                    }
-                    else
-                    {
-                        root = musicDirectory;
-                    }
-                }
-
-                return root;
-            }
-
-            private void getSongsRecursively(MusicDirectory parent, List<MusicDirectory.Entry> songs) throws Exception
-            {
-                for (MusicDirectory.Entry song : parent.getChildren(false, true))
-                {
-                    if (!song.isVideo() && !song.isDirectory())
-                    {
-                        songs.add(song);
-                    }
-                }
-
-                MusicService musicService = MusicServiceFactory.getMusicService(getContext());
-
-                for (MusicDirectory.Entry dir : parent.getChildren(true, false))
-                {
-                    MusicDirectory root;
-
-                    if (!allSongsId.equals(dir.getId()))
-                    {
-                        root = musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, getContext());
-
-                        getSongsRecursively(root, songs);
-                    }
-                }
-            }
-        }.execute();
-    }
-
-    private void getArtist(final boolean refresh, final String id, final String name)
-    {
-        FragmentTitle.Companion.setTitle(this, name);
-
-        new LoadTask()
-        {
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                MusicDirectory root = new MusicDirectory();
-
-                MusicDirectory musicDirectory = service.getArtist(id, name, refresh, getContext());
-
-                if (Util.getShouldShowAllSongsByArtist(getContext()) && musicDirectory.findChild(allSongsId) == null && musicDirectory.getChildren(true, false).size() == musicDirectory.getChildren(true, true).size())
-                {
-                    MusicDirectory.Entry allSongs = new MusicDirectory.Entry();
-
-                    allSongs.setDirectory(true);
-                    allSongs.setArtist(name);
-                    allSongs.setParent(id);
-                    allSongs.setId(allSongsId);
-                    allSongs.setTitle(String.format(getResources().getString(R.string.select_album_all_songs), name));
-
-                    root.addFirst(allSongs);
-
-                    List<MusicDirectory.Entry> children = musicDirectory.getChildren();
-
-                    if (children != null)
-                    {
-                        root.addAll(children);
-                    }
-                }
-                else
-                {
-                    root = musicDirectory;
-                }
-
-                return root;
-            }
-        }.execute();
-    }
-
-    private void getAlbum(final boolean refresh, final String id, final String name, final String parentId)
-    {
-        FragmentTitle.Companion.setTitle(this, name);
-
-        new LoadTask()
-        {
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                MusicDirectory musicDirectory;
-
-                if (allSongsId.equals(id))
-                {
-                    MusicDirectory root = new MusicDirectory();
-
-                    Collection<MusicDirectory.Entry> songs = new LinkedList<>();
-                    getSongsForArtist(parentId, songs);
-
-                    for (MusicDirectory.Entry song : songs)
-                    {
-                        if (!song.isDirectory())
-                        {
-                            root.addChild(song);
-                        }
-                    }
-
-                    musicDirectory = root;
-                }
-                else
-                {
-                    musicDirectory = service.getAlbum(id, name, refresh, getContext());
-                }
-
-                return musicDirectory;
-            }
-
-            private void getSongsForArtist(String id, Collection<MusicDirectory.Entry> songs) throws Exception
-            {
-                MusicService musicService = MusicServiceFactory.getMusicService(getContext());
-                MusicDirectory artist = musicService.getArtist(id, "", false, getContext());
-
-                for (MusicDirectory.Entry album : artist.getChildren())
-                {
-                    if (!allSongsId.equals(album.getId()))
-                    {
-                        MusicDirectory albumDirectory = musicService.getAlbum(album.getId(), "", false, getContext());
-
-                        for (MusicDirectory.Entry song : albumDirectory.getChildren())
-                        {
-                            if (!song.isVideo())
-                            {
-                                songs.add(song);
-                            }
-                        }
-                    }
-                }
-            }
-        }.execute();
-    }
-
-    private void getSongsForGenre(final String genre, final int count, final int offset)
-    {
-        FragmentTitle.Companion.setTitle(this, genre);
-
-        new LoadTask()
-        {
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                return service.getSongsByGenre(genre, count, offset, getContext());
-            }
-
-            @Override
-            protected void done(Pair<MusicDirectory, Boolean> result)
-            {
-                // Hide more button when results are less than album list size
-                if (result.getFirst().getChildren().size() < getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0))
-                {
-                    moreButton.setVisibility(View.GONE);
-                }
-                else
-                {
-                    moreButton.setVisibility(View.VISIBLE);
-                }
-
-                moreButton.setOnClickListener(new View.OnClickListener()
-                {
-                    @Override
-                    public void onClick(View view)
-                    {
-                        String genre = getArguments().getString(Constants.INTENT_EXTRA_NAME_GENRE_NAME);
-                        int size = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
-                        int offset = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0) + size;
-
-                        Bundle bundle = new Bundle();
-                        bundle.putString(Constants.INTENT_EXTRA_NAME_GENRE_NAME, genre);
-                        bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, size);
-                        bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, offset);
-                        Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
-                    }
-                });
-
-                super.done(result);
-            }
-        }.execute();
-    }
-
-    private void getStarred()
-    {
-        FragmentTitle.Companion.setTitle(this, R.string.main_songs_starred);
-
-        new LoadTask()
-        {
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                return Util.getShouldUseId3Tags(getContext()) ? Util.getSongsFromSearchResult(service.getStarred2(getContext())) : Util.getSongsFromSearchResult(service.getStarred(getContext()));
-            }
-        }.execute();
-    }
-
-    private void getVideos(final boolean refresh)
-    {
-        showHeader = false;
-
-        FragmentTitle.Companion.setTitle(this, R.string.main_videos);
-
-        new LoadTask()
-        {
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                return service.getVideos(refresh, getContext());
-            }
-        }.execute();
-    }
-
-    private void getRandom(final int size)
-    {
-        FragmentTitle.Companion.setTitle(this, R.string.main_songs_random);
-
-        new LoadTask()
-        {
-            @Override
-            protected boolean sortableCollection() {
-                return false;
-            }
-
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                return service.getRandomSongs(size, getContext());
-            }
-        }.execute();
-    }
-
-    private void getPlaylist(final String playlistId, final String playlistName)
-    {
-        FragmentTitle.Companion.setTitle(this, playlistName);
-
-        new LoadTask()
-        {
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                return service.getPlaylist(playlistId, playlistName, getContext());
-            }
-        }.execute();
-    }
-
-    private void getPodcastEpisodes(final String podcastChannelId)
-    {
-        FragmentTitle.Companion.setTitle(this, R.string.podcasts_label);
-
-        new LoadTask()
-        {
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                return service.getPodcastEpisodes(podcastChannelId, getContext());
-            }
-        }.execute();
-    }
-
-    private void getShare(final String shareId, final CharSequence shareName)
-    {
-        FragmentTitle.Companion.setTitle(this, shareName);
-        //setActionBarSubtitle(shareName);
-
-        new LoadTask()
-        {
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                List<Share> shares = service.getShares(true, getContext());
-
-                MusicDirectory md = new MusicDirectory();
-
-                for (Share share : shares)
-                {
-                    if (share.getId().equals(shareId))
-                    {
-                        for (MusicDirectory.Entry entry : share.getEntries())
-                        {
-                            md.addChild(entry);
-                        }
-
-                        break;
-                    }
-                }
-
-                return md;
-            }
-        }.execute();
-    }
-
-    private void getAlbumList(final String albumListType, final int albumListTitle, final int size, final int offset)
-    {
-        showHeader = false;
-
-        FragmentTitle.Companion.setTitle(this, albumListTitle);
-        //setActionBarSubtitle(albumListTitle);
-
-        new LoadTask()
-        {
-            @Override
-            protected boolean sortableCollection() {
-                return !albumListType.equals("newest") && !albumListType.equals("random") &&
-                    !albumListType.equals("highest") && !albumListType.equals("recent") &&
-                    !albumListType.equals("frequent");
-            }
-
-            @Override
-            protected MusicDirectory load(MusicService service) throws Exception
-            {
-                return Util.getShouldUseId3Tags(getContext()) ? service.getAlbumList2(albumListType, size, offset, getContext()) : service.getAlbumList(albumListType, size, offset, getContext());
-            }
-
-            @Override
-            protected void done(Pair<MusicDirectory, Boolean> result)
-            {
-                if (!result.getFirst().getChildren().isEmpty())
-                {
-                    pinButton.setVisibility(View.GONE);
-                    unpinButton.setVisibility(View.GONE);
-                    downloadButton.setVisibility(View.GONE);
-                    deleteButton.setVisibility(View.GONE);
-
-                    // Hide more button when results are less than album list size
-                    if (result.getFirst().getChildren().size() < getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0))
-                    {
-                        moreButton.setVisibility(View.GONE);
-                    }
-                    else
-                    {
-                        moreButton.setVisibility(View.VISIBLE);
-
-                        moreButton.setOnClickListener(new View.OnClickListener()
-                        {
-                            @Override
-                            public void onClick(View view)
-                            {
-                                int albumListTitle = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, 0);
-                                String type = getArguments().getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
-                                int size = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
-                                int offset = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0) + size;
-
-                                Bundle bundle = new Bundle();
-                                bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, albumListTitle);
-                                bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
-                                bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, size);
-                                bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, offset);
-                                Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
-                            }
-                        });
-                    }
-                }
-                else
-                {
-                    moreButton.setVisibility(View.GONE);
-                }
-
-                super.done(result);
-            }
-        }.execute();
-    }
-
-    private void selectAllOrNone()
-    {
-        boolean someUnselected = false;
-        int count = albumListView.getCount();
-
-        for (int i = 0; i < count; i++)
-        {
-            if (!albumListView.isItemChecked(i) && albumListView.getItemAtPosition(i) instanceof MusicDirectory.Entry)
-            {
-                someUnselected = true;
-                break;
-            }
-        }
-
-        selectAll(someUnselected, true);
-    }
-
-    private void selectAll(boolean selected, boolean toast)
-    {
-        int count = albumListView.getCount();
-        int selectedCount = 0;
-
-        for (int i = 0; i < count; i++)
-        {
-            MusicDirectory.Entry entry = (MusicDirectory.Entry) albumListView.getItemAtPosition(i);
-            if (entry != null && !entry.isDirectory() && !entry.isVideo())
-            {
-                albumListView.setItemChecked(i, selected);
-                selectedCount++;
-            }
-        }
-
-        // Display toast: N tracks selected / N tracks unselected
-        if (toast)
-        {
-            int toastResId = selected ? R.string.select_album_n_selected : R.string.select_album_n_unselected;
-            Util.toast(getActivity(), getString(toastResId, selectedCount));
-        }
-
-        enableButtons();
-    }
-
-    private void enableButtons()
-    {
-        List<MusicDirectory.Entry> selection = getSelectedSongs(albumListView);
-        boolean enabled = !selection.isEmpty();
-        boolean unpinEnabled = false;
-        boolean deleteEnabled = false;
-
-        int pinnedCount = 0;
-
-        for (MusicDirectory.Entry song : selection)
-        {
-            DownloadFile downloadFile = mediaPlayerControllerLazy.getValue().getDownloadFileForSong(song);
-            if (downloadFile.isWorkDone())
-            {
-                deleteEnabled = true;
-            }
-
-            if (downloadFile.isSaved())
-            {
-                pinnedCount++;
-                unpinEnabled = true;
-            }
-        }
-
-        playNowButton.setVisibility(enabled ? View.VISIBLE : View.GONE);
-        playNextButton.setVisibility(enabled ? View.VISIBLE : View.GONE);
-        playLastButton.setVisibility(enabled ? View.VISIBLE : View.GONE);
-        pinButton.setVisibility((enabled && !ActiveServerProvider.Companion.isOffline(getContext()) && selection.size() > pinnedCount) ? View.VISIBLE : View.GONE);
-        unpinButton.setVisibility(enabled && unpinEnabled ? View.VISIBLE : View.GONE);
-        downloadButton.setVisibility(enabled && !deleteEnabled && !ActiveServerProvider.Companion.isOffline(getContext()) ? View.VISIBLE : View.GONE);
-        deleteButton.setVisibility(enabled && deleteEnabled ? View.VISIBLE : View.GONE);
-    }
-
-    private void downloadBackground(final boolean save)
-    {
-        List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
-
-        if (songs.isEmpty())
-        {
-            selectAll(true, false);
-            songs = getSelectedSongs(albumListView);
-        }
-
-        downloadBackground(save, songs);
-    }
-
-    private void downloadBackground(final boolean save, final List<MusicDirectory.Entry> songs)
-    {
-        Runnable onValid = new Runnable()
-        {
-            @Override
-            public void run()
-            {
-                networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable();
-                mediaPlayerControllerLazy.getValue().downloadBackground(songs, save);
-
-                if (save)
-                {
-                    Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size()));
-                }
-                else
-                {
-                    Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size()));
-                }
-            }
-        };
-        onValid.run();
-    }
-
-    private void delete()
-    {
-        List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
-
-        if (songs.isEmpty())
-        {
-            selectAll(true, false);
-            songs = getSelectedSongs(albumListView);
-        }
-
-        mediaPlayerControllerLazy.getValue().delete(songs);
-    }
-
-    private void unpin()
-    {
-        List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
-        Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_unpinned, songs.size(), songs.size()));
-        mediaPlayerControllerLazy.getValue().unpin(songs);
-    }
-
-    private abstract class LoadTask extends FragmentBackgroundTask<Pair<MusicDirectory, Boolean>>
-    {
-
-        public LoadTask()
-        {
-            super(SelectAlbumFragment.this.getActivity(), true, refreshAlbumListView, cancellationToken);
-        }
-
-        protected abstract MusicDirectory load(MusicService service) throws Exception;
-
-        protected boolean sortableCollection() {
-            return true;
-        }
-
-        @Override
-        protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable
-        {
-            MusicService musicService = MusicServiceFactory.getMusicService(getContext());
-            MusicDirectory dir = load(musicService);
-            boolean valid = musicService.isLicenseValid(getContext());
-            return new Pair<>(dir, valid);
-        }
-
-        @Override
-        protected void done(Pair<MusicDirectory, Boolean> result)
-        {
-            MusicDirectory musicDirectory = result.getFirst();
-            List<MusicDirectory.Entry> entries = musicDirectory.getChildren();
-
-            if (sortableCollection() && Util.getShouldSortByDisc(getContext()))
-            {
-                Collections.sort(entries, new EntryByDiscAndTrackComparator());
-            }
-
-            boolean allVideos = true;
-            int songCount = 0;
-
-            for (MusicDirectory.Entry entry : entries)
-            {
-                if (!entry.isVideo())
-                {
-                    allVideos = false;
-                }
-
-                if (!entry.isDirectory())
-                {
-                    songCount++;
-                }
-            }
-
-            final int listSize = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
-
-            if (songCount > 0)
-            {
-                if (showHeader)
-                {
-                    String intentAlbumName = getArguments().getString(Constants.INTENT_EXTRA_NAME_NAME);
-                    String directoryName = musicDirectory.getName();
-                    View header = createHeader(entries, intentAlbumName != null ? intentAlbumName : directoryName, songCount);
-
-                    if (header != null && albumListView.getHeaderViewsCount() == 0)
-                    {
-                        albumListView.addHeaderView(header, null, false);
-                    }
-                }
-
-                pinButton.setVisibility(View.VISIBLE);
-                unpinButton.setVisibility(View.VISIBLE);
-                downloadButton.setVisibility(View.VISIBLE);
-                deleteButton.setVisibility(View.VISIBLE);
-                selectButton.setVisibility(allVideos ? View.GONE : View.VISIBLE);
-                playNowButton.setVisibility(View.VISIBLE);
-                playNextButton.setVisibility(View.VISIBLE);
-                playLastButton.setVisibility(View.VISIBLE);
-
-                if (listSize == 0 || songCount < listSize)
-                {
-                    moreButton.setVisibility(View.GONE);
-                }
-                else
-                {
-                    moreButton.setVisibility(View.VISIBLE);
-
-                    if (getArguments().getInt(Constants.INTENT_EXTRA_NAME_RANDOM, 0) > 0)
-                    {
-                        moreButton.setOnClickListener(new View.OnClickListener()
-                        {
-                            @Override
-                            public void onClick(View view)
-                            {
-                                int offset = getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0) + listSize;
-
-                                Bundle bundle = new Bundle();
-                                bundle.putInt(Constants.INTENT_EXTRA_NAME_RANDOM, 1);
-                                bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, listSize);
-                                bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, offset);
-                                Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
-                            }
-                        });
-                    }
-                }
-            }
-            else
-            {
-                pinButton.setVisibility(View.GONE);
-                unpinButton.setVisibility(View.GONE);
-                downloadButton.setVisibility(View.GONE);
-                deleteButton.setVisibility(View.GONE);
-                selectButton.setVisibility(View.GONE);
-                playNowButton.setVisibility(View.GONE);
-                playNextButton.setVisibility(View.GONE);
-                playLastButton.setVisibility(View.GONE);
-
-                if (listSize == 0 || result.getFirst().getChildren().size() < listSize)
-                {
-                    albumButtons.setVisibility(View.GONE);
-                }
-                else
-                {
-                    moreButton.setVisibility(View.VISIBLE);
-                }
-            }
-
-            enableButtons();
-
-            boolean isAlbumList = getArguments().containsKey(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
-            playAllButtonVisible = !(isAlbumList || entries.isEmpty()) && !allVideos;
-            shareButtonVisible = !ActiveServerProvider.Companion.isOffline(getContext()) && songCount > 0;
-
-            emptyView.setVisibility(entries.isEmpty() ? View.VISIBLE : View.GONE);
-
-            if (playAllButton != null)
-            {
-                playAllButton.setVisible(playAllButtonVisible);
-            }
-
-            if (shareButton != null)
-            {
-                shareButton.setVisible(shareButtonVisible);
-            }
-
-            albumListView.setAdapter(new EntryAdapter(getContext(), imageLoaderProvider.getValue().getImageLoader(), entries, true));
-
-            boolean playAll = getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
-            if (playAll && songCount > 0)
-            {
-                playAll(getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, false), false);
-            }
-        }
-
-        protected View createHeader(List<MusicDirectory.Entry> entries, CharSequence name, int songCount)
-        {
-            ImageView coverArtView = (ImageView) header.findViewById(R.id.select_album_art);
-            int artworkSelection = random.nextInt(entries.size());
-            imageLoaderProvider.getValue().getImageLoader().loadImage(coverArtView, entries.get(artworkSelection), false, Util.getAlbumImageSize(getContext()), false, true);
-
-            AlbumHeader albumHeader = AlbumHeader.processEntries(getContext(), entries);
-
-            TextView titleView = (TextView) header.findViewById(R.id.select_album_title);
-            titleView.setText(name != null ? name : FragmentTitle.Companion.getTitle(SelectAlbumFragment.this)); //getActionBarSubtitle());
-
-            // Don't show a header if all entries are videos
-            if (albumHeader.getIsAllVideo())
-            {
-                return null;
-            }
-
-            TextView artistView = header.findViewById(R.id.select_album_artist);
-            String artist;
-
-            artist = albumHeader.getArtists().size() == 1 ? albumHeader.getArtists().iterator().next() : albumHeader.getGrandParents().size() == 1 ? albumHeader.getGrandParents().iterator().next() : getResources().getString(R.string.common_various_artists);
-
-            artistView.setText(artist);
-
-            TextView genreView = header.findViewById(R.id.select_album_genre);
-            String genre;
-
-            genre = albumHeader.getGenres().size() == 1 ? albumHeader.getGenres().iterator().next() : getResources().getString(R.string.common_multiple_genres);
-
-            genreView.setText(genre);
-
-            TextView yearView = header.findViewById(R.id.select_album_year);
-            String year;
-
-            year = albumHeader.getYears().size() == 1 ? albumHeader.getYears().iterator().next().toString() : getResources().getString(R.string.common_multiple_years);
-
-            yearView.setText(year);
-
-            TextView songCountView = header.findViewById(R.id.select_album_song_count);
-            String songs = getResources().getQuantityString(R.plurals.select_album_n_songs, songCount, songCount);
-            songCountView.setText(songs);
-
-            String duration = Util.formatTotalDuration(albumHeader.getTotalDuration());
-
-            TextView durationView = header.findViewById(R.id.select_album_duration);
-            durationView.setText(duration);
-
-            return header;
-        }
-    }
-}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumFragment.kt
new file mode 100644
index 00000000..4c96c1c6
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SelectAlbumFragment.kt
@@ -0,0 +1,1145 @@
+package org.moire.ultrasonic.fragment
+
+import android.os.Bundle
+import android.view.ContextMenu
+import android.view.ContextMenu.ContextMenuInfo
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.widget.AdapterView.AdapterContextMenuInfo
+import android.widget.AdapterView.OnItemClickListener
+import android.widget.AdapterView.OnItemLongClickListener
+import android.widget.ImageView
+import android.widget.ListView
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.navigation.Navigation
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
+import java.security.SecureRandom
+import java.util.Collections
+import java.util.LinkedList
+import java.util.Random
+import org.koin.android.ext.android.inject
+import org.moire.ultrasonic.R
+import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
+import org.moire.ultrasonic.domain.MusicDirectory
+import org.moire.ultrasonic.fragment.FragmentTitle.Companion.getTitle
+import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
+import org.moire.ultrasonic.service.MediaPlayerController
+import org.moire.ultrasonic.service.MusicService
+import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
+import org.moire.ultrasonic.subsonic.DownloadHandler
+import org.moire.ultrasonic.subsonic.ImageLoaderProvider
+import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
+import org.moire.ultrasonic.subsonic.ShareHandler
+import org.moire.ultrasonic.subsonic.VideoPlayer
+import org.moire.ultrasonic.util.AlbumHeader
+import org.moire.ultrasonic.util.CancellationToken
+import org.moire.ultrasonic.util.Constants
+import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
+import org.moire.ultrasonic.util.FragmentBackgroundTask
+import org.moire.ultrasonic.util.Util
+import org.moire.ultrasonic.view.AlbumView
+import org.moire.ultrasonic.view.EntryAdapter
+import org.moire.ultrasonic.view.SongView
+import timber.log.Timber
+
+/**
+ * Displays a group of playable media from the library, which can be an Album, a Playlist, etc.
+ */
+class SelectAlbumFragment : Fragment() {
+
+    private val allSongsId = "-1"
+    private var refreshAlbumListView: SwipeRefreshLayout? = null
+    private var albumListView: ListView? = null
+    private var header: View? = null
+    private var albumButtons: View? = null
+    private var emptyView: View? = null
+    private var selectButton: ImageView? = null
+    private var playNowButton: ImageView? = null
+    private var playNextButton: ImageView? = null
+    private var playLastButton: ImageView? = null
+    private var pinButton: ImageView? = null
+    private var unpinButton: ImageView? = null
+    private var downloadButton: ImageView? = null
+    private var deleteButton: ImageView? = null
+    private var moreButton: ImageView? = null
+    private var playAllButtonVisible = false
+    private var shareButtonVisible = false
+    private var playAllButton: MenuItem? = null
+    private var shareButton: MenuItem? = null
+    private var showHeader = true
+    private val random: Random = SecureRandom()
+
+    private val mediaPlayerController: MediaPlayerController by inject()
+    private val videoPlayer: VideoPlayer by inject()
+    private val downloadHandler: DownloadHandler by inject()
+    private val networkAndStorageChecker: NetworkAndStorageChecker by inject()
+    private val imageLoaderProvider: ImageLoaderProvider by inject()
+    private val shareHandler: ShareHandler by inject()
+    private var cancellationToken: CancellationToken? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        Util.applyTheme(this.context)
+        super.onCreate(savedInstanceState)
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return inflater.inflate(R.layout.select_album, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        cancellationToken = CancellationToken()
+
+        albumButtons = view.findViewById(R.id.menu_album)
+
+        refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh)
+        albumListView = view.findViewById(R.id.select_album_entries_list)
+
+        refreshAlbumListView!!.setOnRefreshListener(
+            OnRefreshListener
+            {
+                updateDisplay(true)
+            }
+        )
+
+        header = LayoutInflater.from(context).inflate(
+            R.layout.select_album_header, albumListView,
+            false
+        )
+
+        albumListView!!.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE)
+        albumListView!!.setOnItemClickListener(
+            OnItemClickListener
+            { parent, theView, position, _ ->
+                if (position >= 0) {
+                    val entry = parent.getItemAtPosition(position) as MusicDirectory.Entry?
+                    if (entry != null && entry.isDirectory) {
+                        val bundle = Bundle()
+                        bundle.putString(Constants.INTENT_EXTRA_NAME_ID, entry.id)
+                        bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, entry.isDirectory)
+                        bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.title)
+                        bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.parent)
+                        Navigation.findNavController(theView).navigate(
+                            R.id.selectAlbumFragment,
+                            bundle
+                        )
+                    } else if (entry != null && entry.isVideo) {
+                        videoPlayer.playVideo(entry)
+                    } else {
+                        enableButtons()
+                    }
+                }
+            }
+        )
+
+        // TODO Long click on an item will first try to maximize / collapse the item, even when it
+        // fits inside the TextView. The context menu is only displayed on the second long click...
+        // This may be improved somehow, e.g. checking first if the texts fit
+        albumListView!!.setOnItemLongClickListener(
+            OnItemLongClickListener
+            { _, theView, _, _ ->
+                if (theView is AlbumView) {
+                    val albumView = theView
+                    if (!albumView.isMaximized) {
+                        albumView.maximizeOrMinimize()
+                        return@OnItemLongClickListener true
+                    } else {
+                        return@OnItemLongClickListener false
+                    }
+                }
+                if (theView is SongView) {
+                    theView.maximizeOrMinimize()
+                    return@OnItemLongClickListener true
+                }
+                false
+            }
+        )
+
+        selectButton = view.findViewById(R.id.select_album_select)
+        playNowButton = view.findViewById(R.id.select_album_play_now)
+        playNextButton = view.findViewById(R.id.select_album_play_next)
+        playLastButton = view.findViewById(R.id.select_album_play_last)
+        pinButton = view.findViewById(R.id.select_album_pin)
+        unpinButton = view.findViewById(R.id.select_album_unpin)
+        downloadButton = view.findViewById(R.id.select_album_download)
+        deleteButton = view.findViewById(R.id.select_album_delete)
+        moreButton = view.findViewById(R.id.select_album_more)
+        emptyView = view.findViewById(R.id.select_album_empty)
+
+        selectButton!!.setOnClickListener(
+            View.OnClickListener
+            {
+                selectAllOrNone()
+            }
+        )
+        playNowButton!!.setOnClickListener(
+            View.OnClickListener
+            {
+                playNow(false)
+            }
+        )
+        playNextButton!!.setOnClickListener(
+            View.OnClickListener
+            {
+                downloadHandler.download(
+                    this@SelectAlbumFragment, true,
+                    false, false, true, false,
+                    getSelectedSongs(albumListView)
+                )
+                selectAll(false, false)
+            }
+        )
+        playLastButton!!.setOnClickListener(
+            View.OnClickListener
+            {
+                playNow(true)
+            }
+        )
+        pinButton!!.setOnClickListener(
+            View.OnClickListener
+            {
+                downloadBackground(true)
+                selectAll(false, false)
+            }
+        )
+        unpinButton!!.setOnClickListener(
+            View.OnClickListener
+            {
+                unpin()
+                selectAll(false, false)
+            }
+        )
+        downloadButton!!.setOnClickListener(
+            View.OnClickListener
+            {
+                downloadBackground(false)
+                selectAll(false, false)
+            }
+        )
+        deleteButton!!.setOnClickListener(
+            View.OnClickListener
+            {
+                delete()
+                selectAll(false, false)
+            }
+        )
+
+        registerForContextMenu(albumListView!!)
+        setHasOptionsMenu(true)
+        enableButtons()
+        updateDisplay(false)
+    }
+
+    private fun updateDisplay(refresh: Boolean) {
+        val id = requireArguments().getString(Constants.INTENT_EXTRA_NAME_ID)
+        val isAlbum = requireArguments().getBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false)
+        val name = requireArguments().getString(Constants.INTENT_EXTRA_NAME_NAME)
+        val parentId = requireArguments().getString(Constants.INTENT_EXTRA_NAME_PARENT_ID)
+        val playlistId = requireArguments().getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID)
+        val podcastChannelId = requireArguments().getString(
+            Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID
+        )
+        val playlistName = requireArguments().getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME)
+        val shareId = requireArguments().getString(Constants.INTENT_EXTRA_NAME_SHARE_ID)
+        val shareName = requireArguments().getString(Constants.INTENT_EXTRA_NAME_SHARE_NAME)
+        val albumListType = requireArguments().getString(
+            Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE
+        )
+        val genreName = requireArguments().getString(Constants.INTENT_EXTRA_NAME_GENRE_NAME)
+        val albumListTitle = requireArguments().getInt(
+            Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, 0
+        )
+        val getStarredTracks = requireArguments().getInt(Constants.INTENT_EXTRA_NAME_STARRED, 0)
+        val getVideos = requireArguments().getInt(Constants.INTENT_EXTRA_NAME_VIDEOS, 0)
+        val getRandomTracks = requireArguments().getInt(Constants.INTENT_EXTRA_NAME_RANDOM, 0)
+        val albumListSize = requireArguments().getInt(
+            Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0
+        )
+        val albumListOffset = requireArguments().getInt(
+            Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0
+        )
+
+        if (playlistId != null) {
+            getPlaylist(playlistId, playlistName)
+        } else if (podcastChannelId != null) {
+            getPodcastEpisodes(podcastChannelId)
+        } else if (shareId != null) {
+            getShare(shareId, shareName)
+        } else if (albumListType != null) {
+            getAlbumList(albumListType, albumListTitle, albumListSize, albumListOffset)
+        } else if (genreName != null) {
+            getSongsForGenre(genreName, albumListSize, albumListOffset)
+        } else if (getStarredTracks != 0) {
+            starred
+        } else if (getVideos != 0) {
+            getVideos(refresh)
+        } else if (getRandomTracks != 0) {
+            getRandom(albumListSize)
+        } else {
+            if (!isOffline(activity) && Util.getShouldUseId3Tags(activity)) {
+                if (isAlbum) {
+                    getAlbum(refresh, id, name, parentId)
+                } else {
+                    getArtist(refresh, id, name)
+                }
+            } else {
+                getMusicDirectory(refresh, id, name, parentId)
+            }
+        }
+    }
+
+    override fun onCreateContextMenu(menu: ContextMenu, view: View, menuInfo: ContextMenuInfo?) {
+        super.onCreateContextMenu(menu, view, menuInfo)
+        val info = menuInfo as AdapterContextMenuInfo?
+
+        val entry = albumListView!!.getItemAtPosition(info!!.position) as MusicDirectory.Entry?
+
+        if (entry != null && entry.isDirectory) {
+            val inflater = requireActivity().menuInflater
+            inflater.inflate(R.menu.select_album_context, menu)
+        }
+
+        shareButton = menu.findItem(R.id.menu_item_share)
+
+        if (shareButton != null) {
+            shareButton!!.isVisible = !isOffline(context)
+        }
+
+        val downloadMenuItem = menu.findItem(R.id.album_menu_download)
+        if (downloadMenuItem != null) {
+            downloadMenuItem.isVisible = !isOffline(context)
+        }
+    }
+
+    override fun onContextItemSelected(menuItem: MenuItem): Boolean {
+        Timber.d("onContextItemSelected")
+        val info = menuItem.menuInfo as AdapterContextMenuInfo? ?: return true
+
+        val entry = albumListView!!.getItemAtPosition(info.position) as MusicDirectory.Entry?
+            ?: return true
+
+        val entryId = entry.id
+
+        val itemId = menuItem.itemId
+        if (itemId == R.id.album_menu_play_now) {
+            downloadHandler.downloadRecursively(
+                this, entryId, false, false, true, false, false, false, false, false
+            )
+        } else if (itemId == R.id.album_menu_play_next) {
+            downloadHandler.downloadRecursively(
+                this, entryId, false, false, false, false, false, true, false, false
+            )
+        } else if (itemId == R.id.album_menu_play_last) {
+            downloadHandler.downloadRecursively(
+                this, entryId, false, true, false, false, false, false, false, false
+            )
+        } else if (itemId == R.id.album_menu_pin) {
+            downloadHandler.downloadRecursively(
+                this, entryId, true, true, false, false, false, false, false, false
+            )
+        } else if (itemId == R.id.album_menu_unpin) {
+            downloadHandler.downloadRecursively(
+                this, entryId, false, false, false, false, false, false, true, false
+            )
+        } else if (itemId == R.id.album_menu_download) {
+            downloadHandler.downloadRecursively(
+                this, entryId, false, false, false, false, true, false, false, false
+            )
+        } else if (itemId == R.id.select_album_play_all) {
+            playAll()
+        } else if (itemId == R.id.menu_item_share) {
+            val entries: MutableList<MusicDirectory.Entry?> = ArrayList(1)
+            entries.add(entry)
+            shareHandler.createShare(
+                this, entries, refreshAlbumListView,
+                cancellationToken!!
+            )
+            return true
+        } else {
+            return super.onContextItemSelected(menuItem)
+        }
+        return true
+    }
+
+    override fun onPrepareOptionsMenu(menu: Menu) {
+        super.onPrepareOptionsMenu(menu)
+        playAllButton = menu.findItem(R.id.select_album_play_all)
+
+        if (playAllButton != null) {
+            playAllButton!!.isVisible = playAllButtonVisible
+        }
+
+        shareButton = menu.findItem(R.id.menu_item_share)
+
+        if (shareButton != null) {
+            shareButton!!.isVisible = shareButtonVisible
+        }
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+        inflater.inflate(R.menu.select_album, menu)
+        super.onCreateOptionsMenu(menu, inflater)
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem): Boolean {
+        val itemId = item.itemId
+        if (itemId == R.id.select_album_play_all) {
+            playAll()
+            return true
+        } else if (itemId == R.id.menu_item_share) {
+            shareHandler.createShare(
+                this, getSelectedSongs(albumListView),
+                refreshAlbumListView, cancellationToken!!
+            )
+            return true
+        }
+
+        return false
+    }
+
+    override fun onDestroyView() {
+        cancellationToken!!.cancel()
+        super.onDestroyView()
+    }
+
+    private fun playNow(append: Boolean) {
+        val selectedSongs = getSelectedSongs(albumListView)
+
+        if (!selectedSongs.isEmpty()) {
+            downloadHandler.download(
+                this, append, false, !append, false,
+                false, selectedSongs
+            )
+            selectAll(false, false)
+        } else {
+            playAll(false, append)
+        }
+    }
+
+    private fun playAll(shuffle: Boolean = false, append: Boolean = false) {
+        var hasSubFolders = false
+
+        for (i in 0 until albumListView!!.count) {
+            val entry = albumListView!!.getItemAtPosition(i) as MusicDirectory.Entry?
+            if (entry != null && entry.isDirectory) {
+                hasSubFolders = true
+                break
+            }
+        }
+
+        val isArtist = requireArguments().getBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, false)
+        val id = requireArguments().getString(Constants.INTENT_EXTRA_NAME_ID)
+
+        if (hasSubFolders && id != null) {
+            downloadHandler.downloadRecursively(
+                this, id, false, append, !append,
+                shuffle, false, false, false, isArtist
+            )
+        } else {
+            selectAll(true, false)
+            downloadHandler.download(
+                this, append, false, !append, false,
+                shuffle, getSelectedSongs(albumListView)
+            )
+            selectAll(false, false)
+        }
+    }
+
+    private fun getMusicDirectory(refresh: Boolean, id: String?, name: String?, parentId: String?) {
+        setTitle(this, name)
+
+        object : LoadTask() {
+            override fun load(service: MusicService): MusicDirectory {
+                var root = MusicDirectory()
+
+                if (allSongsId == id) {
+                    val musicDirectory = service.getMusicDirectory(parentId, name, refresh, context)
+
+                    val songs: MutableList<MusicDirectory.Entry> = LinkedList()
+                    getSongsRecursively(musicDirectory, songs)
+
+                    for (song in songs) {
+                        if (!song.isDirectory) {
+                            root.addChild(song)
+                        }
+                    }
+                } else {
+                    val musicDirectory = service.getMusicDirectory(id, name, refresh, context)
+
+                    if (Util.getShouldShowAllSongsByArtist(context) &&
+                        musicDirectory.findChild(allSongsId) == null &&
+                        musicDirectory.getChildren(true, false).size ==
+                        musicDirectory.getChildren(true, true).size
+                    ) {
+                        val allSongs = MusicDirectory.Entry()
+
+                        allSongs.isDirectory = true
+                        allSongs.artist = name
+                        allSongs.parent = id
+                        allSongs.id = allSongsId
+                        allSongs.title = String.format(
+                            resources.getString(R.string.select_album_all_songs), name
+                        )
+
+                        root.addChild(allSongs)
+                        root.addAll(musicDirectory.getChildren())
+                    } else {
+                        root = musicDirectory
+                    }
+                }
+                return root
+            }
+
+            private fun getSongsRecursively(
+                parent: MusicDirectory,
+                songs: MutableList<MusicDirectory.Entry>
+            ) {
+                for (song in parent.getChildren(false, true)) {
+                    if (!song.isVideo && !song.isDirectory) {
+                        songs.add(song)
+                    }
+                }
+
+                val musicService = getMusicService(context!!)
+
+                for ((id1, _, _, title) in parent.getChildren(true, false)) {
+                    var root: MusicDirectory
+
+                    if (allSongsId != id1) {
+                        root = musicService.getMusicDirectory(id1, title, false, context)
+
+                        getSongsRecursively(root, songs)
+                    }
+                }
+            }
+        }.execute()
+    }
+
+    private fun getArtist(refresh: Boolean, id: String?, name: String?) {
+        setTitle(this, name)
+
+        object : LoadTask() {
+            override fun load(service: MusicService): MusicDirectory {
+
+                var root = MusicDirectory()
+
+                val musicDirectory = service.getArtist(id, name, refresh, context)
+
+                if (Util.getShouldShowAllSongsByArtist(context) &&
+                    musicDirectory.findChild(allSongsId) == null &&
+                    musicDirectory.getChildren(true, false).size ==
+                    musicDirectory.getChildren(true, true).size
+                ) {
+                    val allSongs = MusicDirectory.Entry()
+
+                    allSongs.isDirectory = true
+                    allSongs.artist = name
+                    allSongs.parent = id
+                    allSongs.id = allSongsId
+                    allSongs.title = String.format(
+                        resources.getString(R.string.select_album_all_songs), name
+                    )
+
+                    root.addFirst(allSongs)
+                    root.addAll(musicDirectory.getChildren())
+                } else {
+                    root = musicDirectory
+                }
+                return root
+            }
+        }.execute()
+    }
+
+    private fun getAlbum(refresh: Boolean, id: String?, name: String?, parentId: String?) {
+        setTitle(this, name)
+
+        object : LoadTask() {
+            override fun load(service: MusicService): MusicDirectory {
+
+                val musicDirectory: MusicDirectory
+
+                musicDirectory = if (allSongsId == id) {
+                    val root = MusicDirectory()
+
+                    val songs: MutableCollection<MusicDirectory.Entry> = LinkedList()
+                    getSongsForArtist(parentId, songs)
+
+                    for (song in songs) {
+                        if (!song.isDirectory) {
+                            root.addChild(song)
+                        }
+                    }
+                    root
+                } else {
+                    service.getAlbum(id, name, refresh, context)
+                }
+                return musicDirectory
+            }
+
+            private fun getSongsForArtist(
+                id: String?,
+                songs: MutableCollection<MusicDirectory.Entry>
+            ) {
+
+                val musicService = getMusicService(context!!)
+                val artist = musicService.getArtist(id, "", false, context)
+
+                for ((id1) in artist.getChildren()) {
+                    if (allSongsId != id1) {
+                        val albumDirectory = musicService.getAlbum(id1, "", false, context)
+
+                        for (song in albumDirectory.getChildren()) {
+                            if (!song.isVideo) {
+                                songs.add(song)
+                            }
+                        }
+                    }
+                }
+            }
+        }.execute()
+    }
+
+    private fun getSongsForGenre(genre: String, count: Int, offset: Int) {
+        setTitle(this, genre)
+
+        object : LoadTask() {
+            override fun load(service: MusicService): MusicDirectory {
+                return service.getSongsByGenre(genre, count, offset, context)
+            }
+
+            override fun done(result: Pair<MusicDirectory, Boolean>) {
+                // Hide more button when results are less than album list size
+                if (result.first.getChildren().size < arguments!!.getInt(
+                    Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0
+                )
+                ) {
+                    moreButton!!.visibility = View.GONE
+                } else {
+                    moreButton!!.visibility = View.VISIBLE
+                }
+
+                moreButton!!.setOnClickListener {
+                    val theGenre = arguments!!.getString(Constants.INTENT_EXTRA_NAME_GENRE_NAME)
+                    val size = arguments!!.getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0)
+                    val theOffset = arguments!!.getInt(
+                        Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0
+                    ) + size
+                    val bundle = Bundle()
+                    bundle.putString(Constants.INTENT_EXTRA_NAME_GENRE_NAME, theGenre)
+                    bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, size)
+                    bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, theOffset)
+                    Navigation.findNavController(view!!).navigate(R.id.selectAlbumFragment, bundle)
+                }
+
+                super.done(result)
+            }
+        }.execute()
+    }
+
+    private val starred: Unit
+        get() {
+            setTitle(this, R.string.main_songs_starred)
+
+            object : LoadTask() {
+                override fun load(service: MusicService): MusicDirectory {
+                    return if (Util.getShouldUseId3Tags(context))
+                        Util.getSongsFromSearchResult(service.getStarred2(context))
+                    else
+                        Util.getSongsFromSearchResult(service.getStarred(context))
+                }
+            }.execute()
+        }
+
+    private fun getVideos(refresh: Boolean) {
+        showHeader = false
+
+        setTitle(this, R.string.main_videos)
+
+        object : LoadTask() {
+            override fun load(service: MusicService): MusicDirectory {
+                return service.getVideos(refresh, context)
+            }
+        }.execute()
+    }
+
+    private fun getRandom(size: Int) {
+        setTitle(this, R.string.main_songs_random)
+
+        object : LoadTask() {
+            override fun sortableCollection(): Boolean {
+                return false
+            }
+
+            override fun load(service: MusicService): MusicDirectory {
+                return service.getRandomSongs(size, context)
+            }
+        }.execute()
+    }
+
+    private fun getPlaylist(playlistId: String, playlistName: String?) {
+
+        setTitle(this, playlistName)
+
+        object : LoadTask() {
+            override fun load(service: MusicService): MusicDirectory {
+                return service.getPlaylist(playlistId, playlistName, context)
+            }
+        }.execute()
+    }
+
+    private fun getPodcastEpisodes(podcastChannelId: String) {
+
+        setTitle(this, R.string.podcasts_label)
+
+        object : LoadTask() {
+            override fun load(service: MusicService): MusicDirectory {
+                return service.getPodcastEpisodes(podcastChannelId, context)
+            }
+        }.execute()
+    }
+
+    private fun getShare(shareId: String, shareName: CharSequence?) {
+
+        setTitle(this, shareName)
+        // setActionBarSubtitle(shareName);
+
+        object : LoadTask() {
+            override fun load(service: MusicService): MusicDirectory {
+                val shares = service.getShares(true, context)
+
+                val md = MusicDirectory()
+
+                for (share in shares) {
+                    if (share.id == shareId) {
+                        for (entry in share.getEntries()) {
+                            md.addChild(entry)
+                        }
+                        break
+                    }
+                }
+                return md
+            }
+        }.execute()
+    }
+
+    private fun getAlbumList(albumListType: String, albumListTitle: Int, size: Int, offset: Int) {
+
+        showHeader = false
+
+        setTitle(this, albumListTitle)
+        // setActionBarSubtitle(albumListTitle);
+
+        object : LoadTask() {
+            override fun sortableCollection(): Boolean {
+                return albumListType != "newest" && albumListType != "random" &&
+                    albumListType != "highest" && albumListType != "recent" &&
+                    albumListType != "frequent"
+            }
+
+            override fun load(service: MusicService): MusicDirectory {
+                return if (Util.getShouldUseId3Tags(context))
+                    service.getAlbumList2(albumListType, size, offset, context)
+                else
+                    service.getAlbumList(albumListType, size, offset, context)
+            }
+
+            override fun done(result: Pair<MusicDirectory, Boolean>) {
+                if (!result.first.getChildren().isEmpty()) {
+                    pinButton!!.visibility = View.GONE
+                    unpinButton!!.visibility = View.GONE
+                    downloadButton!!.visibility = View.GONE
+                    deleteButton!!.visibility = View.GONE
+
+                    // Hide more button when results are less than album list size
+                    if (result.first.getChildren().size < arguments!!.getInt(
+                        Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0
+                    )
+                    ) {
+                        moreButton!!.visibility = View.GONE
+                    } else {
+                        moreButton!!.visibility = View.VISIBLE
+                        moreButton!!.setOnClickListener {
+                            val theAlbumListTitle = arguments!!.getInt(
+                                Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, 0
+                            )
+                            val type = arguments!!.getString(
+                                Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE
+                            )
+                            val theSize = arguments!!.getInt(
+                                Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0
+                            )
+                            val theOffset = arguments!!.getInt(
+                                Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0
+                            ) + theSize
+
+                            val bundle = Bundle()
+                            bundle.putInt(
+                                Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, theAlbumListTitle
+                            )
+                            bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type)
+                            bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, theSize)
+                            bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, theOffset)
+                            Navigation.findNavController(view!!).navigate(
+                                R.id.selectAlbumFragment, bundle
+                            )
+                        }
+                    }
+                } else {
+                    moreButton!!.visibility = View.GONE
+                }
+                super.done(result)
+            }
+        }.execute()
+    }
+
+    private fun selectAllOrNone() {
+        var someUnselected = false
+        val count = albumListView!!.count
+
+        for (i in 0 until count) {
+            if (!albumListView!!.isItemChecked(i) &&
+                albumListView!!.getItemAtPosition(i) is MusicDirectory.Entry
+            ) {
+                someUnselected = true
+                break
+            }
+        }
+        selectAll(someUnselected, true)
+    }
+
+    private fun selectAll(selected: Boolean, toast: Boolean) {
+        val count = albumListView!!.count
+        var selectedCount = 0
+
+        for (i in 0 until count) {
+            val entry = albumListView!!.getItemAtPosition(i) as MusicDirectory.Entry?
+            if (entry != null && !entry.isDirectory && !entry.isVideo) {
+                albumListView!!.setItemChecked(i, selected)
+                selectedCount++
+            }
+        }
+
+        // Display toast: N tracks selected / N tracks unselected
+        if (toast) {
+            val toastResId = if (selected)
+                R.string.select_album_n_selected
+            else
+                R.string.select_album_n_unselected
+            Util.toast(activity, getString(toastResId, selectedCount))
+        }
+        enableButtons()
+    }
+
+    private fun enableButtons() {
+        val selection = getSelectedSongs(albumListView)
+        val enabled = !selection.isEmpty()
+        var unpinEnabled = false
+        var deleteEnabled = false
+
+        var pinnedCount = 0
+
+        for (song in selection) {
+            val downloadFile = mediaPlayerController.getDownloadFileForSong(song)
+            if (downloadFile.isWorkDone) {
+                deleteEnabled = true
+            }
+            if (downloadFile.isSaved) {
+                pinnedCount++
+                unpinEnabled = true
+            }
+        }
+
+        playNowButton!!.visibility = if (enabled) View.VISIBLE else View.GONE
+        playNextButton!!.visibility = if (enabled) View.VISIBLE else View.GONE
+        playLastButton!!.visibility = if (enabled) View.VISIBLE else View.GONE
+        pinButton!!.visibility = if (enabled && !isOffline(context) && selection.size > pinnedCount)
+            View.VISIBLE
+        else
+            View.GONE
+        unpinButton!!.visibility = if (enabled && unpinEnabled) View.VISIBLE else View.GONE
+        downloadButton!!.visibility = if (enabled && !deleteEnabled && !isOffline(context))
+            View.VISIBLE
+        else
+            View.GONE
+        deleteButton!!.visibility = if (enabled && deleteEnabled) View.VISIBLE else View.GONE
+    }
+
+    private fun downloadBackground(save: Boolean) {
+        var songs = getSelectedSongs(albumListView)
+
+        if (songs.isEmpty()) {
+            selectAll(true, false)
+            songs = getSelectedSongs(albumListView)
+        }
+
+        downloadBackground(save, songs)
+    }
+
+    private fun downloadBackground(save: Boolean, songs: List<MusicDirectory.Entry?>) {
+        val onValid = Runnable {
+            networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
+            mediaPlayerController.downloadBackground(songs, save)
+
+            if (save) {
+                Util.toast(
+                    context,
+                    resources.getQuantityString(
+                        R.plurals.select_album_n_songs_pinned, songs.size, songs.size
+                    )
+                )
+            } else {
+                Util.toast(
+                    context,
+                    resources.getQuantityString(
+                        R.plurals.select_album_n_songs_downloaded, songs.size, songs.size
+                    )
+                )
+            }
+        }
+        onValid.run()
+    }
+
+    private fun delete() {
+        var songs = getSelectedSongs(albumListView)
+
+        if (songs.isEmpty()) {
+            selectAll(true, false)
+            songs = getSelectedSongs(albumListView)
+        }
+
+        mediaPlayerController.delete(songs)
+    }
+
+    private fun unpin() {
+        val songs = getSelectedSongs(albumListView)
+        Util.toast(
+            context,
+            resources.getQuantityString(
+                R.plurals.select_album_n_songs_unpinned, songs.size, songs.size
+            )
+        )
+        mediaPlayerController.unpin(songs)
+    }
+
+    private abstract inner class LoadTask : FragmentBackgroundTask<Pair<MusicDirectory, Boolean>>(
+        this@SelectAlbumFragment.activity, true, refreshAlbumListView,
+        cancellationToken
+    ) {
+
+        protected abstract fun load(service: MusicService): MusicDirectory
+        protected open fun sortableCollection(): Boolean {
+            return true
+        }
+
+        override fun doInBackground(): Pair<MusicDirectory, Boolean> {
+            val musicService = getMusicService(context!!)
+            val dir = load(musicService)
+            val valid = musicService.isLicenseValid(context)
+            return Pair<MusicDirectory, Boolean>(dir, valid)
+        }
+
+        protected override fun done(result: Pair<MusicDirectory, Boolean>) {
+            val musicDirectory = result.first
+            val entries = musicDirectory.getChildren()
+
+            if (sortableCollection() && Util.getShouldSortByDisc(context)) {
+                Collections.sort(entries, EntryByDiscAndTrackComparator())
+            }
+
+            var allVideos = true
+            var songCount = 0
+
+            for (entry in entries) {
+                if (!entry.isVideo) {
+                    allVideos = false
+                }
+                if (!entry.isDirectory) {
+                    songCount++
+                }
+            }
+
+            val listSize = arguments!!.getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0)
+
+            if (songCount > 0) {
+                if (showHeader) {
+                    val intentAlbumName = arguments!!.getString(Constants.INTENT_EXTRA_NAME_NAME)
+                    val directoryName = musicDirectory.name
+                    val header = createHeader(
+                        entries, intentAlbumName ?: directoryName,
+                        songCount
+                    )
+                    if (header != null && albumListView!!.headerViewsCount == 0) {
+                        albumListView!!.addHeaderView(header, null, false)
+                    }
+                }
+
+                pinButton!!.visibility = View.VISIBLE
+                unpinButton!!.visibility = View.VISIBLE
+                downloadButton!!.visibility = View.VISIBLE
+                deleteButton!!.visibility = View.VISIBLE
+                selectButton!!.visibility = if (allVideos) View.GONE else View.VISIBLE
+                playNowButton!!.visibility = View.VISIBLE
+                playNextButton!!.visibility = View.VISIBLE
+                playLastButton!!.visibility = View.VISIBLE
+
+                if (listSize == 0 || songCount < listSize) {
+                    moreButton!!.visibility = View.GONE
+                } else {
+                    moreButton!!.visibility = View.VISIBLE
+                    if (arguments!!.getInt(Constants.INTENT_EXTRA_NAME_RANDOM, 0) > 0) {
+                        moreButton!!.setOnClickListener {
+                            val offset = arguments!!.getInt(
+                                Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0
+                            ) + listSize
+                            val bundle = Bundle()
+                            bundle.putInt(Constants.INTENT_EXTRA_NAME_RANDOM, 1)
+                            bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, listSize)
+                            bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, offset)
+                            Navigation.findNavController(view!!).navigate(
+                                R.id.selectAlbumFragment, bundle
+                            )
+                        }
+                    }
+                }
+            } else {
+                pinButton!!.visibility = View.GONE
+                unpinButton!!.visibility = View.GONE
+                downloadButton!!.visibility = View.GONE
+                deleteButton!!.visibility = View.GONE
+                selectButton!!.visibility = View.GONE
+                playNowButton!!.visibility = View.GONE
+                playNextButton!!.visibility = View.GONE
+                playLastButton!!.visibility = View.GONE
+
+                if (listSize == 0 || result.first.getChildren().size < listSize) {
+                    albumButtons!!.visibility = View.GONE
+                } else {
+                    moreButton!!.visibility = View.VISIBLE
+                }
+            }
+
+            enableButtons()
+
+            val isAlbumList = arguments!!.containsKey(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE)
+            playAllButtonVisible = !(isAlbumList || entries.isEmpty()) && !allVideos
+            shareButtonVisible = !isOffline(context) && songCount > 0
+
+            emptyView!!.visibility = if (entries.isEmpty()) View.VISIBLE else View.GONE
+
+            if (playAllButton != null) {
+                playAllButton!!.isVisible = playAllButtonVisible
+            }
+
+            if (shareButton != null) {
+                shareButton!!.isVisible = shareButtonVisible
+            }
+
+            albumListView!!.adapter = EntryAdapter(
+                context,
+                imageLoaderProvider.getImageLoader(), entries, true
+            )
+
+            val playAll = arguments!!.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false)
+            if (playAll && songCount > 0) {
+                playAll(
+                    arguments!!.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, false),
+                    false
+                )
+            }
+        }
+
+        protected fun createHeader(
+            entries: List<MusicDirectory.Entry>,
+            name: CharSequence?,
+            songCount: Int
+        ): View? {
+            val coverArtView = header!!.findViewById<View>(R.id.select_album_art) as ImageView
+            val artworkSelection = random.nextInt(entries.size)
+            imageLoaderProvider.getImageLoader().loadImage(
+                coverArtView, entries[artworkSelection], false,
+                Util.getAlbumImageSize(context), false, true
+            )
+
+            val albumHeader = AlbumHeader.processEntries(context, entries)
+
+            val titleView = header!!.findViewById<View>(R.id.select_album_title) as TextView
+            titleView.text = name ?: getTitle(this@SelectAlbumFragment) // getActionBarSubtitle());
+
+            // Don't show a header if all entries are videos
+            if (albumHeader.isAllVideo) {
+                return null
+            }
+
+            val artistView = header!!.findViewById<TextView>(R.id.select_album_artist)
+            val artist: String
+
+            artist = if (albumHeader.artists.size == 1)
+                albumHeader.artists.iterator().next()
+            else if (albumHeader.grandParents.size == 1)
+                albumHeader.grandParents.iterator().next()
+            else
+                resources.getString(R.string.common_various_artists)
+
+            artistView.text = artist
+
+            val genreView = header!!.findViewById<TextView>(R.id.select_album_genre)
+            val genre: String
+
+            genre = if (albumHeader.genres.size == 1)
+                albumHeader.genres.iterator().next()
+            else
+                resources.getString(R.string.common_multiple_genres)
+
+            genreView.text = genre
+
+            val yearView = header!!.findViewById<TextView>(R.id.select_album_year)
+            val year: String
+
+            year = if (albumHeader.years.size == 1)
+                albumHeader.years.iterator().next().toString()
+            else
+                resources.getString(R.string.common_multiple_years)
+
+            yearView.text = year
+
+            val songCountView = header!!.findViewById<TextView>(R.id.select_album_song_count)
+            val songs = resources.getQuantityString(
+                R.plurals.select_album_n_songs, songCount,
+                songCount
+            )
+            songCountView.text = songs
+
+            val duration = Util.formatTotalDuration(albumHeader.totalDuration)
+
+            val durationView = header!!.findViewById<TextView>(R.id.select_album_duration)
+            durationView.text = duration
+
+            return header
+        }
+    }
+
+    private fun getSelectedSongs(albumListView: ListView?): List<MusicDirectory.Entry?> {
+        val songs: MutableList<MusicDirectory.Entry?> = ArrayList(10)
+
+        if (albumListView != null) {
+            val count = albumListView.count
+            for (i in 0 until count) {
+                if (albumListView.isItemChecked(i)) {
+                    songs.add(albumListView.getItemAtPosition(i) as MusicDirectory.Entry?)
+                }
+            }
+        }
+
+        return songs
+    }
+}