1
0
mirror of https://github.com/ultrasonic/ultrasonic synced 2025-02-27 08:57:44 +01:00

Add years to album header, determine album name based on entries in album header, make thread collection in ImageLoader concurrent

This commit is contained in:
Joshua Bahnsen 2014-01-26 16:47:13 -07:00
parent 4389698d7f
commit 36167e5ab9
9 changed files with 83 additions and 31 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="wrap_content" > a:layout_height="wrap_content">
<ImageView <ImageView
a:id="@+id/select_album_art" a:id="@+id/select_album_art"
@ -12,7 +12,7 @@
a:layout_marginRight="10dip" a:layout_marginRight="10dip"
a:contentDescription="@null" a:contentDescription="@null"
a:scaleType="fitCenter" a:scaleType="fitCenter"
a:src="@drawable/unknown_album_large" /> a:src="@drawable/unknown_album_large"/>
<TextView <TextView
a:id="@+id/select_album_title" a:id="@+id/select_album_title"
@ -23,7 +23,7 @@
a:paddingRight="4dip" a:paddingRight="4dip"
a:paddingTop="10dip" a:paddingTop="10dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceMedium" /> a:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView <TextView
a:id="@+id/select_album_artist" a:id="@+id/select_album_artist"
@ -34,7 +34,7 @@
a:ellipsize="end" a:ellipsize="end"
a:paddingRight="4dip" a:paddingRight="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall" /> a:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView <TextView
a:id="@+id/select_album_genre" a:id="@+id/select_album_genre"
@ -45,18 +45,29 @@
a:ellipsize="end" a:ellipsize="end"
a:paddingRight="4dip" a:paddingRight="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall" /> a:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView
a:id="@+id/select_album_year"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_below="@+id/select_album_genre"
a:layout_toRightOf="@+id/select_album_art"
a:ellipsize="end"
a:paddingRight="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView <TextView
a:id="@+id/select_album_song_count" a:id="@+id/select_album_song_count"
a:layout_width="wrap_content" a:layout_width="wrap_content"
a:layout_height="wrap_content" a:layout_height="wrap_content"
a:layout_below="@+id/select_album_genre" a:layout_below="@+id/select_album_year"
a:layout_toRightOf="@+id/select_album_art" a:layout_toRightOf="@+id/select_album_art"
a:ellipsize="none" a:ellipsize="none"
a:paddingRight="4dip" a:paddingRight="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall" /> a:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView <TextView
a:id="@+id/select_album_duration" a:id="@+id/select_album_duration"
@ -67,6 +78,6 @@
a:ellipsize="none" a:ellipsize="none"
a:paddingRight="4dip" a:paddingRight="4dip"
a:singleLine="true" a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall" /> a:textAppearance="?android:attr/textAppearanceSmall"/>
</RelativeLayout> </RelativeLayout>

View File

@ -388,6 +388,7 @@
<string name="settings.image_loader_concurrency_11">11</string> <string name="settings.image_loader_concurrency_11">11</string>
<string name="settings.image_loader_concurrency_12">12</string> <string name="settings.image_loader_concurrency_12">12</string>
<string name="albumArt">albumArt</string> <string name="albumArt">albumArt</string>
<string name="common_multiple_years">Multiple Years</string>
<plurals name="select_album_n_songs"> <plurals name="select_album_n_songs">
<item quantity="zero">Aucun titre</item> <item quantity="zero">Aucun titre</item>

View File

@ -388,6 +388,7 @@
<string name="settings.image_loader_concurrency_11">11</string> <string name="settings.image_loader_concurrency_11">11</string>
<string name="settings.image_loader_concurrency_12">12</string> <string name="settings.image_loader_concurrency_12">12</string>
<string name="albumArt">albumArt</string> <string name="albumArt">albumArt</string>
<string name="common_multiple_years">Multiple Years</string>
<plurals name="select_album_n_songs"> <plurals name="select_album_n_songs">
<item quantity="zero">Nincsenek dalok</item> <item quantity="zero">Nincsenek dalok</item>

View File

@ -388,6 +388,7 @@
<string name="settings.image_loader_concurrency_11">11</string> <string name="settings.image_loader_concurrency_11">11</string>
<string name="settings.image_loader_concurrency_12">12</string> <string name="settings.image_loader_concurrency_12">12</string>
<string name="albumArt">albumArt</string> <string name="albumArt">albumArt</string>
<string name="common_multiple_years">Multiple Years</string>
<plurals name="select_album_n_songs"> <plurals name="select_album_n_songs">
<item quantity="zero">No songs</item> <item quantity="zero">No songs</item>

View File

@ -1325,7 +1325,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
artistTextView.setText(null); artistTextView.setText(null);
downloadTrackTextView.setText(null); downloadTrackTextView.setText(null);
downloadTotalDurationTextView.setText(null); downloadTotalDurationTextView.setText(null);
getImageLoader().loadImage(albumArtImageView, null, true, 0, false, false); getImageLoader().loadImage(albumArtImageView, null, true, 0, false, true);
} }
} }

View File

@ -1196,11 +1196,12 @@ public class SelectAlbumActivity extends SubsonicTabActivity
int artworkSelection = random.nextInt(entries.size()); int artworkSelection = random.nextInt(entries.size());
getImageLoader().loadImage(coverArtView, entries.get(artworkSelection), false, Util.getAlbumImageSize(SelectAlbumActivity.this), false, true); getImageLoader().loadImage(coverArtView, entries.get(artworkSelection), false, Util.getAlbumImageSize(SelectAlbumActivity.this), false, true);
TextView titleView = (TextView) header.findViewById(R.id.select_album_title);
titleView.setText(name != null ? name : getActionBarSubtitle());
AlbumHeader albumHeader = AlbumHeader.processEntries(SelectAlbumActivity.this, entries); AlbumHeader albumHeader = AlbumHeader.processEntries(SelectAlbumActivity.this, entries);
TextView titleView = (TextView) header.findViewById(R.id.select_album_title);
name = albumHeader.getAlbumNames().size() == 1 ? albumHeader.getAlbumNames().iterator().next() : name;
titleView.setText(name != null ? name : getActionBarSubtitle());
// Don't show a header if all entries are videos // Don't show a header if all entries are videos
if (albumHeader.getIsAllVideo()) if (albumHeader.getIsAllVideo())
{ {
@ -1221,6 +1222,13 @@ public class SelectAlbumActivity extends SubsonicTabActivity
genreView.setText(genre); genreView.setText(genre);
TextView yearView = (TextView) 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 = (TextView) header.findViewById(R.id.select_album_song_count); TextView songCountView = (TextView) header.findViewById(R.id.select_album_song_count);
String songs = getResources().getQuantityString(R.plurals.select_album_n_songs, songCount, songCount); String songs = getResources().getQuantityString(R.plurals.select_album_n_songs, songCount, songCount);
songCountView.setText(songs); songCountView.setText(songs);

View File

@ -14,6 +14,8 @@ public class AlbumHeader
private Set<String> artists; private Set<String> artists;
private Set<String> grandParents; private Set<String> grandParents;
private Set<String> genres; private Set<String> genres;
private Set<String> albumNames;
private Set<Integer> years;
public boolean getIsAllVideo() public boolean getIsAllVideo()
{ {
@ -40,11 +42,24 @@ public class AlbumHeader
return this.genres; return this.genres;
} }
public Set<String> getAlbumNames()
{
return this.albumNames;
}
public Set<Integer> getYears()
{
return this.years;
}
public AlbumHeader() public AlbumHeader()
{ {
this.artists = new HashSet<String>(); this.artists = new HashSet<String>();
this.grandParents = new HashSet<String>(); this.grandParents = new HashSet<String>();
this.genres = new HashSet<String>(); this.genres = new HashSet<String>();
this.albumNames = new HashSet<String>();
this.years = new HashSet<Integer>();
this.isAllVideo = true; this.isAllVideo = true;
this.totalDuration = 0; this.totalDuration = 0;
} }
@ -83,6 +98,16 @@ public class AlbumHeader
{ {
albumHeader.genres.add(entry.getGenre()); albumHeader.genres.add(entry.getGenre());
} }
if (entry.getAlbum() != null)
{
albumHeader.albumNames.add(entry.getAlbum());
}
if (entry.getYear() != null)
{
albumHeader.years.add(entry.getYear());
}
} }
} }

View File

@ -77,11 +77,11 @@ public class CacheCleaner
} }
} }
private static void deleteEmptyDirs(Iterable<File> dirs, Collection<File> undeletable) private static void deleteEmptyDirs(Iterable<File> dirs, Collection<File> doNotDelete)
{ {
for (File dir : dirs) for (File dir : dirs)
{ {
if (undeletable.contains(dir)) if (doNotDelete.contains(dir))
{ {
continue; continue;
} }
@ -114,8 +114,8 @@ public class CacheCleaner
} }
long cacheSizeBytes = Util.getCacheSizeMB(context) * 1024L * 1024L; long cacheSizeBytes = Util.getCacheSizeMB(context) * 1024L * 1024L;
long bytesUsedBySubsonic = 0L; long bytesUsedBySubsonic = 0L;
for (File file : files) for (File file : files)
{ {
bytesUsedBySubsonic += file.length(); bytesUsedBySubsonic += file.length();
@ -140,7 +140,7 @@ public class CacheCleaner
return bytesToDelete; return bytesToDelete;
} }
private static void deleteFiles(Collection<File> files, Collection<File> undeletable, long bytesToDelete, boolean deletePartials) private static void deleteFiles(Collection<File> files, Collection<File> doNotDelete, long bytesToDelete, boolean deletePartials)
{ {
if (files.isEmpty()) if (files.isEmpty())
{ {
@ -154,9 +154,10 @@ public class CacheCleaner
if (bytesToDelete > bytesDeleted || (deletePartials && (file.getName().endsWith(".partial") || file.getName().contains(".partial.")))) if (bytesToDelete > bytesDeleted || (deletePartials && (file.getName().endsWith(".partial") || file.getName().contains(".partial."))))
{ {
if (!undeletable.contains(file) && !file.getName().equals(Constants.ALBUM_ART_FILE)) if (!doNotDelete.contains(file) && !file.getName().equals(Constants.ALBUM_ART_FILE))
{ {
long size = file.length(); long size = file.length();
if (Util.delete(file)) if (Util.delete(file))
{ {
bytesDeleted += size; bytesDeleted += size;
@ -174,6 +175,7 @@ public class CacheCleaner
{ {
String name = file.getName(); String name = file.getName();
boolean isCacheFile = name.endsWith(".partial") || name.contains(".partial.") || name.endsWith(".complete") || name.contains(".complete."); boolean isCacheFile = name.endsWith(".partial") || name.contains(".partial.") || name.endsWith(".complete") || name.contains(".complete.");
if (isCacheFile) if (isCacheFile)
{ {
files.add(file); files.add(file);
@ -186,6 +188,7 @@ public class CacheCleaner
{ {
findCandidatesForDeletion(child, files, dirs); findCandidatesForDeletion(child, files, dirs);
} }
dirs.add(file); dirs.add(file);
} }
} }
@ -212,18 +215,18 @@ public class CacheCleaner
}); });
} }
private Set<File> findUndeletableFiles() private Set<File> findFilesToNotDelete()
{ {
Set<File> undeletable = new HashSet<File>(5); Set<File> filesToNotDelete = new HashSet<File>(5);
for (DownloadFile downloadFile : downloadService.getDownloads()) for (DownloadFile downloadFile : downloadService.getDownloads())
{ {
undeletable.add(downloadFile.getPartialFile()); filesToNotDelete.add(downloadFile.getPartialFile());
undeletable.add(downloadFile.getCompleteFile()); filesToNotDelete.add(downloadFile.getCompleteFile());
} }
undeletable.add(FileUtil.getMusicDirectory(context)); filesToNotDelete.add(FileUtil.getMusicDirectory(context));
return undeletable; return filesToNotDelete;
} }
private class BackgroundCleanup extends AsyncTask<Void, Void, Void> private class BackgroundCleanup extends AsyncTask<Void, Void, Void>
@ -246,10 +249,10 @@ public class CacheCleaner
findCandidatesForDeletion(FileUtil.getMusicDirectory(context), files, dirs); findCandidatesForDeletion(FileUtil.getMusicDirectory(context), files, dirs);
sortByAscendingModificationTime(files); sortByAscendingModificationTime(files);
Set<File> undeletable = findUndeletableFiles(); Set<File> filesToNotDelete = findFilesToNotDelete();
deleteFiles(files, undeletable, getMinimumDelete(files), true); deleteFiles(files, filesToNotDelete, getMinimumDelete(files), true);
deleteEmptyDirs(dirs, undeletable); deleteEmptyDirs(dirs, filesToNotDelete);
} }
catch (RuntimeException x) catch (RuntimeException x)
{ {
@ -282,8 +285,8 @@ public class CacheCleaner
if (bytesToDelete > 0L) if (bytesToDelete > 0L)
{ {
sortByAscendingModificationTime(files); sortByAscendingModificationTime(files);
Set<File> undeletable = findUndeletableFiles(); Set<File> filesToNotDelete = findFilesToNotDelete();
deleteFiles(files, undeletable, bytesToDelete, false); deleteFiles(files, filesToNotDelete, bytesToDelete, false);
} }
} }
catch (RuntimeException x) catch (RuntimeException x)

View File

@ -38,6 +38,7 @@ import com.thejoshwa.ultrasonic.androidapp.service.MusicServiceFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -98,7 +99,7 @@ public class ImageLoader implements Runnable
{ {
running.set(true); running.set(true);
threads = new ArrayList<Thread>(this.concurrency); threads = Collections.synchronizedCollection(new ArrayList<Thread>(this.concurrency));
for (int i = 0; i < this.concurrency; i++) for (int i = 0; i < this.concurrency; i++)
{ {
@ -209,9 +210,10 @@ public class ImageLoader implements Runnable
MusicDirectory.Entry tagEntry = (MusicDirectory.Entry) view.getTag(); MusicDirectory.Entry tagEntry = (MusicDirectory.Entry) view.getTag();
// Only apply image to the view if the view is intended for this entry
if (entry != null && tagEntry != null && !entry.equals(tagEntry)) if (entry != null && tagEntry != null && !entry.equals(tagEntry))
{ {
Log.i(TAG, "Skipping entry"); Log.i(TAG, "View is no longer valid, not setting ImageBitmap");
return; return;
} }