Improve DownloadManager and -Service
* Fix permission at some places * Fix access problem for downloaded files with external player * Store finished Downloads * Remove binding to DownloadService just to download a file * Javadoc * Code improvements
This commit is contained in:
parent
53ff58daa3
commit
ea76f1d6e2
23 changed files with 1066 additions and 323 deletions
|
|
@ -0,0 +1,36 @@
|
|||
package us.shandian.giga.get;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides access to the storage of {@link DownloadMission}s
|
||||
*/
|
||||
public interface DownloadDataSource {
|
||||
|
||||
/**
|
||||
* Load all missions
|
||||
* @return a list of download missions
|
||||
*/
|
||||
List<DownloadMission> loadMissions();
|
||||
|
||||
/**
|
||||
* Add a downlaod mission to the storage
|
||||
* @param downloadMission the download mission to add
|
||||
* @return the identifier of the mission
|
||||
*/
|
||||
void addMission(DownloadMission downloadMission);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void updateMission(DownloadMission downloadMission);
|
||||
|
||||
|
||||
/**
|
||||
* Delete a download mission
|
||||
* @param downloadMission the mission to delete
|
||||
*/
|
||||
void deleteMission(DownloadMission downloadMission);
|
||||
}
|
||||
|
|
@ -3,12 +3,46 @@ package us.shandian.giga.get;
|
|||
public interface DownloadManager
|
||||
{
|
||||
int BLOCK_SIZE = 512 * 1024;
|
||||
|
||||
int startMission(String url, String name, boolean isAudio, int threads);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void pauseMission(int id);
|
||||
|
||||
/**
|
||||
* Deletes the mission from the downloaded list but keeps the downloaded file.
|
||||
* @param id The mission identifier
|
||||
*/
|
||||
void deleteMission(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
|
||||
*/
|
||||
DownloadMission getMission(int id);
|
||||
|
||||
/**
|
||||
* Get the number of download missions.
|
||||
* @return the number of download missions.
|
||||
*/
|
||||
int getCount();
|
||||
String getLocation();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,21 @@
|
|||
package us.shandian.giga.get;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import us.shandian.giga.util.Utility;
|
||||
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||
|
|
@ -19,33 +23,48 @@ import static org.schabi.newpipe.BuildConfig.DEBUG;
|
|||
public class DownloadManagerImpl implements DownloadManager
|
||||
{
|
||||
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
|
||||
|
||||
private Context mContext;
|
||||
private String mLocation;
|
||||
protected ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
|
||||
|
||||
public DownloadManagerImpl(Context context, String location) {
|
||||
mContext = context;
|
||||
mLocation = location;
|
||||
loadMissions();
|
||||
private final DownloadDataSource mDownloadDataSource;
|
||||
|
||||
private final ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
|
||||
|
||||
/**
|
||||
* Create a new instance
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int startMission(String url, String name, boolean isAudio, int threads) {
|
||||
DownloadMission mission = new DownloadMission();
|
||||
mission.url = url;
|
||||
mission.name = name;
|
||||
if(isAudio) {
|
||||
mission.location = NewPipeSettings.getAudioDownloadPath(mContext);
|
||||
} else {
|
||||
mission.location = NewPipeSettings.getVideoDownloadPath(mContext);
|
||||
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 (?)
|
||||
try {
|
||||
name = generateUniqueName(location, name);
|
||||
}catch (Exception e) {
|
||||
Log.e(TAG, "Unable to generate unique name", e);
|
||||
name = System.currentTimeMillis() + name ;
|
||||
Log.i(TAG, "Using " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DownloadMission mission = new DownloadMission(name, url, location);
|
||||
mission.timestamp = System.currentTimeMillis();
|
||||
mission.threadCount = threads;
|
||||
new Initializer(mContext, mission).start();
|
||||
mission.addListener(new MissionListener(mission));
|
||||
new Initializer(mission).start();
|
||||
return insertMission(mission);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void resumeMission(int i) {
|
||||
DownloadMission d = getMission(i);
|
||||
|
|
@ -53,7 +72,7 @@ public class DownloadManagerImpl implements DownloadManager
|
|||
d.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void pauseMission(int i) {
|
||||
DownloadMission d = getMission(i);
|
||||
|
|
@ -61,55 +80,94 @@ public class DownloadManagerImpl implements DownloadManager
|
|||
d.pause();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void deleteMission(int i) {
|
||||
getMission(i).delete();
|
||||
DownloadMission mission = getMission(i);
|
||||
if(mission.finished) {
|
||||
mDownloadDataSource.deleteMission(mission);
|
||||
}
|
||||
mission.delete();
|
||||
mMissions.remove(i);
|
||||
}
|
||||
|
||||
private void loadMissions() {
|
||||
File f = new File(mLocation);
|
||||
|
||||
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) {
|
||||
finishedMissions = new ArrayList<>();
|
||||
}
|
||||
// Ensure its sorted
|
||||
Collections.sort(finishedMissions, new Comparator<DownloadMission>() {
|
||||
@Override
|
||||
public int compare(DownloadMission o1, DownloadMission o2) {
|
||||
return (int) (o1.timestamp - o2.timestamp);
|
||||
}
|
||||
});
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadMissions(String location) {
|
||||
|
||||
File f = new File(location);
|
||||
|
||||
if (f.exists() && f.isDirectory()) {
|
||||
File[] subs = f.listFiles();
|
||||
|
||||
|
||||
if(subs == null) {
|
||||
Log.e(TAG, "listFiles() returned null");
|
||||
return;
|
||||
}
|
||||
|
||||
for (File sub : subs) {
|
||||
if (sub.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sub.getName().endsWith(".giga")) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
DownloadMission mis = new Gson().fromJson(str, DownloadMission.class);
|
||||
|
||||
|
||||
if (mis.finished) {
|
||||
sub.delete();
|
||||
if(!sub.delete()) {
|
||||
Log.w(TAG, "Unable to delete .giga file: " + sub.getPath());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
mis.running = false;
|
||||
mis.recovered = true;
|
||||
insertMission(mis);
|
||||
}
|
||||
} else if (!sub.getName().startsWith(".") && !new File(sub.getPath() + ".giga").exists()) {
|
||||
// Add a dummy mission for downloaded files
|
||||
DownloadMission mis = new DownloadMission();
|
||||
mis.length = sub.length();
|
||||
mis.done = mis.length;
|
||||
mis.finished = true;
|
||||
mis.running = false;
|
||||
mis.name = sub.getName();
|
||||
mis.location = mLocation;
|
||||
mis.timestamp = sub.lastModified();
|
||||
insertMission(mis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -144,18 +202,81 @@ public class DownloadManagerImpl implements DownloadManager
|
|||
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocation() {
|
||||
return mLocation;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
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 Context context;
|
||||
private DownloadMission mission;
|
||||
|
||||
public Initializer(Context context, DownloadMission mission) {
|
||||
this.context = context;
|
||||
public Initializer(DownloadMission mission) {
|
||||
this.mission = mission;
|
||||
}
|
||||
|
||||
|
|
@ -217,4 +338,30 @@ public class DownloadManagerImpl implements DownloadManager
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package us.shandian.giga.get;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
|
@ -10,39 +9,63 @@ import com.google.gson.Gson;
|
|||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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 interface MissionListener {
|
||||
HashMap<MissionListener, Handler> handlerStore = new HashMap<>();
|
||||
|
||||
void onProgressUpdate(long done, long total);
|
||||
void onFinish();
|
||||
void onError(int errCode);
|
||||
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 String name = "";
|
||||
public String url = "";
|
||||
public String location = "";
|
||||
|
||||
/**
|
||||
* The filename
|
||||
*/
|
||||
public String name;
|
||||
|
||||
/**
|
||||
* The url of the file to download
|
||||
*/
|
||||
public String url;
|
||||
|
||||
/**
|
||||
* The directory to store the download
|
||||
*/
|
||||
public String location;
|
||||
|
||||
/**
|
||||
* Number of blocks the size of {@link DownloadManager#BLOCK_SIZE}
|
||||
*/
|
||||
public long blocks;
|
||||
|
||||
/**
|
||||
* Number of bytes
|
||||
*/
|
||||
public long length;
|
||||
|
||||
/**
|
||||
* Number of bytes downloaded
|
||||
*/
|
||||
public long done;
|
||||
public int threadCount = 3;
|
||||
public int finishCount;
|
||||
public List<Long> threadPositions = new ArrayList<Long>();
|
||||
public Map<Long, Boolean> blockState = new HashMap<Long, Boolean>();
|
||||
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;
|
||||
|
|
@ -53,23 +76,65 @@ public class DownloadMission
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPosition(int id, long position) {
|
||||
threadPositions.set(id, position);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
public long getPosition(int id) {
|
||||
return threadPositions.get(id);
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
|
@ -95,13 +160,16 @@ public class DownloadMission
|
|||
MissionListener.handlerStore.get(listener).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onProgressUpdate(done, length);
|
||||
listener.onProgressUpdate(DownloadMission.this, done, length);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called by a download thread when it finished.
|
||||
*/
|
||||
public synchronized void notifyFinished() {
|
||||
if (errCode > 0) return;
|
||||
|
||||
|
|
@ -111,7 +179,10 @@ public class DownloadMission
|
|||
onFinish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when all parts are downloaded
|
||||
*/
|
||||
private void onFinish() {
|
||||
if (errCode > 0) return;
|
||||
|
||||
|
|
@ -130,7 +201,7 @@ public class DownloadMission
|
|||
MissionListener.handlerStore.get(listener).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onFinish();
|
||||
listener.onFinish(DownloadMission.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -147,7 +218,7 @@ public class DownloadMission
|
|||
MissionListener.handlerStore.get(listener).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onError(errCode);
|
||||
listener.onError(DownloadMission.this, errCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -169,7 +240,10 @@ public class DownloadMission
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start downloading with multiple threads.
|
||||
*/
|
||||
public void start() {
|
||||
if (!running && !finished) {
|
||||
running = true;
|
||||
|
|
@ -200,12 +274,19 @@ public class DownloadMission
|
|||
// if (err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the file and the meta file
|
||||
*/
|
||||
public void delete() {
|
||||
deleteThisFromFile();
|
||||
new File(location + "/" + name).delete();
|
||||
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;
|
||||
|
|
@ -218,14 +299,30 @@ public class DownloadMission
|
|||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write this {@link DownloadMission} to the meta file.
|
||||
*/
|
||||
private void doWriteThisToFile() {
|
||||
synchronized (blockState) {
|
||||
Utility.writeToFile(location + "/" + name + ".giga", new Gson().toJson(this));
|
||||
Utility.writeToFile(getMetaFilename(), new Gson().toJson(this));
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteThisFromFile() {
|
||||
new File(location + "/" + name + ".giga").delete();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,19 @@ import java.net.URL;
|
|||
|
||||
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 DownloadMission mMission;
|
||||
private int mId;
|
||||
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;
|
||||
}
|
||||
|
|
@ -86,7 +91,7 @@ public class DownloadRunnable implements Runnable
|
|||
Log.d(TAG, mId + ":Content-Length=" + conn.getContentLength() + " Code:" + conn.getResponseCode());
|
||||
}
|
||||
|
||||
// A server may be ignoring the range requet
|
||||
// A server may be ignoring the range request
|
||||
if (conn.getResponseCode() != 206) {
|
||||
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
|
||||
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
|
||||
|
|
@ -131,7 +136,7 @@ public class DownloadRunnable implements Runnable
|
|||
notifyProgress(-total);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, mId + ":position " + position + " retrying");
|
||||
Log.d(TAG, mId + ":position " + position + " retrying", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ import java.net.URL;
|
|||
// Single-threaded fallback mode
|
||||
public class DownloadRunnableFallback implements Runnable
|
||||
{
|
||||
private DownloadMission mMission;
|
||||
private final DownloadMission mMission;
|
||||
//private int mId;
|
||||
|
||||
public DownloadRunnableFallback(DownloadMission mission) {
|
||||
if(mission == null) throw new NullPointerException("mission is null");
|
||||
//mId = id;
|
||||
mMission = mission;
|
||||
}
|
||||
|
|
@ -35,7 +36,7 @@ public class DownloadRunnableFallback implements Runnable
|
|||
f.write(buf, 0, len);
|
||||
notifyProgress(len);
|
||||
|
||||
if (Thread.currentThread().interrupted()) {
|
||||
if (Thread.interrupted()) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
package us.shandian.giga.get.sqlite;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import us.shandian.giga.get.DownloadMission;
|
||||
|
||||
/**
|
||||
* SqliteHelper to store {@link us.shandian.giga.get.DownloadMission}
|
||||
*/
|
||||
public class DownloadMissionSQLiteHelper extends SQLiteOpenHelper {
|
||||
|
||||
|
||||
private final String TAG = "DownloadMissionHelper";
|
||||
|
||||
// TODO: use NewPipeSQLiteHelper ('s constants) when playlist branch is merged (?)
|
||||
private static final String DATABASE_NAME = "newpipe.db";
|
||||
|
||||
private static final int DATABASE_VERSION = 2;
|
||||
/**
|
||||
* The table name of download missions
|
||||
*/
|
||||
static final String MISSIONS_TABLE_NAME = "download_missions";
|
||||
|
||||
/**
|
||||
* The key to the directory location of the mission
|
||||
*/
|
||||
static final String KEY_LOCATION = "location";
|
||||
/**
|
||||
* The key to the url of a mission
|
||||
*/
|
||||
static final String KEY_URL = "url";
|
||||
/**
|
||||
* The key to the name of a mission
|
||||
*/
|
||||
static final String KEY_NAME = "name";
|
||||
|
||||
/**
|
||||
* The key to the done.
|
||||
*/
|
||||
static final String KEY_DONE = "bytes_downloaded";
|
||||
|
||||
static final String KEY_TIMESTAMP = "timestamp";
|
||||
|
||||
/**
|
||||
* The statement to create the table
|
||||
*/
|
||||
private static final String MISSIONS_CREATE_TABLE =
|
||||
"CREATE TABLE " + MISSIONS_TABLE_NAME + " (" +
|
||||
KEY_LOCATION + " TEXT NOT NULL, " +
|
||||
KEY_NAME + " TEXT NOT NULL, " +
|
||||
KEY_URL + " TEXT NOT NULL, " +
|
||||
KEY_DONE + " INTEGER NOT NULL, " +
|
||||
KEY_TIMESTAMP + " INTEGER NOT NULL, " +
|
||||
" UNIQUE(" + KEY_LOCATION + ", " + KEY_NAME + "));";
|
||||
|
||||
|
||||
DownloadMissionSQLiteHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all values of the download mission as ContentValues.
|
||||
* @param downloadMission the download mission
|
||||
* @return the content values
|
||||
*/
|
||||
public static ContentValues getValuesOfMission(DownloadMission downloadMission) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_URL, downloadMission.url);
|
||||
values.put(KEY_LOCATION, downloadMission.location);
|
||||
values.put(KEY_NAME, downloadMission.name);
|
||||
values.put(KEY_DONE, downloadMission.done);
|
||||
values.put(KEY_TIMESTAMP, downloadMission.timestamp);
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(MISSIONS_CREATE_TABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// Currently nothing to do
|
||||
}
|
||||
|
||||
public static DownloadMission getMissionFromCursor(Cursor cursor) {
|
||||
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));
|
||||
String url = cursor.getString(cursor.getColumnIndexOrThrow(KEY_URL));
|
||||
DownloadMission mission = new DownloadMission(name, url, location);
|
||||
mission.done = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_DONE));
|
||||
mission.timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_TIMESTAMP));
|
||||
mission.finished = true;
|
||||
return mission;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package us.shandian.giga.get.sqlite;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import us.shandian.giga.get.DownloadDataSource;
|
||||
import us.shandian.giga.get.DownloadMission;
|
||||
|
||||
import static us.shandian.giga.get.sqlite.DownloadMissionSQLiteHelper.KEY_LOCATION;
|
||||
import static us.shandian.giga.get.sqlite.DownloadMissionSQLiteHelper.KEY_NAME;
|
||||
import static us.shandian.giga.get.sqlite.DownloadMissionSQLiteHelper.MISSIONS_TABLE_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* Non-thread-safe implementation of {@link DownloadDataSource}
|
||||
*/
|
||||
public class SQLiteDownloadDataSource implements DownloadDataSource {
|
||||
|
||||
private static final String TAG = "DownloadDataSourceImpl";
|
||||
private final DownloadMissionSQLiteHelper downloadMissionSQLiteHelper;
|
||||
|
||||
public SQLiteDownloadDataSource(Context context) {
|
||||
downloadMissionSQLiteHelper = new DownloadMissionSQLiteHelper(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DownloadMission> loadMissions() {
|
||||
ArrayList<DownloadMission> result;
|
||||
SQLiteDatabase database = downloadMissionSQLiteHelper.getReadableDatabase();
|
||||
Cursor cursor = database.query(MISSIONS_TABLE_NAME, null, null,
|
||||
null, null, null, DownloadMissionSQLiteHelper.KEY_TIMESTAMP);
|
||||
|
||||
int count = cursor.getCount();
|
||||
if(count == 0) return new ArrayList<>();
|
||||
result = new ArrayList<>(count);
|
||||
while (cursor.moveToNext()) {
|
||||
result.add(DownloadMissionSQLiteHelper.getMissionFromCursor(cursor));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMission(DownloadMission downloadMission) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMission(DownloadMission downloadMission) {
|
||||
if(downloadMission == null) throw new NullPointerException("downloadMission is null");
|
||||
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
|
||||
ContentValues values = DownloadMissionSQLiteHelper.getValuesOfMission(downloadMission);
|
||||
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) {
|
||||
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");
|
||||
SQLiteDatabase database = downloadMissionSQLiteHelper.getWritableDatabase();
|
||||
database.delete(MISSIONS_TABLE_NAME,
|
||||
KEY_LOCATION + " = ? AND " +
|
||||
KEY_NAME + " = ?",
|
||||
new String[]{downloadMission.location, downloadMission.name});
|
||||
}
|
||||
}
|
||||
|
|
@ -4,38 +4,69 @@ import android.Manifest;
|
|||
import android.app.Notification;
|
||||
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;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
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;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.download.DownloadActivity;
|
||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import us.shandian.giga.get.DownloadDataSource;
|
||||
import us.shandian.giga.get.DownloadManager;
|
||||
import us.shandian.giga.get.DownloadManagerImpl;
|
||||
import us.shandian.giga.get.DownloadMission;
|
||||
import us.shandian.giga.get.sqlite.SQLiteDownloadDataSource;
|
||||
|
||||
import static org.schabi.newpipe.BuildConfig.DEBUG;
|
||||
|
||||
public class DownloadManagerService extends Service implements DownloadMission.MissionListener
|
||||
public class DownloadManagerService extends Service
|
||||
{
|
||||
|
||||
|
||||
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";
|
||||
|
||||
|
||||
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 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() {
|
||||
|
|
@ -44,14 +75,19 @@ public class DownloadManagerService extends Service implements DownloadMission.M
|
|||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreate");
|
||||
}
|
||||
|
||||
|
||||
mBinder = new DMBinder();
|
||||
if(mDataSource == null) {
|
||||
mDataSource = new SQLiteDownloadDataSource(this);
|
||||
}
|
||||
if (mManager == null) {
|
||||
String path = NewPipeSettings.getVideoDownloadPath(this);
|
||||
mManager = new DownloadManagerImpl(this, path);
|
||||
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: " + path);
|
||||
Log.d(TAG, "Download directory: " + paths);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,28 +123,50 @@ public class DownloadManagerService extends Service implements DownloadMission.M
|
|||
mHandler = new Handler(thread.getLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (msg.what == 0) {
|
||||
int runningCount = 0;
|
||||
|
||||
for (int i = 0; i < mManager.getCount(); i++) {
|
||||
if (mManager.getMission(i).running) {
|
||||
runningCount++;
|
||||
switch (msg.what) {
|
||||
case UPDATE_MESSAGE: {
|
||||
int runningCount = 0;
|
||||
|
||||
for (int i = 0; i < mManager.getCount(); i++) {
|
||||
if (mManager.getMission(i).running) {
|
||||
runningCount++;
|
||||
}
|
||||
}
|
||||
updateState(runningCount);
|
||||
break;
|
||||
}
|
||||
|
||||
updateState(runningCount);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
|
|
@ -123,65 +181,76 @@ public class DownloadManagerService extends Service implements DownloadMission.M
|
|||
for (int i = 0; i < mManager.getCount(); i++) {
|
||||
mManager.pauseMission(i);
|
||||
}
|
||||
|
||||
|
||||
stopForeground(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
int permissionCheck;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||
int permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
permissionCheck = PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
if(permissionCheck == PermissionChecker.PERMISSION_DENIED) {
|
||||
Toast.makeText(this, "Permission denied (read)", Toast.LENGTH_SHORT).show();
|
||||
return null;
|
||||
}
|
||||
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 null;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onProgressUpdate(long done, long total) {
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
long delta = now - mLastTimeStamp;
|
||||
|
||||
if (delta > 2000) {
|
||||
postUpdateMessage();
|
||||
mLastTimeStamp = now;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
postUpdateMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errCode) {
|
||||
postUpdateMessage();
|
||||
}
|
||||
|
||||
private void postUpdateMessage() {
|
||||
mHandler.sendEmptyMessage(0);
|
||||
mHandler.sendEmptyMessage(UPDATE_MESSAGE);
|
||||
}
|
||||
|
||||
private void updateState(int runningCount) {
|
||||
if (runningCount == 0) {
|
||||
stopForeground(true);
|
||||
} else {
|
||||
startForeground(1000, mNotification);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(DownloadMission downloadMission) {
|
||||
postUpdateMessage();
|
||||
notifyMediaScanner(downloadMission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(DownloadMission downloadMission, int errCode) {
|
||||
postUpdateMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Wrapper of DownloadManager
|
||||
public class DMBinder extends Binder {
|
||||
public DownloadManager getDownloadManager() {
|
||||
|
|
@ -189,15 +258,13 @@ public class DownloadManagerService extends Service implements DownloadMission.M
|
|||
}
|
||||
|
||||
public void onMissionAdded(DownloadMission mission) {
|
||||
mission.addListener(DownloadManagerService.this);
|
||||
mission.addListener(missionListener);
|
||||
postUpdateMessage();
|
||||
}
|
||||
|
||||
public void onMissionRemoved(DownloadMission mission) {
|
||||
mission.removeListener(DownloadManagerService.this);
|
||||
mission.removeListener(missionListener);
|
||||
postUpdateMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.support.v4.content.FileProvider;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
|
@ -28,10 +31,14 @@ import us.shandian.giga.service.DownloadManagerService;
|
|||
import us.shandian.giga.ui.common.ProgressDrawable;
|
||||
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";
|
||||
|
||||
static {
|
||||
ALGORITHMS.put(R.id.md5, "MD5");
|
||||
ALGORITHMS.put(R.id.sha1, "SHA1");
|
||||
|
|
@ -212,23 +219,23 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
|
|||
h.lastDone = -1;
|
||||
return true;
|
||||
case R.id.view:
|
||||
Intent i = new Intent();
|
||||
i.setAction(Intent.ACTION_VIEW);
|
||||
File f = new File(h.mission.location + "/" + h.mission.name);
|
||||
File f = new File(h.mission.location, h.mission.name);
|
||||
String ext = Utility.getFileExt(h.mission.name);
|
||||
|
||||
if (ext == null) return false;
|
||||
|
||||
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()) {
|
||||
i.setDataAndType(Uri.fromFile(f), mime);
|
||||
|
||||
try {
|
||||
mContext.startActivity(i);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
viewFileWithFileProvider(f, mime);
|
||||
} else {
|
||||
Log.w(TAG, "File doesn't exist");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -249,6 +256,34 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
private class ChecksumTask extends AsyncTask<String, Void, String> {
|
||||
ProgressDialog prog;
|
||||
|
|
@ -316,12 +351,12 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onProgressUpdate(long done, long total) {
|
||||
public void onProgressUpdate(DownloadMission downloadMission, long done, long total) {
|
||||
mAdapter.updateProgress(mHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
public void onFinish(DownloadMission downloadMission) {
|
||||
//mAdapter.mManager.deleteMission(mHolder.position);
|
||||
// TODO Notification
|
||||
//mAdapter.notifyDataSetChanged();
|
||||
|
|
@ -332,7 +367,7 @@ public class MissionAdapter extends RecyclerView.Adapter<MissionAdapter.ViewHold
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errCode) {
|
||||
public void onError(DownloadMission downloadMission, int errCode) {
|
||||
mAdapter.updateProgress(mHolder);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public abstract class MissionsFragment extends Fragment
|
|||
private MissionAdapter mAdapter;
|
||||
private GridLayoutManager mGridManager;
|
||||
private LinearLayoutManager mLinearManager;
|
||||
private Activity mActivity;
|
||||
private Context mActivity;
|
||||
|
||||
private ServiceConnection mConnection = new ServiceConnection() {
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ public abstract class MissionsFragment extends Fragment
|
|||
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
mLinear = mPrefs.getBoolean("linear", false);
|
||||
|
||||
|
||||
// Bind the service
|
||||
Intent i = new Intent();
|
||||
i.setClass(getActivity(), DownloadManagerService.class);
|
||||
|
|
@ -85,14 +85,15 @@ public abstract class MissionsFragment extends Fragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
public void onAttach(Context activity) {
|
||||
super.onAttach(activity);
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
getActivity().unbindService(mConnection);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue