ultrasonic-app-subsonic-and.../src/com/thejoshwa/ultrasonic/androidapp/util/CacheCleaner.java

183 lines
6.4 KiB
Java
Raw Normal View History

2013-04-06 21:47:24 +02:00
package com.thejoshwa.ultrasonic.androidapp.util;
2012-02-26 21:25:13 +01:00
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
2012-02-26 21:25:13 +01:00
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import android.content.Context;
import android.util.Log;
import android.os.StatFs;
2013-04-06 21:47:24 +02:00
import com.thejoshwa.ultrasonic.androidapp.service.DownloadFile;
import com.thejoshwa.ultrasonic.androidapp.service.DownloadService;
2012-02-26 21:25:13 +01:00
/**
* @author Sindre Mehus
* @version $Id$
*/
public class CacheCleaner {
private static final String TAG = CacheCleaner.class.getSimpleName();
private static final double MAX_FILE_SYSTEM_USAGE = 0.95;
private final Context context;
private final DownloadService downloadService;
public CacheCleaner(Context context, DownloadService downloadService) {
this.context = context;
this.downloadService = downloadService;
}
public void clean() {
new Thread(new Runnable() {
public void run() {
Log.i(TAG, "Starting cache cleaning.");
2012-02-26 21:25:13 +01:00
if (downloadService == null) {
Log.e(TAG, "DownloadService not set. Aborting cache cleaning.");
return;
}
2012-02-26 21:25:13 +01:00
try {
List<File> files = new ArrayList<File>();
List<File> dirs = new ArrayList<File>();
2012-02-26 21:25:13 +01:00
findCandidatesForDeletion(FileUtil.getMusicDirectory(context), files, dirs);
sortByAscendingModificationTime(files);
2012-02-26 21:25:13 +01:00
Set<File> undeletable = findUndeletableFiles();
2012-02-26 21:25:13 +01:00
deleteFiles(files, undeletable);
deleteEmptyDirs(dirs, undeletable);
Log.i(TAG, "Completed cache cleaning.");
2012-02-26 21:25:13 +01:00
} catch (RuntimeException x) {
Log.e(TAG, "Error in cache cleaning.", x);
}
}
}).start();
2012-02-26 21:25:13 +01:00
}
private void deleteEmptyDirs(List<File> dirs, Set<File> undeletable) {
for (File dir : dirs) {
if (undeletable.contains(dir)) {
continue;
}
File[] children = dir.listFiles();
// Delete empty directory and associated album artwork.
if (children != null && children.length == 0) {
2012-02-26 21:25:13 +01:00
Util.delete(dir);
Util.delete(FileUtil.getAlbumArtFile(dir));
}
}
}
private void deleteFiles(List<File> files, Set<File> undeletable) {
if (files.isEmpty()) {
return;
}
long cacheSizeBytes = Util.getCacheSizeMB(context) * 1024L * 1024L;
2013-04-06 21:47:24 +02:00
long bytesUsedByUltraSonic = 0L;
2012-02-26 21:25:13 +01:00
for (File file : files) {
2013-04-06 21:47:24 +02:00
bytesUsedByUltraSonic += file.length();
2012-02-26 21:25:13 +01:00
}
long bytesToDelete = 0;
2012-02-26 21:25:13 +01:00
// Ensure that file system is not more than 95% full.
try
{
StatFs stat = new StatFs(files.get(0).getPath());
long bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize();
long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
long bytesUsedFs = bytesTotalFs - bytesAvailableFs;
long minFsAvailability = Math.round(MAX_FILE_SYSTEM_USAGE * (double) bytesTotalFs);
2013-04-06 21:47:24 +02:00
long bytesToDeleteCacheLimit = Math.max(bytesUsedByUltraSonic - cacheSizeBytes, 0L);
long bytesToDeleteFsLimit = Math.max(bytesUsedFs - minFsAvailability, 0L);
bytesToDelete = Math.max(bytesToDeleteCacheLimit, bytesToDeleteFsLimit);
Log.i(TAG, "File system : " + Util.formatBytes(bytesAvailableFs) + " of " + Util.formatBytes(bytesTotalFs) + " available");
Log.i(TAG, "Cache limit : " + Util.formatBytes(cacheSizeBytes));
2013-04-06 21:47:24 +02:00
Log.i(TAG, "Cache size before : " + Util.formatBytes(bytesUsedByUltraSonic));
Log.i(TAG, "Minimum to delete : " + Util.formatBytes(bytesToDelete));
} catch (Exception x) {
//
}
2012-02-26 21:25:13 +01:00
long bytesDeleted = 0L;
for (File file : files) {
if (file.getName().equals(Constants.ALBUM_ART_FILE)) {
// Move artwork to new folder.
file.renameTo(FileUtil.getAlbumArtFile(file.getParentFile()));
} else if (bytesToDelete > bytesDeleted || file.getName().endsWith(".partial") || file.getName().contains(".partial.")) {
if (!undeletable.contains(file)) {
long size = file.length();
if (Util.delete(file)) {
bytesDeleted += size;
}
}
}
}
Log.i(TAG, "Deleted : " + Util.formatBytes(bytesDeleted));
2013-04-06 21:47:24 +02:00
Log.i(TAG, "Cache size after : " + Util.formatBytes(bytesUsedByUltraSonic - bytesDeleted));
2012-02-26 21:25:13 +01:00
}
private void findCandidatesForDeletion(File file, List<File> files, List<File> dirs) {
if (file.isFile()) {
String name = file.getName();
boolean isCacheFile = name.endsWith(".partial") || name.contains(".partial.") || name.endsWith(".complete") || name.contains(".complete.");
boolean isAlbumArtFile = name.equals(Constants.ALBUM_ART_FILE);
if (isCacheFile || isAlbumArtFile) {
files.add(file);
}
} else {
// Depth-first
for (File child : FileUtil.listFiles(file)) {
findCandidatesForDeletion(child, files, dirs);
}
dirs.add(file);
}
}
private void sortByAscendingModificationTime(List<File> files) {
Collections.sort(files, new Comparator<File>() {
@Override
public int compare(File a, File b) {
if (a.lastModified() < b.lastModified()) {
return -1;
}
if (a.lastModified() > b.lastModified()) {
return 1;
}
return 0;
}
});
}
private Set<File> findUndeletableFiles() {
Set<File> undeletable = new HashSet<File>(5);
for (DownloadFile downloadFile : downloadService.getDownloads()) {
undeletable.add(downloadFile.getPartialFile());
undeletable.add(downloadFile.getCompleteFile());
}
undeletable.add(FileUtil.getMusicDirectory(context));
return undeletable;
}
}