Code improvements

* Replace unchecked casts with checked casts
 * remove Utility.finViewById
 * Fix return activity checking
 * Create UserAction enum
 * Fix typos
 * Add instrumented test for error info
 * ErrorInfo make fields final
 * Log exception using logger
 * Add inherited annotations
 * Resolve deprecation warnings
 * Remove unused methods from utility
 * Reformat code
 * Remove unused methods from Utility and improve getFileExt
 * Create OnScrollBelowItemsListener
This commit is contained in:
Coffeemakr 2017-06-28 07:27:32 +02:00
parent 40213b2d6a
commit b03723c3fb
40 changed files with 2077 additions and 1981 deletions

View file

@ -9,12 +9,14 @@ public interface DownloadDataSource {
/**
* Load all missions
*
* @return a list of download missions
*/
List<DownloadMission> loadMissions();
/**
* Add a downlaod mission to the storage
* Add a download mission to the storage
*
* @param downloadMission the download mission to add
* @return the identifier of the mission
*/
@ -22,6 +24,7 @@ public interface DownloadDataSource {
/**
* Update a download mission which exists in the storage
*
* @param downloadMission the download mission to update
* @throws IllegalArgumentException if the mission was not added to storage
*/
@ -30,6 +33,7 @@ public interface DownloadDataSource {
/**
* Delete a download mission
*
* @param downloadMission the mission to delete
*/
void deleteMission(DownloadMission downloadMission);

View file

@ -1,48 +1,53 @@
package us.shandian.giga.get;
public interface DownloadManager
{
int BLOCK_SIZE = 512 * 1024;
public interface DownloadManager {
int BLOCK_SIZE = 512 * 1024;
/**
* Start a new download mission
* @param url the url to download
* @param location the location
* @param name the name of the file to create
* @param isAudio true if the download is an audio file
* @param threads the number of threads maximal used to download chunks of the file. @return the identifier of the mission.
/**
* Start a new download mission
*
* @param url the url to download
* @param location the location
* @param name the name of the file to create
* @param isAudio true if the download is an audio file
* @param threads the number of threads maximal used to download chunks of the file. @return the identifier of the mission.
*/
int startMission(String url, String location, String name, boolean isAudio, int threads);
int startMission(String url, String location, String name, boolean isAudio, int threads);
/**
* Resume the execution of a download mission.
* @param id the identifier of the mission to resume.
*/
void resumeMission(int id);
/**
* Pause the execution of a download mission.
* @param id the identifier of the mission to pause.
/**
* Resume the execution of a download mission.
*
* @param id the identifier of the mission to resume.
*/
void pauseMission(int id);
void resumeMission(int id);
/**
* Deletes the mission from the downloaded list but keeps the downloaded file.
* @param id The mission identifier
/**
* Pause the execution of a download mission.
*
* @param id the identifier of the mission to pause.
*/
void deleteMission(int id);
void pauseMission(int id);
/**
* Get the download mission by its identifier
* @param id the identifier of the download mission
* @return the download mission or null if the mission doesn't exist
/**
* Deletes the mission from the downloaded list but keeps the downloaded file.
*
* @param id The mission identifier
*/
DownloadMission getMission(int id);
void deleteMission(int id);
/**
* Get the number of download missions.
* @return the number of download missions.
/**
* Get the download mission by its identifier
*
* @param id the identifier of the download mission
* @return the download mission or null if the mission doesn't exist
*/
int getCount();
DownloadMission getMission(int id);
/**
* Get the number of download missions.
*
* @return the number of download missions.
*/
int getCount();
}

View file

@ -18,95 +18,96 @@ import java.util.Comparator;
import java.util.List;
import us.shandian.giga.util.Utility;
import static org.schabi.newpipe.BuildConfig.DEBUG;
public class DownloadManagerImpl implements DownloadManager
{
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
private final DownloadDataSource mDownloadDataSource;
public class DownloadManagerImpl implements DownloadManager {
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
private final DownloadDataSource mDownloadDataSource;
private final ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
private final ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
/**
* Create a new instance
* @param searchLocations the directories to search for unfinished downloads
*
* @param searchLocations the directories to search for unfinished downloads
* @param downloadDataSource the data source for finished downloads
*/
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) {
mDownloadDataSource = downloadDataSource;
loadMissions(searchLocations);
}
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) {
mDownloadDataSource = downloadDataSource;
loadMissions(searchLocations);
}
@Override
public int startMission(String url, String location, String name, boolean isAudio, int threads) {
DownloadMission existingMission = getMissionByLocation(location, name);
if(existingMission != null) {
// Already downloaded or downloading
if(existingMission.finished) {
// Overwrite mission
@Override
public int startMission(String url, String location, String name, boolean isAudio, int threads) {
DownloadMission existingMission = getMissionByLocation(location, name);
if (existingMission != null) {
// Already downloaded or downloading
if (existingMission.finished) {
// Overwrite mission
deleteMission(mMissions.indexOf(existingMission));
} else {
// Rename file (?)
} else {
// Rename file (?)
try {
name = generateUniqueName(location, name);
}catch (Exception e) {
} catch (Exception e) {
Log.e(TAG, "Unable to generate unique name", e);
name = System.currentTimeMillis() + name ;
name = System.currentTimeMillis() + name;
Log.i(TAG, "Using " + name);
}
}
}
}
}
DownloadMission mission = new DownloadMission(name, url, location);
mission.timestamp = System.currentTimeMillis();
mission.threadCount = threads;
mission.addListener(new MissionListener(mission));
new Initializer(mission).start();
return insertMission(mission);
}
DownloadMission mission = new DownloadMission(name, url, location);
mission.timestamp = System.currentTimeMillis();
mission.threadCount = threads;
mission.addListener(new MissionListener(mission));
new Initializer(mission).start();
return insertMission(mission);
}
@Override
public void resumeMission(int i) {
DownloadMission d = getMission(i);
if (!d.running && d.errCode == -1) {
d.start();
}
}
@Override
public void resumeMission(int i) {
DownloadMission d = getMission(i);
if (!d.running && d.errCode == -1) {
d.start();
}
}
@Override
public void pauseMission(int i) {
DownloadMission d = getMission(i);
if (d.running) {
d.pause();
}
}
@Override
public void pauseMission(int i) {
DownloadMission d = getMission(i);
if (d.running) {
d.pause();
}
}
@Override
public void deleteMission(int i) {
DownloadMission mission = getMission(i);
if(mission.finished) {
mDownloadDataSource.deleteMission(mission);
}
mission.delete();
mMissions.remove(i);
}
@Override
public void deleteMission(int i) {
DownloadMission mission = getMission(i);
if (mission.finished) {
mDownloadDataSource.deleteMission(mission);
}
mission.delete();
mMissions.remove(i);
}
private void loadMissions(Iterable<String> searchLocations) {
mMissions.clear();
loadFinishedMissions();
for(String location: searchLocations) {
loadMissions(location);
}
private void loadMissions(Iterable<String> searchLocations) {
mMissions.clear();
loadFinishedMissions();
for (String location : searchLocations) {
loadMissions(location);
}
}
}
/**
* Loads finished missions from the data source
*/
private void loadFinishedMissions() {
List<DownloadMission> finishedMissions = mDownloadDataSource.loadMissions();
if(finishedMissions == null) {
/**
* Loads finished missions from the data source
*/
private void loadFinishedMissions() {
List<DownloadMission> finishedMissions = mDownloadDataSource.loadMissions();
if (finishedMissions == null) {
finishedMissions = new ArrayList<>();
}
// Ensure its sorted
@ -117,251 +118,255 @@ public class DownloadManagerImpl implements DownloadManager
}
});
mMissions.ensureCapacity(mMissions.size() + finishedMissions.size());
for(DownloadMission mission: finishedMissions) {
File downloadedFile = mission.getDownloadedFile();
if(!downloadedFile.isFile()) {
if(DEBUG) {
Log.d(TAG, "downloaded file removed: " + downloadedFile.getAbsolutePath());
}
mDownloadDataSource.deleteMission(mission);
} else {
mission.length = downloadedFile.length();
mission.finished = true;
mission.running = false;
mMissions.add(mission);
}
}
}
for (DownloadMission mission : finishedMissions) {
File downloadedFile = mission.getDownloadedFile();
if (!downloadedFile.isFile()) {
if (DEBUG) {
Log.d(TAG, "downloaded file removed: " + downloadedFile.getAbsolutePath());
}
mDownloadDataSource.deleteMission(mission);
} else {
mission.length = downloadedFile.length();
mission.finished = true;
mission.running = false;
mMissions.add(mission);
}
}
}
private void loadMissions(String location) {
private void loadMissions(String location) {
File f = new File(location);
File f = new File(location);
if (f.exists() && f.isDirectory()) {
File[] subs = f.listFiles();
if (f.exists() && f.isDirectory()) {
File[] subs = f.listFiles();
if(subs == null) {
if (subs == null) {
Log.e(TAG, "listFiles() returned null");
return;
}
for (File sub : subs) {
if (sub.isFile() && sub.getName().endsWith(".giga")) {
String str = Utility.readFromFile(sub.getAbsolutePath());
if (str != null && !str.trim().equals("")) {
for (File sub : subs) {
if (sub.isFile() && sub.getName().endsWith(".giga")) {
String str = Utility.readFromFile(sub.getAbsolutePath());
if (str != null && !str.trim().equals("")) {
if (DEBUG) {
Log.d(TAG, "loading mission " + sub.getName());
Log.d(TAG, str);
}
if (DEBUG) {
Log.d(TAG, "loading mission " + sub.getName());
Log.d(TAG, str);
}
DownloadMission mis = new Gson().fromJson(str, DownloadMission.class);
DownloadMission mis = new Gson().fromJson(str, DownloadMission.class);
if (mis.finished) {
if(!sub.delete()) {
if (mis.finished) {
if (!sub.delete()) {
Log.w(TAG, "Unable to delete .giga file: " + sub.getPath());
}
continue;
}
continue;
}
mis.running = false;
mis.recovered = true;
insertMission(mis);
}
}
}
}
}
@Override
public DownloadMission getMission(int i) {
return mMissions.get(i);
}
@Override
public int getCount() {
return mMissions.size();
}
private int insertMission(DownloadMission mission) {
int i = -1;
DownloadMission m = null;
if (mMissions.size() > 0) {
do {
m = mMissions.get(++i);
} while (m.timestamp > mission.timestamp && i < mMissions.size() - 1);
//if (i > 0) i--;
} else {
i = 0;
}
mMissions.add(i, mission);
return i;
}
mis.running = false;
mis.recovered = true;
insertMission(mis);
}
}
}
}
}
@Override
public DownloadMission getMission(int i) {
return mMissions.get(i);
}
@Override
public int getCount() {
return mMissions.size();
}
private int insertMission(DownloadMission mission) {
int i = -1;
DownloadMission m = null;
if (mMissions.size() > 0) {
do {
m = mMissions.get(++i);
} while (m.timestamp > mission.timestamp && i < mMissions.size() - 1);
//if (i > 0) i--;
} else {
i = 0;
}
mMissions.add(i, mission);
return i;
}
/**
* Get a mission by its location and name
* @param location the location
* @param name the name
* @return the mission or null if no such mission exists
*/
private @Nullable DownloadMission getMissionByLocation(String location, String name) {
for(DownloadMission mission: mMissions) {
if(location.equals(mission.location) && name.equals(mission.name)) {
return mission;
}
}
return null;
}
/**
* Splits the filename into name and extension
*
* Dots are ignored if they appear: not at all, at the beginning of the file,
* at the end of the file
*
* @param name the name to split
* @return a string array with a length of 2 containing the name and the extension
* Get a mission by its location and name
*
* @param location the location
* @param name the name
* @return the mission or null if no such mission exists
*/
private static String[] splitName(String name) {
int dotIndex = name.lastIndexOf('.');
if(dotIndex <= 0 || (dotIndex == name.length() - 1)) {
return new String[]{name, ""};
} else {
return new String[]{name.substring(0, dotIndex), name.substring(dotIndex + 1)};
}
}
private
@Nullable
DownloadMission getMissionByLocation(String location, String name) {
for (DownloadMission mission : mMissions) {
if (location.equals(mission.location) && name.equals(mission.name)) {
return mission;
}
}
return null;
}
/**
* Generates a unique file name.
*
* e.g. "myname (1).txt" if the name "myname.txt" exists.
* @param location the location (to check for existing files)
* @param name the name of the file
/**
* Splits the filename into name and extension
* <p>
* Dots are ignored if they appear: not at all, at the beginning of the file,
* at the end of the file
*
* @param name the name to split
* @return a string array with a length of 2 containing the name and the extension
*/
private static String[] splitName(String name) {
int dotIndex = name.lastIndexOf('.');
if (dotIndex <= 0 || (dotIndex == name.length() - 1)) {
return new String[]{name, ""};
} else {
return new String[]{name.substring(0, dotIndex), name.substring(dotIndex + 1)};
}
}
/**
* Generates a unique file name.
* <p>
* e.g. "myname (1).txt" if the name "myname.txt" exists.
*
* @param location the location (to check for existing files)
* @param name the name of the file
* @return the unique file name
* @throws IllegalArgumentException if the location is not a directory
* @throws SecurityException if the location is not readable
* @throws IllegalArgumentException if the location is not a directory
* @throws SecurityException if the location is not readable
*/
private static String generateUniqueName(String location, String name) {
if(location == null) throw new NullPointerException("location is null");
if(name == null) throw new NullPointerException("name is null");
File destination = new File(location);
if(!destination.isDirectory()) {
throw new IllegalArgumentException("location is not a directory: " + location);
}
final String[] nameParts = splitName(name);
String[] existingName = destination.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith(nameParts[0]);
}
});
Arrays.sort(existingName);
String newName;
int downloadIndex = 0;
do {
newName = nameParts[0] + " (" + downloadIndex + ")." + nameParts[1];
++downloadIndex;
if(downloadIndex == 1000) { // Probably an error on our side
throw new RuntimeException("Too many existing files");
}
} while (Arrays.binarySearch(existingName, newName) >= 0);
return newName;
}
private static String generateUniqueName(String location, String name) {
if (location == null) throw new NullPointerException("location is null");
if (name == null) throw new NullPointerException("name is null");
File destination = new File(location);
if (!destination.isDirectory()) {
throw new IllegalArgumentException("location is not a directory: " + location);
}
final String[] nameParts = splitName(name);
String[] existingName = destination.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith(nameParts[0]);
}
});
Arrays.sort(existingName);
String newName;
int downloadIndex = 0;
do {
newName = nameParts[0] + " (" + downloadIndex + ")." + nameParts[1];
++downloadIndex;
if (downloadIndex == 1000) { // Probably an error on our side
throw new RuntimeException("Too many existing files");
}
} while (Arrays.binarySearch(existingName, newName) >= 0);
return newName;
}
private class Initializer extends Thread {
private DownloadMission mission;
public Initializer(DownloadMission mission) {
this.mission = mission;
}
@Override
public void run() {
try {
URL url = new URL(mission.url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
mission.length = conn.getContentLength();
if (mission.length <= 0) {
mission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
//mission.notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
return;
}
// Open again
conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range", "bytes=" + (mission.length - 10) + "-" + mission.length);
if (conn.getResponseCode() != 206) {
// Fallback to single thread if no partial content support
mission.fallback = true;
if (DEBUG) {
Log.d(TAG, "falling back");
}
}
if (DEBUG) {
Log.d(TAG, "response = " + conn.getResponseCode());
}
mission.blocks = mission.length / BLOCK_SIZE;
if (mission.threadCount > mission.blocks) {
mission.threadCount = (int) mission.blocks;
}
if (mission.threadCount <= 0) {
mission.threadCount = 1;
}
if (mission.blocks * BLOCK_SIZE < mission.length) {
mission.blocks++;
}
private class Initializer extends Thread {
private DownloadMission mission;
new File(mission.location).mkdirs();
new File(mission.location + "/" + mission.name).createNewFile();
RandomAccessFile af = new RandomAccessFile(mission.location + "/" + mission.name, "rw");
af.setLength(mission.length);
af.close();
mission.start();
} catch (Exception e) {
// TODO Notify
throw new RuntimeException(e);
}
}
}
public Initializer(DownloadMission mission) {
this.mission = mission;
}
/**
* Waits for mission to finish to add it to the {@link #mDownloadDataSource}
*/
private class MissionListener implements DownloadMission.MissionListener {
private final DownloadMission mMission;
@Override
public void run() {
try {
URL url = new URL(mission.url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
mission.length = conn.getContentLength();
private MissionListener(DownloadMission mission) {
if(mission == null) throw new NullPointerException("mission is null");
// Could the mission be passed in onFinish()?
mMission = mission;
}
if (mission.length <= 0) {
mission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
//mission.notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
return;
}
@Override
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
}
// Open again
conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range", "bytes=" + (mission.length - 10) + "-" + mission.length);
@Override
public void onFinish(DownloadMission downloadMission) {
mDownloadDataSource.addMission(mMission);
}
if (conn.getResponseCode() != 206) {
// Fallback to single thread if no partial content support
mission.fallback = true;
@Override
public void onError(DownloadMission downloadMission, int errCode) {
}
}
if (DEBUG) {
Log.d(TAG, "falling back");
}
}
if (DEBUG) {
Log.d(TAG, "response = " + conn.getResponseCode());
}
mission.blocks = mission.length / BLOCK_SIZE;
if (mission.threadCount > mission.blocks) {
mission.threadCount = (int) mission.blocks;
}
if (mission.threadCount <= 0) {
mission.threadCount = 1;
}
if (mission.blocks * BLOCK_SIZE < mission.length) {
mission.blocks++;
}
new File(mission.location).mkdirs();
new File(mission.location + "/" + mission.name).createNewFile();
RandomAccessFile af = new RandomAccessFile(mission.location + "/" + mission.name, "rw");
af.setLength(mission.length);
af.close();
mission.start();
} catch (Exception e) {
// TODO Notify
throw new RuntimeException(e);
}
}
}
/**
* Waits for mission to finish to add it to the {@link #mDownloadDataSource}
*/
private class MissionListener implements DownloadMission.MissionListener {
private final DownloadMission mMission;
private MissionListener(DownloadMission mission) {
if (mission == null) throw new NullPointerException("mission is null");
// Could the mission be passed in onFinish()?
mMission = mission;
}
@Override
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
}
@Override
public void onFinish(DownloadMission downloadMission) {
mDownloadDataSource.addMission(mMission);
}
@Override
public void onError(DownloadMission downloadMission, int errCode) {
}
}
}

View file

@ -18,311 +18,315 @@ import us.shandian.giga.util.Utility;
import static org.schabi.newpipe.BuildConfig.DEBUG;
public class DownloadMission
{
private static final String TAG = DownloadMission.class.getSimpleName();
public class DownloadMission {
private static final String TAG = DownloadMission.class.getSimpleName();
public interface MissionListener {
HashMap<MissionListener, Handler> handlerStore = new HashMap<>();
void onProgressUpdate(DownloadMission downloadMission, long done, long total);
void onFinish(DownloadMission downloadMission);
void onError(DownloadMission downloadMission, int errCode);
}
public static final int ERROR_SERVER_UNSUPPORTED = 206;
public static final int ERROR_UNKNOWN = 233;
public interface MissionListener {
HashMap<MissionListener, Handler> handlerStore = new HashMap<>();
/**
* The filename
*/
public String name;
void onProgressUpdate(DownloadMission downloadMission, long done, long total);
/**
* The url of the file to download
*/
public String url;
void onFinish(DownloadMission downloadMission);
/**
* The directory to store the download
*/
public String location;
void onError(DownloadMission downloadMission, int errCode);
}
/**
* Number of blocks the size of {@link DownloadManager#BLOCK_SIZE}
*/
public long blocks;
public static final int ERROR_SERVER_UNSUPPORTED = 206;
public static final int ERROR_UNKNOWN = 233;
/**
* Number of bytes
*/
public long length;
/**
* Number of bytes downloaded
*/
public long done;
public int threadCount = 3;
public int finishCount;
private List<Long> threadPositions = new ArrayList<Long>();
public final Map<Long, Boolean> blockState = new HashMap<Long, Boolean>();
public boolean running;
public boolean finished;
public boolean fallback;
public int errCode = -1;
public long timestamp;
public transient boolean recovered;
private transient ArrayList<WeakReference<MissionListener>> mListeners = new ArrayList<WeakReference<MissionListener>>();
private transient boolean mWritingToFile;
private static final int NO_IDENTIFIER = -1;
private long db_identifier = NO_IDENTIFIER;
public DownloadMission() {
}
public DownloadMission(String name, String url, String location) {
if(name == null) throw new NullPointerException("name is null");
if(name.isEmpty()) throw new IllegalArgumentException("name is empty");
if(url == null) throw new NullPointerException("url is null");
if(url.isEmpty()) throw new IllegalArgumentException("url is empty");
if(location == null) throw new NullPointerException("location is null");
if(location.isEmpty()) throw new IllegalArgumentException("location is empty");
this.url = url;
this.name = name;
this.location = location;
}
private void checkBlock(long block) {
if(block < 0 || block >= blocks) {
throw new IllegalArgumentException("illegal block identifier");
}
}
/**
* Check if a block is reserved
* @param block the block identifier
* @return true if the block is reserved and false if otherwise
/**
* The filename
*/
public boolean isBlockPreserved(long block) {
checkBlock(block);
return blockState.containsKey(block) ? blockState.get(block) : false;
}
public void preserveBlock(long block) {
checkBlock(block);
synchronized (blockState) {
blockState.put(block, true);
}
}
public String name;
/**
* Set the download position of the file
* @param threadId the identifier of the thread
* @param position the download position of the thread
/**
* The url of the file to download
*/
public void setPosition(int threadId, long position) {
threadPositions.set(threadId, position);
}
public String url;
/**
* Get the position of a thread
* @param threadId the identifier of the thread
* @return the position for the thread
/**
* The directory to store the download
*/
public long getPosition(int threadId) {
return threadPositions.get(threadId);
}
public synchronized void notifyProgress(long deltaLen) {
if (!running) return;
if (recovered) {
recovered = false;
}
done += deltaLen;
if (done > length) {
done = length;
}
if (done != length) {
writeThisToFile();
}
for (WeakReference<MissionListener> ref: mListeners) {
final MissionListener listener = ref.get();
if (listener != null) {
MissionListener.handlerStore.get(listener).post(new Runnable() {
@Override
public void run() {
listener.onProgressUpdate(DownloadMission.this, done, length);
}
});
}
}
}
public String location;
/**
* Called by a download thread when it finished.
*/
public synchronized void notifyFinished() {
if (errCode > 0) return;
finishCount++;
if (finishCount == threadCount) {
onFinish();
}
}
/**
* Called when all parts are downloaded
*/
private void onFinish() {
if (errCode > 0) return;
if (DEBUG) {
Log.d(TAG, "onFinish");
}
running = false;
finished = true;
deleteThisFromFile();
for (WeakReference<MissionListener> ref : mListeners) {
final MissionListener listener = ref.get();
if (listener != null) {
MissionListener.handlerStore.get(listener).post(new Runnable() {
@Override
public void run() {
listener.onFinish(DownloadMission.this);
}
});
}
}
}
public synchronized void notifyError(int err) {
errCode = err;
writeThisToFile();
for (WeakReference<MissionListener> ref : mListeners) {
final MissionListener listener = ref.get();
MissionListener.handlerStore.get(listener).post(new Runnable() {
@Override
public void run() {
listener.onError(DownloadMission.this, errCode);
}
});
}
}
public synchronized void addListener(MissionListener listener) {
Handler handler = new Handler(Looper.getMainLooper());
MissionListener.handlerStore.put(listener, handler);
mListeners.add(new WeakReference<MissionListener>(listener));
}
public synchronized void removeListener(MissionListener listener) {
for (Iterator<WeakReference<MissionListener>> iterator = mListeners.iterator();
iterator.hasNext(); ) {
WeakReference<MissionListener> weakRef = iterator.next();
if (listener!=null && listener == weakRef.get())
{
iterator.remove();
}
}
}
/**
* Start downloading with multiple threads.
*/
public void start() {
if (!running && !finished) {
running = true;
if (!fallback) {
for (int i = 0; i < threadCount; i++) {
if (threadPositions.size() <= i && !recovered) {
threadPositions.add((long) i);
}
new Thread(new DownloadRunnable(this, i)).start();
}
} else {
// In fallback mode, resuming is not supported.
threadCount = 1;
done = 0;
blocks = 0;
new Thread(new DownloadRunnableFallback(this)).start();
}
}
}
public void pause() {
if (running) {
running = false;
recovered = true;
// TODO: Notify & Write state to info file
// if (err)
}
}
/**
* Removes the file and the meta file
*/
public void delete() {
deleteThisFromFile();
new File(location, name).delete();
}
/**
* Write this {@link DownloadMission} to the meta file asynchronously
* if no thread is already running.
*/
public void writeThisToFile() {
if (!mWritingToFile) {
mWritingToFile = true;
new Thread() {
@Override
public void run() {
doWriteThisToFile();
mWritingToFile = false;
}
}.start();
}
}
/**
* Write this {@link DownloadMission} to the meta file.
*/
private void doWriteThisToFile() {
synchronized (blockState) {
Utility.writeToFile(getMetaFilename(), new Gson().toJson(this));
}
}
private void deleteThisFromFile() {
new File(getMetaFilename()).delete();
}
/**
* Get the path of the meta file
* @return the path to the meta file
/**
* Number of blocks the size of {@link DownloadManager#BLOCK_SIZE}
*/
private String getMetaFilename() {
return location + "/" + name + ".giga";
}
public long blocks;
public File getDownloadedFile() {
return new File(location, name);
}
/**
* Number of bytes
*/
public long length;
/**
* Number of bytes downloaded
*/
public long done;
public int threadCount = 3;
public int finishCount;
private List<Long> threadPositions = new ArrayList<Long>();
public final Map<Long, Boolean> blockState = new HashMap<Long, Boolean>();
public boolean running;
public boolean finished;
public boolean fallback;
public int errCode = -1;
public long timestamp;
public transient boolean recovered;
private transient ArrayList<WeakReference<MissionListener>> mListeners = new ArrayList<WeakReference<MissionListener>>();
private transient boolean mWritingToFile;
private static final int NO_IDENTIFIER = -1;
private long db_identifier = NO_IDENTIFIER;
public DownloadMission() {
}
public DownloadMission(String name, String url, String location) {
if (name == null) throw new NullPointerException("name is null");
if (name.isEmpty()) throw new IllegalArgumentException("name is empty");
if (url == null) throw new NullPointerException("url is null");
if (url.isEmpty()) throw new IllegalArgumentException("url is empty");
if (location == null) throw new NullPointerException("location is null");
if (location.isEmpty()) throw new IllegalArgumentException("location is empty");
this.url = url;
this.name = name;
this.location = location;
}
private void checkBlock(long block) {
if (block < 0 || block >= blocks) {
throw new IllegalArgumentException("illegal block identifier");
}
}
/**
* Check if a block is reserved
*
* @param block the block identifier
* @return true if the block is reserved and false if otherwise
*/
public boolean isBlockPreserved(long block) {
checkBlock(block);
return blockState.containsKey(block) ? blockState.get(block) : false;
}
public void preserveBlock(long block) {
checkBlock(block);
synchronized (blockState) {
blockState.put(block, true);
}
}
/**
* Set the download position of the file
*
* @param threadId the identifier of the thread
* @param position the download position of the thread
*/
public void setPosition(int threadId, long position) {
threadPositions.set(threadId, position);
}
/**
* Get the position of a thread
*
* @param threadId the identifier of the thread
* @return the position for the thread
*/
public long getPosition(int threadId) {
return threadPositions.get(threadId);
}
public synchronized void notifyProgress(long deltaLen) {
if (!running) return;
if (recovered) {
recovered = false;
}
done += deltaLen;
if (done > length) {
done = length;
}
if (done != length) {
writeThisToFile();
}
for (WeakReference<MissionListener> ref : mListeners) {
final MissionListener listener = ref.get();
if (listener != null) {
MissionListener.handlerStore.get(listener).post(new Runnable() {
@Override
public void run() {
listener.onProgressUpdate(DownloadMission.this, done, length);
}
});
}
}
}
/**
* Called by a download thread when it finished.
*/
public synchronized void notifyFinished() {
if (errCode > 0) return;
finishCount++;
if (finishCount == threadCount) {
onFinish();
}
}
/**
* Called when all parts are downloaded
*/
private void onFinish() {
if (errCode > 0) return;
if (DEBUG) {
Log.d(TAG, "onFinish");
}
running = false;
finished = true;
deleteThisFromFile();
for (WeakReference<MissionListener> ref : mListeners) {
final MissionListener listener = ref.get();
if (listener != null) {
MissionListener.handlerStore.get(listener).post(new Runnable() {
@Override
public void run() {
listener.onFinish(DownloadMission.this);
}
});
}
}
}
public synchronized void notifyError(int err) {
errCode = err;
writeThisToFile();
for (WeakReference<MissionListener> ref : mListeners) {
final MissionListener listener = ref.get();
MissionListener.handlerStore.get(listener).post(new Runnable() {
@Override
public void run() {
listener.onError(DownloadMission.this, errCode);
}
});
}
}
public synchronized void addListener(MissionListener listener) {
Handler handler = new Handler(Looper.getMainLooper());
MissionListener.handlerStore.put(listener, handler);
mListeners.add(new WeakReference<MissionListener>(listener));
}
public synchronized void removeListener(MissionListener listener) {
for (Iterator<WeakReference<MissionListener>> iterator = mListeners.iterator();
iterator.hasNext(); ) {
WeakReference<MissionListener> weakRef = iterator.next();
if (listener != null && listener == weakRef.get()) {
iterator.remove();
}
}
}
/**
* Start downloading with multiple threads.
*/
public void start() {
if (!running && !finished) {
running = true;
if (!fallback) {
for (int i = 0; i < threadCount; i++) {
if (threadPositions.size() <= i && !recovered) {
threadPositions.add((long) i);
}
new Thread(new DownloadRunnable(this, i)).start();
}
} else {
// In fallback mode, resuming is not supported.
threadCount = 1;
done = 0;
blocks = 0;
new Thread(new DownloadRunnableFallback(this)).start();
}
}
}
public void pause() {
if (running) {
running = false;
recovered = true;
// TODO: Notify & Write state to info file
// if (err)
}
}
/**
* Removes the file and the meta file
*/
public void delete() {
deleteThisFromFile();
new File(location, name).delete();
}
/**
* Write this {@link DownloadMission} to the meta file asynchronously
* if no thread is already running.
*/
public void writeThisToFile() {
if (!mWritingToFile) {
mWritingToFile = true;
new Thread() {
@Override
public void run() {
doWriteThisToFile();
mWritingToFile = false;
}
}.start();
}
}
/**
* Write this {@link DownloadMission} to the meta file.
*/
private void doWriteThisToFile() {
synchronized (blockState) {
Utility.writeToFile(getMetaFilename(), new Gson().toJson(this));
}
}
private void deleteThisFromFile() {
new File(getMetaFilename()).delete();
}
/**
* Get the path of the meta file
*
* @return the path to the meta file
*/
private String getMetaFilename() {
return location + "/" + name + ".giga";
}
public File getDownloadedFile() {
return new File(location, name);
}
}

View file

@ -13,166 +13,165 @@ import static org.schabi.newpipe.BuildConfig.DEBUG;
* Runnable to download blocks of a file until the file is completely downloaded,
* an error occurs or the process is stopped.
*/
public class DownloadRunnable implements Runnable
{
private static final String TAG = DownloadRunnable.class.getSimpleName();
private final DownloadMission mMission;
private final int mId;
public DownloadRunnable(DownloadMission mission, int id) {
if(mission == null) throw new NullPointerException("mission is null");
mMission = mission;
mId = id;
}
@Override
public void run() {
boolean retry = mMission.recovered;
long position = mMission.getPosition(mId);
if (DEBUG) {
Log.d(TAG, mId + ":default pos " + position);
Log.d(TAG, mId + ":recovered: " + mMission.recovered);
}
while (mMission.errCode == -1 && mMission.running && position < mMission.blocks) {
if (Thread.currentThread().isInterrupted()) {
mMission.pause();
return;
}
if (DEBUG && retry) {
Log.d(TAG, mId + ":retry is true. Resuming at " + position);
}
// Wait for an unblocked position
while (!retry && position < mMission.blocks && mMission.isBlockPreserved(position)) {
if (DEBUG) {
Log.d(TAG, mId + ":position " + position + " preserved, passing");
}
position++;
}
retry = false;
if (position >= mMission.blocks) {
break;
}
if (DEBUG) {
Log.d(TAG, mId + ":preserving position " + position);
}
mMission.preserveBlock(position);
mMission.setPosition(mId, position);
long start = position * DownloadManager.BLOCK_SIZE;
long end = start + DownloadManager.BLOCK_SIZE - 1;
if (end >= mMission.length) {
end = mMission.length - 1;
}
HttpURLConnection conn = null;
int total = 0;
try {
URL url = new URL(mMission.url);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
if (DEBUG) {
Log.d(TAG, mId + ":" + conn.getRequestProperty("Range"));
Log.d(TAG, mId + ":Content-Length=" + conn.getContentLength() + " Code:" + conn.getResponseCode());
}
// A server may be ignoring the range request
if (conn.getResponseCode() != 206) {
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
if (DEBUG) {
Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode());
}
break;
}
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
f.seek(start);
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
byte[] buf = new byte[512];
while (start < end && mMission.running) {
int len = ipt.read(buf, 0, 512);
if (len == -1) {
break;
} else {
start += len;
total += len;
f.write(buf, 0, len);
notifyProgress(len);
}
}
if (DEBUG && mMission.running) {
Log.d(TAG, mId + ":position " + position + " finished, total length " + total);
}
f.close();
ipt.close();
// TODO We should save progress for each thread
} catch (Exception e) {
// TODO Retry count limit & notify error
retry = true;
notifyProgress(-total);
if (DEBUG) {
Log.d(TAG, mId + ":position " + position + " retrying", e);
}
}
}
if (DEBUG) {
Log.d(TAG, "thread " + mId + " exited main loop");
}
if (mMission.errCode == -1 && mMission.running) {
if (DEBUG) {
Log.d(TAG, "no error has happened, notifying");
}
notifyFinished();
}
if (DEBUG && !mMission.running) {
Log.d(TAG, "The mission has been paused. Passing.");
}
}
private void notifyProgress(final long len) {
synchronized (mMission) {
mMission.notifyProgress(len);
}
}
private void notifyError(final int err) {
synchronized (mMission) {
mMission.notifyError(err);
mMission.pause();
}
}
private void notifyFinished() {
synchronized (mMission) {
mMission.notifyFinished();
}
}
public class DownloadRunnable implements Runnable {
private static final String TAG = DownloadRunnable.class.getSimpleName();
private final DownloadMission mMission;
private final int mId;
public DownloadRunnable(DownloadMission mission, int id) {
if (mission == null) throw new NullPointerException("mission is null");
mMission = mission;
mId = id;
}
@Override
public void run() {
boolean retry = mMission.recovered;
long position = mMission.getPosition(mId);
if (DEBUG) {
Log.d(TAG, mId + ":default pos " + position);
Log.d(TAG, mId + ":recovered: " + mMission.recovered);
}
while (mMission.errCode == -1 && mMission.running && position < mMission.blocks) {
if (Thread.currentThread().isInterrupted()) {
mMission.pause();
return;
}
if (DEBUG && retry) {
Log.d(TAG, mId + ":retry is true. Resuming at " + position);
}
// Wait for an unblocked position
while (!retry && position < mMission.blocks && mMission.isBlockPreserved(position)) {
if (DEBUG) {
Log.d(TAG, mId + ":position " + position + " preserved, passing");
}
position++;
}
retry = false;
if (position >= mMission.blocks) {
break;
}
if (DEBUG) {
Log.d(TAG, mId + ":preserving position " + position);
}
mMission.preserveBlock(position);
mMission.setPosition(mId, position);
long start = position * DownloadManager.BLOCK_SIZE;
long end = start + DownloadManager.BLOCK_SIZE - 1;
if (end >= mMission.length) {
end = mMission.length - 1;
}
HttpURLConnection conn = null;
int total = 0;
try {
URL url = new URL(mMission.url);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
if (DEBUG) {
Log.d(TAG, mId + ":" + conn.getRequestProperty("Range"));
Log.d(TAG, mId + ":Content-Length=" + conn.getContentLength() + " Code:" + conn.getResponseCode());
}
// A server may be ignoring the range request
if (conn.getResponseCode() != 206) {
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
if (DEBUG) {
Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode());
}
break;
}
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
f.seek(start);
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
byte[] buf = new byte[512];
while (start < end && mMission.running) {
int len = ipt.read(buf, 0, 512);
if (len == -1) {
break;
} else {
start += len;
total += len;
f.write(buf, 0, len);
notifyProgress(len);
}
}
if (DEBUG && mMission.running) {
Log.d(TAG, mId + ":position " + position + " finished, total length " + total);
}
f.close();
ipt.close();
// TODO We should save progress for each thread
} catch (Exception e) {
// TODO Retry count limit & notify error
retry = true;
notifyProgress(-total);
if (DEBUG) {
Log.d(TAG, mId + ":position " + position + " retrying", e);
}
}
}
if (DEBUG) {
Log.d(TAG, "thread " + mId + " exited main loop");
}
if (mMission.errCode == -1 && mMission.running) {
if (DEBUG) {
Log.d(TAG, "no error has happened, notifying");
}
notifyFinished();
}
if (DEBUG && !mMission.running) {
Log.d(TAG, "The mission has been paused. Passing.");
}
}
private void notifyProgress(final long len) {
synchronized (mMission) {
mMission.notifyProgress(len);
}
}
private void notifyError(final int err) {
synchronized (mMission) {
mMission.notifyError(err);
mMission.pause();
}
}
private void notifyFinished() {
synchronized (mMission) {
mMission.notifyFinished();
}
}
}

View file

@ -6,70 +6,69 @@ import java.net.HttpURLConnection;
import java.net.URL;
// Single-threaded fallback mode
public class DownloadRunnableFallback implements Runnable
{
private final DownloadMission mMission;
//private int mId;
public DownloadRunnableFallback(DownloadMission mission) {
if(mission == null) throw new NullPointerException("mission is null");
//mId = id;
mMission = mission;
}
public class DownloadRunnableFallback implements Runnable {
private final DownloadMission mMission;
//private int mId;
@Override
public void run() {
try {
URL url = new URL(mMission.url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn.getResponseCode() != 200 && conn.getResponseCode() != 206) {
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
} else {
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
f.seek(0);
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
byte[] buf = new byte[512];
int len = 0;
while ((len = ipt.read(buf, 0, 512)) != -1 && mMission.running) {
f.write(buf, 0, len);
notifyProgress(len);
if (Thread.interrupted()) {
break;
}
}
f.close();
ipt.close();
}
} catch (Exception e) {
notifyError(DownloadMission.ERROR_UNKNOWN);
}
if (mMission.errCode == -1 && mMission.running) {
notifyFinished();
}
}
private void notifyProgress(final long len) {
synchronized (mMission) {
mMission.notifyProgress(len);
}
}
private void notifyError(final int err) {
synchronized (mMission) {
mMission.notifyError(err);
mMission.pause();
}
}
public DownloadRunnableFallback(DownloadMission mission) {
if (mission == null) throw new NullPointerException("mission is null");
//mId = id;
mMission = mission;
}
private void notifyFinished() {
synchronized (mMission) {
mMission.notifyFinished();
}
}
@Override
public void run() {
try {
URL url = new URL(mMission.url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn.getResponseCode() != 200 && conn.getResponseCode() != 206) {
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
} else {
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
f.seek(0);
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
byte[] buf = new byte[512];
int len = 0;
while ((len = ipt.read(buf, 0, 512)) != -1 && mMission.running) {
f.write(buf, 0, len);
notifyProgress(len);
if (Thread.interrupted()) {
break;
}
}
f.close();
ipt.close();
}
} catch (Exception e) {
notifyError(DownloadMission.ERROR_UNKNOWN);
}
if (mMission.errCode == -1 && mMission.running) {
notifyFinished();
}
}
private void notifyProgress(final long len) {
synchronized (mMission) {
mMission.notifyProgress(len);
}
}
private void notifyError(final int err) {
synchronized (mMission) {
mMission.notifyError(err);
mMission.pause();
}
}
private void notifyFinished() {
synchronized (mMission) {
mMission.notifyFinished();
}
}
}

View file

@ -64,6 +64,7 @@ public class DownloadMissionSQLiteHelper extends SQLiteOpenHelper {
/**
* Returns all values of the download mission as ContentValues.
*
* @param downloadMission the download mission
* @return the content values
*/
@ -88,7 +89,7 @@ public class DownloadMissionSQLiteHelper extends SQLiteOpenHelper {
}
public static DownloadMission getMissionFromCursor(Cursor cursor) {
if(cursor == null) throw new NullPointerException("cursor is null");
if (cursor == null) throw new NullPointerException("cursor is null");
int pos;
String name = cursor.getString(cursor.getColumnIndexOrThrow(KEY_NAME));
String location = cursor.getString(cursor.getColumnIndexOrThrow(KEY_LOCATION));

View file

@ -37,7 +37,7 @@ public class SQLiteDownloadDataSource implements DownloadDataSource {
null, null, null, DownloadMissionSQLiteHelper.KEY_TIMESTAMP);
int count = cursor.getCount();
if(count == 0) return new ArrayList<>();
if (count == 0) return new ArrayList<>();
result = new ArrayList<>(count);
while (cursor.moveToNext()) {
result.add(DownloadMissionSQLiteHelper.getMissionFromCursor(cursor));
@ -47,7 +47,7 @@ public class SQLiteDownloadDataSource implements DownloadDataSource {
@Override
public void addMission(DownloadMission downloadMission) {
if(downloadMission == null) throw new NullPointerException("downloadMission is null");
if (downloadMission == null) throw new NullPointerException("downloadMission is null");
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
ContentValues values = DownloadMissionSQLiteHelper.getValuesOfMission(downloadMission);
database.insert(MISSIONS_TABLE_NAME, null, values);
@ -55,25 +55,25 @@ public class SQLiteDownloadDataSource implements DownloadDataSource {
@Override
public void updateMission(DownloadMission downloadMission) {
if(downloadMission == null) throw new NullPointerException("downloadMission is null");
if (downloadMission == null) throw new NullPointerException("downloadMission is null");
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
ContentValues values = DownloadMissionSQLiteHelper.getValuesOfMission(downloadMission);
String whereClause = KEY_LOCATION+ " = ? AND " +
String whereClause = KEY_LOCATION + " = ? AND " +
KEY_NAME + " = ?";
int rowsAffected = database.update(MISSIONS_TABLE_NAME, values,
whereClause, new String[]{downloadMission.location, downloadMission.name});
if(rowsAffected != 1) {
if (rowsAffected != 1) {
Log.e(TAG, "Expected 1 row to be affected by update but got " + rowsAffected);
}
}
@Override
public void deleteMission(DownloadMission downloadMission) {
if(downloadMission == null) throw new NullPointerException("downloadMission is null");
if (downloadMission == null) throw new NullPointerException("downloadMission is null");
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
database.delete(MISSIONS_TABLE_NAME,
KEY_LOCATION + " = ? AND " +
KEY_NAME + " = ?",
KEY_NAME + " = ?",
new String[]{downloadMission.location, downloadMission.name});
}
}

View file

@ -6,7 +6,6 @@ import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@ -16,6 +15,7 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.support.v4.app.NotificationCompat.Builder;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.PermissionChecker;
import android.util.Log;
import android.widget.Toast;
@ -34,237 +34,235 @@ import us.shandian.giga.get.sqlite.SQLiteDownloadDataSource;
import static org.schabi.newpipe.BuildConfig.DEBUG;
public class DownloadManagerService extends Service
{
public class DownloadManagerService extends Service {
private static final String TAG = DownloadManagerService.class.getSimpleName();
private static final String TAG = DownloadManagerService.class.getSimpleName();
/**
* Message code of update messages stored as {@link Message#what}.
*/
private static final int UPDATE_MESSAGE = 0;
private static final int NOTIFICATION_ID = 1000;
private static final String EXTRA_NAME = "DownloadManagerService.extra.name";
private static final String EXTRA_LOCATION = "DownloadManagerService.extra.location";
private static final String EXTRA_IS_AUDIO = "DownloadManagerService.extra.is_audio";
private static final String EXTRA_THREADS = "DownloadManagerService.extra.threads";
/**
* Message code of update messages stored as {@link Message#what}.
*/
private static final int UPDATE_MESSAGE = 0;
private static final int NOTIFICATION_ID = 1000;
private static final String EXTRA_NAME = "DownloadManagerService.extra.name";
private static final String EXTRA_LOCATION = "DownloadManagerService.extra.location";
private static final String EXTRA_IS_AUDIO = "DownloadManagerService.extra.is_audio";
private static final String EXTRA_THREADS = "DownloadManagerService.extra.threads";
private DMBinder mBinder;
private DownloadManager mManager;
private Notification mNotification;
private Handler mHandler;
private long mLastTimeStamp = System.currentTimeMillis();
private DownloadDataSource mDataSource;
private DMBinder mBinder;
private DownloadManager mManager;
private Notification mNotification;
private Handler mHandler;
private long mLastTimeStamp = System.currentTimeMillis();
private DownloadDataSource mDataSource;
private MissionListener missionListener = new MissionListener();
private MissionListener missionListener = new MissionListener();
private void notifyMediaScanner(DownloadMission mission) {
Uri uri = Uri.parse("file://" + mission.location + "/" + mission.name);
// notify media scanner on downloaded media file ...
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
}
private void notifyMediaScanner(DownloadMission mission) {
Uri uri = Uri.parse("file://" + mission.location + "/" + mission.name);
// notify media scanner on downloaded media file ...
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
}
@Override
public void onCreate() {
super.onCreate();
@Override
public void onCreate() {
super.onCreate();
if (DEBUG) {
Log.d(TAG, "onCreate");
}
if (DEBUG) {
Log.d(TAG, "onCreate");
}
mBinder = new DMBinder();
if(mDataSource == null) {
mDataSource = new SQLiteDownloadDataSource(this);
}
if (mManager == null) {
ArrayList<String> paths = new ArrayList<>(2);
paths.add(NewPipeSettings.getVideoDownloadPath(this));
paths.add(NewPipeSettings.getAudioDownloadPath(this));
mManager = new DownloadManagerImpl(paths, mDataSource);
if (DEBUG) {
Log.d(TAG, "mManager == null");
Log.d(TAG, "Download directory: " + paths);
}
}
mBinder = new DMBinder();
if (mDataSource == null) {
mDataSource = new SQLiteDownloadDataSource(this);
}
if (mManager == null) {
ArrayList<String> paths = new ArrayList<>(2);
paths.add(NewPipeSettings.getVideoDownloadPath(this));
paths.add(NewPipeSettings.getAudioDownloadPath(this));
mManager = new DownloadManagerImpl(paths, mDataSource);
if (DEBUG) {
Log.d(TAG, "mManager == null");
Log.d(TAG, "Download directory: " + paths);
}
}
Intent i = new Intent();
i.setAction(Intent.ACTION_MAIN);
i.setClass(this, DownloadActivity.class);
Intent i = new Intent();
i.setAction(Intent.ACTION_MAIN);
i.setClass(this, DownloadActivity.class);
Drawable icon = this.getResources().getDrawable(R.mipmap.ic_launcher);
Drawable icon = ContextCompat.getDrawable(this, R.mipmap.ic_launcher);
Builder builder = new Builder(this)
.setContentIntent(PendingIntent.getActivity(this, 0, i, 0))
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
.setContentTitle(getString(R.string.msg_running))
.setContentText(getString(R.string.msg_running_detail));
Builder builder = new Builder(this)
.setContentIntent(PendingIntent.getActivity(this, 0, i, 0))
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
.setContentTitle(getString(R.string.msg_running))
.setContentText(getString(R.string.msg_running_detail));
PendingIntent pendingIntent =
PendingIntent.getActivity(
this,
0,
new Intent(this, DownloadActivity.class)
.setAction(DownloadActivity.INTENT_LIST),
PendingIntent.FLAG_UPDATE_CURRENT
);
PendingIntent pendingIntent =
PendingIntent.getActivity(
this,
0,
new Intent(this, DownloadActivity.class)
.setAction(DownloadActivity.INTENT_LIST),
PendingIntent.FLAG_UPDATE_CURRENT
);
builder.setContentIntent(pendingIntent);
builder.setContentIntent(pendingIntent);
mNotification = builder.build();
mNotification = builder.build();
HandlerThread thread = new HandlerThread("ServiceMessenger");
thread.start();
mHandler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_MESSAGE: {
int runningCount = 0;
HandlerThread thread = new HandlerThread("ServiceMessenger");
thread.start();
for (int i = 0; i < mManager.getCount(); i++) {
if (mManager.getMission(i).running) {
runningCount++;
}
}
updateState(runningCount);
break;
}
}
}
};
}
mHandler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_MESSAGE: {
int runningCount = 0;
private void startMissionAsync(final String url, final String location, final String name,
final boolean isAudio, final int threads) {
mHandler.post(new Runnable() {
@Override
public void run() {
int missionId = mManager.startMission(url, location, name, isAudio, threads);
mBinder.onMissionAdded(mManager.getMission(missionId));
}
});
}
for (int i = 0; i < mManager.getCount(); i++) {
if (mManager.getMission(i).running) {
runningCount++;
}
}
updateState(runningCount);
break;
}
}
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (DEBUG) {
Log.d(TAG, "Starting");
}
Log.i(TAG, "Got intent: " + intent);
String action = intent.getAction();
if(action != null && action.equals(Intent.ACTION_RUN)) {
String name = intent.getStringExtra(EXTRA_NAME);
String location = intent.getStringExtra(EXTRA_LOCATION);
int threads = intent.getIntExtra(EXTRA_THREADS, 1);
boolean isAudio = intent.getBooleanExtra(EXTRA_IS_AUDIO, false);
String url = intent.getDataString();
startMissionAsync(url, location, name, isAudio, threads);
}
return START_NOT_STICKY;
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (DEBUG) {
Log.d(TAG, "Destroying");
}
for (int i = 0; i < mManager.getCount(); i++) {
mManager.pauseMission(i);
}
private void startMissionAsync(final String url, final String location, final String name,
final boolean isAudio, final int threads) {
mHandler.post(new Runnable() {
@Override
public void run() {
int missionId = mManager.startMission(url, location, name, isAudio, threads);
mBinder.onMissionAdded(mManager.getMission(missionId));
}
});
}
stopForeground(true);
}
@Override
public IBinder onBind(Intent intent) {
int permissionCheck;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
if(permissionCheck == PermissionChecker.PERMISSION_DENIED) {
Toast.makeText(this, "Permission denied (read)", Toast.LENGTH_SHORT).show();
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (DEBUG) {
Log.d(TAG, "Starting");
}
Log.i(TAG, "Got intent: " + intent);
String action = intent.getAction();
if (action != null && action.equals(Intent.ACTION_RUN)) {
String name = intent.getStringExtra(EXTRA_NAME);
String location = intent.getStringExtra(EXTRA_LOCATION);
int threads = intent.getIntExtra(EXTRA_THREADS, 1);
boolean isAudio = intent.getBooleanExtra(EXTRA_IS_AUDIO, false);
String url = intent.getDataString();
startMissionAsync(url, location, name, isAudio, threads);
}
return START_NOT_STICKY;
}
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(permissionCheck == PermissionChecker.PERMISSION_DENIED) {
Toast.makeText(this, "Permission denied (write)", Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroy() {
super.onDestroy();
return mBinder;
}
if (DEBUG) {
Log.d(TAG, "Destroying");
}
private void postUpdateMessage() {
mHandler.sendEmptyMessage(UPDATE_MESSAGE);
}
private void updateState(int runningCount) {
if (runningCount == 0) {
stopForeground(true);
} else {
startForeground(NOTIFICATION_ID, mNotification);
}
}
for (int i = 0; i < mManager.getCount(); i++) {
mManager.pauseMission(i);
}
public static void startMission(Context context, String url, String location, String name, boolean isAudio, int threads) {
Intent intent = new Intent(context, DownloadManagerService.class);
intent.setAction(Intent.ACTION_RUN);
intent.setData(Uri.parse(url));
intent.putExtra(EXTRA_NAME, name);
intent.putExtra(EXTRA_LOCATION, location);
intent.putExtra(EXTRA_IS_AUDIO, isAudio);
intent.putExtra(EXTRA_THREADS, threads);
context.startService(intent);
}
stopForeground(true);
}
@Override
public IBinder onBind(Intent intent) {
int permissionCheck;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
if (permissionCheck == PermissionChecker.PERMISSION_DENIED) {
Toast.makeText(this, "Permission denied (read)", Toast.LENGTH_SHORT).show();
}
}
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permissionCheck == PermissionChecker.PERMISSION_DENIED) {
Toast.makeText(this, "Permission denied (write)", Toast.LENGTH_SHORT).show();
}
return mBinder;
}
private void postUpdateMessage() {
mHandler.sendEmptyMessage(UPDATE_MESSAGE);
}
private void updateState(int runningCount) {
if (runningCount == 0) {
stopForeground(true);
} else {
startForeground(NOTIFICATION_ID, mNotification);
}
}
public static void startMission(Context context, String url, String location, String name, boolean isAudio, int threads) {
Intent intent = new Intent(context, DownloadManagerService.class);
intent.setAction(Intent.ACTION_RUN);
intent.setData(Uri.parse(url));
intent.putExtra(EXTRA_NAME, name);
intent.putExtra(EXTRA_LOCATION, location);
intent.putExtra(EXTRA_IS_AUDIO, isAudio);
intent.putExtra(EXTRA_THREADS, threads);
context.startService(intent);
}
class MissionListener implements DownloadMission.MissionListener {
@Override
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
long now = System.currentTimeMillis();
long delta = now - mLastTimeStamp;
if (delta > 2000) {
postUpdateMessage();
mLastTimeStamp = now;
}
}
private class MissionListener implements DownloadMission.MissionListener {
@Override
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
long now = System.currentTimeMillis();
long delta = now - mLastTimeStamp;
if (delta > 2000) {
postUpdateMessage();
mLastTimeStamp = now;
}
}
@Override
public void onFinish(DownloadMission downloadMission) {
postUpdateMessage();
notifyMediaScanner(downloadMission);
}
@Override
public void onFinish(DownloadMission downloadMission) {
postUpdateMessage();
notifyMediaScanner(downloadMission);
}
@Override
public void onError(DownloadMission downloadMission, int errCode) {
postUpdateMessage();
}
}
@Override
public void onError(DownloadMission downloadMission, int errCode) {
postUpdateMessage();
}
}
// Wrapper of DownloadManager
public class DMBinder extends Binder {
public DownloadManager getDownloadManager() {
return mManager;
}
public void onMissionAdded(DownloadMission mission) {
mission.addListener(missionListener);
postUpdateMessage();
}
public void onMissionRemoved(DownloadMission mission) {
mission.removeListener(missionListener);
postUpdateMessage();
}
}
// Wrapper of DownloadManager
public class DMBinder extends Binder {
public DownloadManager getDownloadManager() {
return mManager;
}
public void onMissionAdded(DownloadMission mission) {
mission.addListener(missionListener);
postUpdateMessage();
}
public void onMissionRemoved(DownloadMission mission) {
mission.removeListener(missionListener);
postUpdateMessage();
}
}
}

View file

@ -7,6 +7,8 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.support.v4.content.FileProvider;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@ -18,14 +20,13 @@ import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.support.v7.widget.RecyclerView;
import org.schabi.newpipe.R;
import java.io.File;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.schabi.newpipe.R;
import us.shandian.giga.get.DownloadManager;
import us.shandian.giga.get.DownloadMission;
import us.shandian.giga.service.DownloadManagerService;
@ -35,341 +36,340 @@ import us.shandian.giga.util.Utility;
import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHolder>
{
private static final Map<Integer, String> ALGORITHMS = new HashMap<>();
private static final String TAG = "MissionAdapter";
public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHolder> {
private static final Map<Integer, String> ALGORITHMS = new HashMap<>();
private static final String TAG = "MissionAdapter";
static {
ALGORITHMS.put(R.id.md5, "MD5");
ALGORITHMS.put(R.id.sha1, "SHA1");
}
private Context mContext;
private LayoutInflater mInflater;
private DownloadManager mManager;
private DownloadManagerService.DMBinder mBinder;
private int mLayout;
public MissionAdapter(Context context, DownloadManagerService.DMBinder binder, DownloadManager manager, boolean isLinear) {
mContext = context;
mManager = manager;
mBinder = binder;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mLayout = isLinear ? R.layout.mission_item_linear : R.layout.mission_item;
}
static {
ALGORITHMS.put(R.id.md5, "MD5");
ALGORITHMS.put(R.id.sha1, "SHA1");
}
private Context mContext;
private LayoutInflater mInflater;
private DownloadManager mManager;
private DownloadManagerService.DMBinder mBinder;
private int mLayout;
public MissionAdapter(Context context, DownloadManagerService.DMBinder binder, DownloadManager manager, boolean isLinear) {
mContext = context;
mManager = manager;
mBinder = binder;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mLayout = isLinear ? R.layout.mission_item_linear : R.layout.mission_item;
}
@Override
public MissionAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final ViewHolder h = new ViewHolder(mInflater.inflate(mLayout, parent, false));
h.menu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
buildPopup(h);
}
});
@Override
public MissionAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final ViewHolder h = new ViewHolder(mInflater.inflate(mLayout, parent, false));
h.menu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
buildPopup(h);
}
});
/*h.itemView.setOnClickListener(new View.OnClickListener() {
@Override
@Override
public void onClick(View v) {
showDetail(h);
}
});*/
return h;
}
@Override
public void onViewRecycled(MissionAdapter.ViewHolder h) {
super.onViewRecycled(h);
h.mission.removeListener(h.observer);
h.mission = null;
h.observer = null;
h.progress = null;
h.position = -1;
h.lastTimeStamp = -1;
h.lastDone = -1;
h.colorId = 0;
}
return h;
}
@Override
public void onBindViewHolder(MissionAdapter.ViewHolder h, int pos) {
DownloadMission ms = mManager.getMission(pos);
h.mission = ms;
h.position = pos;
Utility.FileType type = Utility.getFileType(ms.name);
h.icon.setImageResource(Utility.getIconForFileType(type));
h.name.setText(ms.name);
h.size.setText(Utility.formatBytes(ms.length));
h.progress = new ProgressDrawable(mContext, Utility.getBackgroundForFileType(type), Utility.getForegroundForFileType(type));
h.bkg.setBackgroundDrawable(h.progress);
h.observer = new MissionObserver(this, h);
ms.addListener(h.observer);
updateProgress(h);
}
@Override
public void onViewRecycled(MissionAdapter.ViewHolder h) {
super.onViewRecycled(h);
h.mission.removeListener(h.observer);
h.mission = null;
h.observer = null;
h.progress = null;
h.position = -1;
h.lastTimeStamp = -1;
h.lastDone = -1;
h.colorId = 0;
}
@Override
public int getItemCount() {
return mManager.getCount();
}
@Override
public void onBindViewHolder(MissionAdapter.ViewHolder h, int pos) {
DownloadMission ms = mManager.getMission(pos);
h.mission = ms;
h.position = pos;
@Override
public long getItemId(int position) {
return position;
}
private void updateProgress(ViewHolder h) {
updateProgress(h, false);
}
private void updateProgress(ViewHolder h, boolean finished) {
if (h.mission == null) return;
long now = System.currentTimeMillis();
if (h.lastTimeStamp == -1) {
h.lastTimeStamp = now;
}
if (h.lastDone == -1) {
h.lastDone = h.mission.done;
}
long deltaTime = now - h.lastTimeStamp;
long deltaDone = h.mission.done - h.lastDone;
if (deltaTime == 0 || deltaTime > 1000 || finished) {
if (h.mission.errCode > 0) {
h.status.setText(R.string.msg_error);
} else {
float progress = (float) h.mission.done / h.mission.length;
h.status.setText(String.format(Locale.US, "%.2f%%", progress * 100));
h.progress.setProgress(progress);
}
}
if (deltaTime > 1000 && deltaDone > 0) {
float speed = (float) deltaDone / deltaTime;
String speedStr = Utility.formatSpeed(speed * 1000);
String sizeStr = Utility.formatBytes(h.mission.length);
h.size.setText(sizeStr + " " + speedStr);
h.lastTimeStamp = now;
h.lastDone = h.mission.done;
}
}
Utility.FileType type = Utility.getFileType(ms.name);
private void buildPopup(final ViewHolder h) {
PopupMenu popup = new PopupMenu(mContext, h.menu);
popup.inflate(R.menu.mission);
Menu menu = popup.getMenu();
MenuItem start = menu.findItem(R.id.start);
MenuItem pause = menu.findItem(R.id.pause);
MenuItem view = menu.findItem(R.id.view);
MenuItem delete = menu.findItem(R.id.delete);
MenuItem checksum = menu.findItem(R.id.checksum);
// Set to false first
start.setVisible(false);
pause.setVisible(false);
view.setVisible(false);
delete.setVisible(false);
checksum.setVisible(false);
if (!h.mission.finished) {
if (!h.mission.running) {
if (h.mission.errCode == -1) {
start.setVisible(true);
}
delete.setVisible(true);
} else {
pause.setVisible(true);
}
} else {
view.setVisible(true);
delete.setVisible(true);
checksum.setVisible(true);
}
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.start:
mManager.resumeMission(h.position);
mBinder.onMissionAdded(mManager.getMission(h.position));
return true;
case R.id.pause:
mManager.pauseMission(h.position);
mBinder.onMissionRemoved(mManager.getMission(h.position));
h.lastTimeStamp = -1;
h.lastDone = -1;
return true;
case R.id.view:
File f = new File(h.mission.location, h.mission.name);
String ext = Utility.getFileExt(h.mission.name);
h.icon.setImageResource(Utility.getIconForFileType(type));
h.name.setText(ms.name);
h.size.setText(Utility.formatBytes(ms.length));
Log.d(TAG, "Viewing file: " + f.getAbsolutePath() + " ext: " + ext);
h.progress = new ProgressDrawable(mContext, Utility.getBackgroundForFileType(type), Utility.getForegroundForFileType(type));
ViewCompat.setBackground(h.bkg, h.progress);
if (ext == null) {
Log.w(TAG, "Can't view file because it has no extension: " +
h.mission.name);
return false;
}
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext.substring(1));
Log.v(TAG, "Mime: " + mime + " package: " + mContext.getApplicationContext().getPackageName() + ".provider");
if (f.exists()) {
viewFileWithFileProvider(f, mime);
} else {
Log.w(TAG, "File doesn't exist");
}
return true;
case R.id.delete:
mManager.deleteMission(h.position);
notifyDataSetChanged();
return true;
case R.id.md5:
case R.id.sha1:
DownloadMission mission = mManager.getMission(h.position);
new ChecksumTask().execute(mission.location + "/" + mission.name, ALGORITHMS.get(id));
return true;
default:
return false;
}
}
});
popup.show();
}
h.observer = new MissionObserver(this, h);
ms.addListener(h.observer);
private void viewFile(File file, String mimetype) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), mimetype);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
}
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.v(TAG, "Starting intent: " + intent);
mContext.startActivity(intent);
}
updateProgress(h);
}
private void viewFileWithFileProvider(File file, String mimetype) {
String ourPackage = mContext.getApplicationContext().getPackageName();
Uri uri = FileProvider.getUriForFile(mContext, ourPackage + ".provider", file);
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimetype);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
}
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.v(TAG, "Starting intent: " + intent);
mContext.startActivity(intent);
}
private class ChecksumTask extends AsyncTask<String, Void, String> {
ProgressDialog prog;
@Override
public int getItemCount() {
return mManager.getCount();
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// Create dialog
prog = new ProgressDialog(mContext);
prog.setCancelable(false);
prog.setMessage(mContext.getString(R.string.msg_wait));
prog.show();
}
@Override
public long getItemId(int position) {
return position;
}
@Override
protected String doInBackground(String... params) {
return Utility.checksum(params[0], params[1]);
}
private void updateProgress(ViewHolder h) {
updateProgress(h, false);
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
prog.dismiss();
Utility.copyToClipboard(mContext, result);
}
}
static class ViewHolder extends RecyclerView.ViewHolder {
public DownloadMission mission;
public int position;
private void updateProgress(ViewHolder h, boolean finished) {
if (h.mission == null) return;
public TextView status;
public ImageView icon;
public TextView name;
public TextView size;
public View bkg;
public ImageView menu;
public ProgressDrawable progress;
public MissionObserver observer;
long now = System.currentTimeMillis();
public long lastTimeStamp = -1;
public long lastDone = -1;
public int colorId;
public ViewHolder(View v) {
super(v);
status = Utility.findViewById(v, R.id.item_status);
icon = Utility.findViewById(v, R.id.item_icon);
name = Utility.findViewById(v, R.id.item_name);
size = Utility.findViewById(v, R.id.item_size);
bkg = Utility.findViewById(v, R.id.item_bkg);
menu = Utility.findViewById(v, R.id.item_more);
}
}
static class MissionObserver implements DownloadMission.MissionListener {
private MissionAdapter mAdapter;
private ViewHolder mHolder;
public MissionObserver(MissionAdapter adapter, ViewHolder holder) {
mAdapter = adapter;
mHolder = holder;
}
@Override
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
mAdapter.updateProgress(mHolder);
}
if (h.lastTimeStamp == -1) {
h.lastTimeStamp = now;
}
@Override
public void onFinish(DownloadMission downloadMission) {
//mAdapter.mManager.deleteMission(mHolder.position);
// TODO Notification
//mAdapter.notifyDataSetChanged();
if (mHolder.mission != null) {
mHolder.size.setText(Utility.formatBytes(mHolder.mission.length));
mAdapter.updateProgress(mHolder, true);
}
}
if (h.lastDone == -1) {
h.lastDone = h.mission.done;
}
@Override
public void onError(DownloadMission downloadMission, int errCode) {
mAdapter.updateProgress(mHolder);
}
}
long deltaTime = now - h.lastTimeStamp;
long deltaDone = h.mission.done - h.lastDone;
if (deltaTime == 0 || deltaTime > 1000 || finished) {
if (h.mission.errCode > 0) {
h.status.setText(R.string.msg_error);
} else {
float progress = (float) h.mission.done / h.mission.length;
h.status.setText(String.format(Locale.US, "%.2f%%", progress * 100));
h.progress.setProgress(progress);
}
}
if (deltaTime > 1000 && deltaDone > 0) {
float speed = (float) deltaDone / deltaTime;
String speedStr = Utility.formatSpeed(speed * 1000);
String sizeStr = Utility.formatBytes(h.mission.length);
h.size.setText(sizeStr + " " + speedStr);
h.lastTimeStamp = now;
h.lastDone = h.mission.done;
}
}
private void buildPopup(final ViewHolder h) {
PopupMenu popup = new PopupMenu(mContext, h.menu);
popup.inflate(R.menu.mission);
Menu menu = popup.getMenu();
MenuItem start = menu.findItem(R.id.start);
MenuItem pause = menu.findItem(R.id.pause);
MenuItem view = menu.findItem(R.id.view);
MenuItem delete = menu.findItem(R.id.delete);
MenuItem checksum = menu.findItem(R.id.checksum);
// Set to false first
start.setVisible(false);
pause.setVisible(false);
view.setVisible(false);
delete.setVisible(false);
checksum.setVisible(false);
if (!h.mission.finished) {
if (!h.mission.running) {
if (h.mission.errCode == -1) {
start.setVisible(true);
}
delete.setVisible(true);
} else {
pause.setVisible(true);
}
} else {
view.setVisible(true);
delete.setVisible(true);
checksum.setVisible(true);
}
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.start:
mManager.resumeMission(h.position);
mBinder.onMissionAdded(mManager.getMission(h.position));
return true;
case R.id.pause:
mManager.pauseMission(h.position);
mBinder.onMissionRemoved(mManager.getMission(h.position));
h.lastTimeStamp = -1;
h.lastDone = -1;
return true;
case R.id.view:
File f = new File(h.mission.location, h.mission.name);
String ext = Utility.getFileExt(h.mission.name);
Log.d(TAG, "Viewing file: " + f.getAbsolutePath() + " ext: " + ext);
if (ext == null) {
Log.w(TAG, "Can't view file because it has no extension: " +
h.mission.name);
return false;
}
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext.substring(1));
Log.v(TAG, "Mime: " + mime + " package: " + mContext.getApplicationContext().getPackageName() + ".provider");
if (f.exists()) {
viewFileWithFileProvider(f, mime);
} else {
Log.w(TAG, "File doesn't exist");
}
return true;
case R.id.delete:
mManager.deleteMission(h.position);
notifyDataSetChanged();
return true;
case R.id.md5:
case R.id.sha1:
DownloadMission mission = mManager.getMission(h.position);
new ChecksumTask().execute(mission.location + "/" + mission.name, ALGORITHMS.get(id));
return true;
default:
return false;
}
}
});
popup.show();
}
private void viewFile(File file, String mimetype) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), mimetype);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
}
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.v(TAG, "Starting intent: " + intent);
mContext.startActivity(intent);
}
private void viewFileWithFileProvider(File file, String mimetype) {
String ourPackage = mContext.getApplicationContext().getPackageName();
Uri uri = FileProvider.getUriForFile(mContext, ourPackage + ".provider", file);
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimetype);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
}
//mContext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.v(TAG, "Starting intent: " + intent);
mContext.startActivity(intent);
}
static class ViewHolder extends RecyclerView.ViewHolder {
public DownloadMission mission;
public int position;
public TextView status;
public ImageView icon;
public TextView name;
public TextView size;
public View bkg;
public ImageView menu;
public ProgressDrawable progress;
public MissionObserver observer;
public long lastTimeStamp = -1;
public long lastDone = -1;
public int colorId;
public ViewHolder(View v) {
super(v);
status = (TextView) v.findViewById(R.id.item_status);
icon = (ImageView) v.findViewById(R.id.item_icon);
name = (TextView) v.findViewById(R.id.item_name);
size = (TextView) v.findViewById(R.id.item_size);
bkg = v.findViewById(R.id.item_bkg);
menu = (ImageView) v.findViewById(R.id.item_more);
}
}
static class MissionObserver implements DownloadMission.MissionListener {
private MissionAdapter mAdapter;
private ViewHolder mHolder;
public MissionObserver(MissionAdapter adapter, ViewHolder holder) {
mAdapter = adapter;
mHolder = holder;
}
@Override
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
mAdapter.updateProgress(mHolder);
}
@Override
public void onFinish(DownloadMission downloadMission) {
//mAdapter.mManager.deleteMission(mHolder.position);
// TODO Notification
//mAdapter.notifyDataSetChanged();
if (mHolder.mission != null) {
mHolder.size.setText(Utility.formatBytes(mHolder.mission.length));
mAdapter.updateProgress(mHolder, true);
}
}
@Override
public void onError(DownloadMission downloadMission, int errCode) {
mAdapter.updateProgress(mHolder);
}
}
private class ChecksumTask extends AsyncTask<String, Void, String> {
ProgressDialog prog;
@Override
protected void onPreExecute() {
super.onPreExecute();
// Create dialog
prog = new ProgressDialog(mContext);
prog.setCancelable(false);
prog.setMessage(mContext.getString(R.string.msg_wait));
prog.show();
}
@Override
protected String doInBackground(String... params) {
return Utility.checksum(params[0], params[1]);
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
prog.dismiss();
Utility.copyToClipboard(mContext, result);
}
}
}

View file

@ -6,53 +6,55 @@ import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
public class ProgressDrawable extends Drawable
{
private float mProgress;
private int mBackgroundColor, mForegroundColor;
public ProgressDrawable(Context context, int background, int foreground) {
this(context.getResources().getColor(background), context.getResources().getColor(foreground));
}
public ProgressDrawable(int background, int foreground) {
mBackgroundColor = background;
mForegroundColor = foreground;
}
public void setProgress(float progress) {
mProgress = progress;
invalidateSelf();
}
public class ProgressDrawable extends Drawable {
private float mProgress;
private int mBackgroundColor, mForegroundColor;
@Override
public void draw(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
Paint paint = new Paint();
paint.setColor(mBackgroundColor);
canvas.drawRect(0, 0, width, height, paint);
paint.setColor(mForegroundColor);
canvas.drawRect(0, 0, (int) (mProgress * width), height, paint);
}
public ProgressDrawable(Context context, @ColorRes int background, @ColorRes int foreground) {
this(ContextCompat.getColor(context, background), ContextCompat.getColor(context, foreground));
}
@Override
public void setAlpha(int alpha) {
// Unsupported
}
public ProgressDrawable(int background, int foreground) {
mBackgroundColor = background;
mForegroundColor = foreground;
}
@Override
public void setColorFilter(ColorFilter filter) {
// Unsupported
}
public void setProgress(float progress) {
mProgress = progress;
invalidateSelf();
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
@Override
public void draw(@NonNull Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
Paint paint = new Paint();
paint.setColor(mBackgroundColor);
canvas.drawRect(0, 0, width, height, paint);
paint.setColor(mForegroundColor);
canvas.drawRect(0, 0, (int) (mProgress * width), height, paint);
}
@Override
public void setAlpha(int alpha) {
// Unsupported
}
@Override
public void setColorFilter(ColorFilter filter) {
// Unsupported
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
}

View file

@ -1,26 +1,23 @@
package us.shandian.giga.ui.common;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import org.schabi.newpipe.R;
import us.shandian.giga.util.Utility;
public abstract class ToolbarActivity extends ActionBarActivity
{
protected Toolbar mToolbar;
public abstract class ToolbarActivity extends ActionBarActivity {
protected Toolbar mToolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResource());
mToolbar = Utility.findViewById(this, R.id.toolbar);
setSupportActionBar(mToolbar);
}
protected abstract int getLayoutResource();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResource());
mToolbar = (Toolbar) this.findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
}
protected abstract int getLayoutResource();
}

View file

@ -3,11 +3,10 @@ package us.shandian.giga.ui.fragment;
import us.shandian.giga.get.DownloadManager;
import us.shandian.giga.service.DownloadManagerService;
public class AllMissionsFragment extends MissionsFragment
{
public class AllMissionsFragment extends MissionsFragment {
@Override
protected DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder) {
return binder.getDownloadManager();
}
@Override
protected DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder) {
return binder.getDownloadManager();
}
}

View file

@ -23,126 +23,128 @@ import org.schabi.newpipe.R;
import us.shandian.giga.get.DownloadManager;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.ui.adapter.MissionAdapter;
import us.shandian.giga.util.Utility;
public abstract class MissionsFragment extends Fragment
{
private DownloadManager mManager;
private DownloadManagerService.DMBinder mBinder;
private SharedPreferences mPrefs;
private boolean mLinear;
private MenuItem mSwitch;
private RecyclerView mList;
private MissionAdapter mAdapter;
private GridLayoutManager mGridManager;
private LinearLayoutManager mLinearManager;
private Context mActivity;
private ServiceConnection mConnection = new ServiceConnection() {
public abstract class MissionsFragment extends Fragment {
private DownloadManager mManager;
private DownloadManagerService.DMBinder mBinder;
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mBinder = (DownloadManagerService.DMBinder) binder;
mManager = setupDownloadManager(mBinder);
updateList();
}
private SharedPreferences mPrefs;
private boolean mLinear;
private MenuItem mSwitch;
@Override
public void onServiceDisconnected(ComponentName name) {
// What to do?
}
private RecyclerView mList;
private MissionAdapter mAdapter;
private GridLayoutManager mGridManager;
private LinearLayoutManager mLinearManager;
private Context mActivity;
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.missions, container, false);
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mBinder = (DownloadManagerService.DMBinder) binder;
mManager = setupDownloadManager(mBinder);
updateList();
}
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
mLinear = mPrefs.getBoolean("linear", false);
@Override
public void onServiceDisconnected(ComponentName name) {
// What to do?
}
// Bind the service
Intent i = new Intent();
i.setClass(getActivity(), DownloadManagerService.class);
getActivity().bindService(i, mConnection, Context.BIND_AUTO_CREATE);
// Views
mList = Utility.findViewById(v, R.id.mission_recycler);
// Init
mGridManager = new GridLayoutManager(getActivity(), 2);
mLinearManager = new LinearLayoutManager(getActivity());
mList.setLayoutManager(mGridManager);
setHasOptionsMenu(true);
return v;
}
/** Added in API level 23. */
};
@Override
public void onAttach(Context activity) {
super.onAttach(activity);
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.missions, container, false);
// Bug: in api< 23 this is never called
// so mActivity=null
// so app crashes with nullpointer exception
mActivity = activity;
}
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
mLinear = mPrefs.getBoolean("linear", false);
/** deprecated in API level 23,
* but must remain to allow compatibility with api<23 */
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Bind the service
Intent i = new Intent();
i.setClass(getActivity(), DownloadManagerService.class);
getActivity().bindService(i, mConnection, Context.BIND_AUTO_CREATE);
mActivity = activity;
}
// Views
mList = (RecyclerView) v.findViewById(R.id.mission_recycler);
@Override
public void onDestroyView() {
super.onDestroyView();
getActivity().unbindService(mConnection);
}
// Init
mGridManager = new GridLayoutManager(getActivity(), 2);
mLinearManager = new LinearLayoutManager(getActivity());
mList.setLayoutManager(mGridManager);
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
setHasOptionsMenu(true);
return v;
}
/**
* Added in API level 23.
*/
@Override
public void onAttach(Context activity) {
super.onAttach(activity);
// Bug: in api< 23 this is never called
// so mActivity=null
// so app crashes with nullpointer exception
mActivity = activity;
}
/**
* deprecated in API level 23,
* but must remain to allow compatibility with api<23
*/
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
@Override
public void onDestroyView() {
super.onDestroyView();
getActivity().unbindService(mConnection);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
/*switch (item.getItemId()) {
case R.id.switch_mode:
case R.id.switch_mode:
mLinear = !mLinear;
updateList();
return true;
default:
return super.onOptionsItemSelected(item);
}*/
}
}
public void notifyChange() {
mAdapter.notifyDataSetChanged();
}
private void updateList() {
mAdapter = new MissionAdapter(mActivity, mBinder, mManager, mLinear);
if (mLinear) {
mList.setLayoutManager(mLinearManager);
} else {
mList.setLayoutManager(mGridManager);
}
mList.setAdapter(mAdapter);
if (mSwitch != null) {
mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list);
}
mPrefs.edit().putBoolean("linear", mLinear).commit();
}
protected abstract DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder);
public void notifyChange() {
mAdapter.notifyDataSetChanged();
}
private void updateList() {
mAdapter = new MissionAdapter(mActivity, mBinder, mManager, mLinear);
if (mLinear) {
mList.setLayoutManager(mLinearManager);
} else {
mList.setLayoutManager(mGridManager);
}
mList.setAdapter(mAdapter);
if (mSwitch != null) {
mSwitch.setIcon(mLinear ? R.drawable.grid : R.drawable.list);
}
mPrefs.edit().putBoolean("linear", mLinear).commit();
}
protected abstract DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder);
}

View file

@ -9,77 +9,76 @@ import java.io.File;
import java.io.PrintWriter;
//todo: replace this by using the internal crash handler of newpipe
public class CrashHandler implements Thread.UncaughtExceptionHandler
{
public static final String CRASH_DIR = Environment.getExternalStorageDirectory().getPath() + "/GigaCrash/";
public static final String CRASH_LOG = CRASH_DIR + "last_crash.log";
public static final String CRASH_TAG = CRASH_DIR + ".crashed";
public class CrashHandler implements Thread.UncaughtExceptionHandler {
public static final String CRASH_DIR = Environment.getExternalStorageDirectory().getPath() + "/GigaCrash/";
public static final String CRASH_LOG = CRASH_DIR + "last_crash.log";
public static final String CRASH_TAG = CRASH_DIR + ".crashed";
private static String ANDROID = Build.VERSION.RELEASE;
private static String MODEL = Build.MODEL;
private static String MANUFACTURER = Build.MANUFACTURER;
private static String ANDROID = Build.VERSION.RELEASE;
private static String MODEL = Build.MODEL;
private static String MANUFACTURER = Build.MANUFACTURER;
public static String VERSION = "Unknown";
public static String VERSION = "Unknown";
private Thread.UncaughtExceptionHandler mPrevious;
private Thread.UncaughtExceptionHandler mPrevious;
public static void init(Context context) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
VERSION = info.versionName + info.versionCode;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void init(Context context) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
VERSION = info.versionName + info.versionCode;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void register() {
new CrashHandler();
}
public static void register() {
new CrashHandler();
}
private CrashHandler() {
mPrevious = Thread.currentThread().getUncaughtExceptionHandler();
Thread.currentThread().setUncaughtExceptionHandler(this);
}
private CrashHandler() {
mPrevious = Thread.currentThread().getUncaughtExceptionHandler();
Thread.currentThread().setUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
File f = new File(CRASH_LOG);
if (f.exists()) {
f.delete();
} else {
try {
new File(CRASH_DIR).mkdirs();
f.createNewFile();
} catch (Exception e) {
return;
}
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
File f = new File(CRASH_LOG);
if (f.exists()) {
f.delete();
} else {
try {
new File(CRASH_DIR).mkdirs();
f.createNewFile();
} catch (Exception e) {
return;
}
}
PrintWriter p;
try {
p = new PrintWriter(f);
} catch (Exception e) {
return;
}
PrintWriter p;
try {
p = new PrintWriter(f);
} catch (Exception e) {
return;
}
p.write("Android Version: " + ANDROID + "\n");
p.write("Device Model: " + MODEL + "\n");
p.write("Device Manufacturer: " + MANUFACTURER + "\n");
p.write("App Version: " + VERSION + "\n");
p.write("*********************\n");
throwable.printStackTrace(p);
p.write("Android Version: " + ANDROID + "\n");
p.write("Device Model: " + MODEL + "\n");
p.write("Device Manufacturer: " + MANUFACTURER + "\n");
p.write("App Version: " + VERSION + "\n");
p.write("*********************\n");
throwable.printStackTrace(p);
p.close();
p.close();
try {
new File(CRASH_TAG).createNewFile();
} catch (Exception e) {
return;
}
try {
new File(CRASH_TAG).createNewFile();
} catch (Exception e) {
return;
}
if (mPrevious != null) {
mPrevious.uncaughtException(thread, throwable);
}
}
if (mPrevious != null) {
mPrevious.uncaughtException(thread, throwable);
}
}
}

View file

@ -1,256 +1,221 @@
package us.shandian.giga.util;
import android.app.Activity;
import android.content.ClipboardManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
import android.widget.Toast;
import org.schabi.newpipe.R;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.R;
public class Utility {
import com.nononsenseapps.filepicker.FilePickerActivity;
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
public enum FileType {
VIDEO,
MUSIC,
UNKNOWN
}
public class Utility
{
public static enum FileType {
VIDEO,
MUSIC,
UNKNOWN
}
public static String formatBytes(long bytes) {
if (bytes < 1024) {
return String.format("%d B", bytes);
} else if (bytes < 1024 * 1024) {
return String.format("%.2f kB", (float) bytes / 1024);
} else if (bytes < 1024 * 1024 * 1024) {
return String.format("%.2f MB", (float) bytes / 1024 / 1024);
} else {
return String.format("%.2f GB", (float) bytes / 1024 / 1024 / 1024);
}
}
public static String formatSpeed(float speed) {
if (speed < 1024) {
return String.format("%.2f B/s", speed);
} else if (speed < 1024 * 1024) {
return String.format("%.2f kB/s", speed / 1024);
} else if (speed < 1024 * 1024 * 1024) {
return String.format("%.2f MB/s", speed / 1024 / 1024);
} else {
return String.format("%.2f GB/s", speed / 1024 / 1024 / 1024);
}
}
public static void writeToFile(String fileName, String content) {
try {
writeToFile(fileName, content.getBytes("UTF-8"));
} catch (Exception e) {
}
}
public static void writeToFile(String fileName, byte[] content) {
File f = new File(fileName);
if (!f.exists()) {
try {
f.createNewFile();
} catch (Exception e) {
}
}
try {
FileOutputStream opt = new FileOutputStream(f, false);
opt.write(content, 0, content.length);
opt.close();
} catch (Exception e) {
}
}
public static String readFromFile(String file) {
try {
File f = new File(file);
if (!f.exists() || !f.canRead()) {
return null;
}
BufferedInputStream ipt = new BufferedInputStream(new FileInputStream(f));
byte[] buf = new byte[512];
StringBuilder sb = new StringBuilder();
while (ipt.available() > 0) {
int len = ipt.read(buf, 0, 512);
sb.append(new String(buf, 0, len, "UTF-8"));
}
ipt.close();
return sb.toString();
} catch (Exception e) {
return null;
}
}
public static <T> T findViewById(View v, int id) {
return (T) v.findViewById(id);
}
public static <T> T findViewById(Activity activity, int id) {
return (T) activity.findViewById(id);
}
public static String getFileExt(String url) {
if (url.indexOf("?")>-1) {
url = url.substring(0,url.indexOf("?"));
}
if (url.lastIndexOf(".") == -1) {
return null;
} else {
String ext = url.substring(url.lastIndexOf(".") );
if (ext.indexOf("%")>-1) {
ext = ext.substring(0,ext.indexOf("%"));
}
if (ext.indexOf("/")>-1) {
ext = ext.substring(0,ext.indexOf("/"));
}
return ext.toLowerCase();
public static String formatBytes(long bytes) {
if (bytes < 1024) {
return String.format("%d B", bytes);
} else if (bytes < 1024 * 1024) {
return String.format("%.2f kB", (float) bytes / 1024);
} else if (bytes < 1024 * 1024 * 1024) {
return String.format("%.2f MB", (float) bytes / 1024 / 1024);
} else {
return String.format("%.2f GB", (float) bytes / 1024 / 1024 / 1024);
}
}
}
}
public static String formatSpeed(float speed) {
if (speed < 1024) {
return String.format("%.2f B/s", speed);
} else if (speed < 1024 * 1024) {
return String.format("%.2f kB/s", speed / 1024);
} else if (speed < 1024 * 1024 * 1024) {
return String.format("%.2f MB/s", speed / 1024 / 1024);
} else {
return String.format("%.2f GB/s", speed / 1024 / 1024 / 1024);
}
}
public static FileType getFileType(String file) {
if (file.endsWith(".mp3") || file.endsWith(".wav") || file.endsWith(".flac") || file.endsWith(".m4a")) {
return FileType.MUSIC;
} else if (file.endsWith(".mp4") || file.endsWith(".mpeg") || file.endsWith(".rm") || file.endsWith(".rmvb")
|| file.endsWith(".flv") || file.endsWith(".webp") || file.endsWith(".webm")) {
return FileType.VIDEO;
} else {
return FileType.UNKNOWN;
}
}
public static void writeToFile(String fileName, String content) {
try {
writeToFile(fileName, content.getBytes("UTF-8"));
} catch (Exception e) {
public static Boolean isMusicFile(String file)
{
return Utility.getFileType(file) == FileType.MUSIC;
}
}
}
public static Boolean isVideoFile(String file)
{
return Utility.getFileType(file) == FileType.VIDEO;
}
public static int getBackgroundForFileType(FileType type) {
switch (type) {
case MUSIC:
return R.color.audio_left_to_load_color;
case VIDEO:
return R.color.video_left_to_load_color;
default:
return R.color.gray;
}
}
public static int getForegroundForFileType(FileType type) {
switch (type) {
case MUSIC:
return R.color.audio_already_load_color;
case VIDEO:
return R.color.video_already_load_color;
default:
return R.color.gray;
}
}
public static void writeToFile(String fileName, byte[] content) {
File f = new File(fileName);
public static int getIconForFileType(FileType type) {
switch(type) {
case MUSIC:
return R.drawable.music;
case VIDEO:
return R.drawable.video;
default:
return R.drawable.video;
}
}
if (!f.exists()) {
try {
f.createNewFile();
} catch (Exception e) {
public static boolean isDirectoryAvailble(String path) {
File dir = new File(path);
return dir.exists() && dir.isDirectory();
}
}
}
public static boolean isDownloadDirectoryAvailble(Context context) {
return isDirectoryAvailble(NewPipeSettings.getVideoDownloadPath(context));
}
try {
FileOutputStream opt = new FileOutputStream(f, false);
opt.write(content, 0, content.length);
opt.close();
} catch (Exception e) {
public static void showDirectoryChooser(Activity activity) {
Intent i = new Intent(activity, FilePickerActivity.class);
i.setAction(Intent.ACTION_GET_CONTENT);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true);
i.putExtra(FilePickerActivity.EXTRA_MODE, AbstractFilePickerFragment.MODE_DIR);
activity.startActivityForResult(i, 233);
}
public static void copyToClipboard(Context context, String str) {
ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(ClipData.newPlainText("text", str));
Toast.makeText(context, R.string.msg_copied, Toast.LENGTH_SHORT).show();
}
public static String checksum(String path, String algorithm) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
FileInputStream i = null;
try {
i = new FileInputStream(path);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
byte[] buf = new byte[1024];
int len = 0;
try {
while ((len = i.read(buf)) != -1) {
md.update(buf, 0, len);
}
} catch (IOException e) {
}
byte[] digest = md.digest();
// HEX
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
}
}
public static String readFromFile(String file) {
try {
File f = new File(file);
if (!f.exists() || !f.canRead()) {
return null;
}
BufferedInputStream ipt = new BufferedInputStream(new FileInputStream(f));
byte[] buf = new byte[512];
StringBuilder sb = new StringBuilder();
while (ipt.available() > 0) {
int len = ipt.read(buf, 0, 512);
sb.append(new String(buf, 0, len, "UTF-8"));
}
ipt.close();
return sb.toString();
} catch (Exception e) {
return null;
}
}
@Nullable
public static String getFileExt(String url) {
int index;
if ((index = url.indexOf("?")) > -1) {
url = url.substring(0, index);
}
index = url.lastIndexOf(".");
if (index == -1) {
return null;
} else {
String ext = url.substring(index);
if ((index = ext.indexOf("%")) > -1) {
ext = ext.substring(0, index);
}
if ((index = ext.indexOf("/")) > -1) {
ext = ext.substring(0, index);
}
return ext.toLowerCase();
}
}
public static FileType getFileType(String file) {
if (file.endsWith(".mp3") || file.endsWith(".wav") || file.endsWith(".flac") || file.endsWith(".m4a")) {
return FileType.MUSIC;
} else if (file.endsWith(".mp4") || file.endsWith(".mpeg") || file.endsWith(".rm") || file.endsWith(".rmvb")
|| file.endsWith(".flv") || file.endsWith(".webp") || file.endsWith(".webm")) {
return FileType.VIDEO;
} else {
return FileType.UNKNOWN;
}
}
@ColorRes
public static int getBackgroundForFileType(FileType type) {
switch (type) {
case MUSIC:
return R.color.audio_left_to_load_color;
case VIDEO:
return R.color.video_left_to_load_color;
default:
return R.color.gray;
}
}
@ColorRes
public static int getForegroundForFileType(FileType type) {
switch (type) {
case MUSIC:
return R.color.audio_already_load_color;
case VIDEO:
return R.color.video_already_load_color;
default:
return R.color.gray;
}
}
@DrawableRes
public static int getIconForFileType(FileType type) {
switch (type) {
case MUSIC:
return R.drawable.music;
case VIDEO:
return R.drawable.video;
default:
return R.drawable.video;
}
}
public static void copyToClipboard(Context context, String str) {
ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setPrimaryClip(ClipData.newPlainText("text", str));
Toast.makeText(context, R.string.msg_copied, Toast.LENGTH_SHORT).show();
}
public static String checksum(String path, String algorithm) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
FileInputStream i = null;
try {
i = new FileInputStream(path);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
byte[] buf = new byte[1024];
int len = 0;
try {
while ((len = i.read(buf)) != -1) {
md.update(buf, 0, len);
}
} catch (IOException e) {
}
byte[] digest = md.digest();
// HEX
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
}