You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
639 lines
18 KiB
639 lines
18 KiB
/* |
|
* This file is part of Transdroid <http://www.transdroid.org> |
|
* |
|
* Transdroid is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* Transdroid is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
*/ |
|
package org.transdroid.daemon; |
|
|
|
import android.os.Parcel; |
|
import android.os.Parcelable; |
|
|
|
import java.util.Calendar; |
|
import java.util.Date; |
|
|
|
/** |
|
* Represents a torrent on a server daemon. |
|
* |
|
* @author erickok |
|
*/ |
|
public final class Torrent implements Parcelable, Comparable<Torrent>, Finishable { |
|
|
|
public static final Parcelable.Creator<Torrent> CREATOR = new Parcelable.Creator<Torrent>() { |
|
public Torrent createFromParcel(Parcel in) { |
|
return new Torrent(in); |
|
} |
|
|
|
public Torrent[] newArray(int size) { |
|
return new Torrent[size]; |
|
} |
|
}; |
|
final private long id; |
|
final private String hash; |
|
final private String name; |
|
final private int rateDownload; |
|
final private int rateUpload; |
|
final private int seedersConnected; |
|
final private int seedersKnown; |
|
final private int leechersConnected; |
|
final private int leechersKnown; |
|
final private int eta; |
|
final private long downloadedEver; |
|
final private long uploadedEver; |
|
final private long totalSize; |
|
final private float partDone; |
|
final private float available; |
|
final private Date dateAdded; |
|
final private Date dateDone; |
|
final private String error; |
|
final private Daemon daemon; |
|
private TorrentStatus statusCode; |
|
private String locationDir; |
|
private String label; |
|
private boolean sequentialDownload; |
|
private boolean firstLastPieceDownload; |
|
|
|
|
|
private Torrent(Parcel in) { |
|
this.id = in.readLong(); |
|
this.hash = in.readString(); |
|
this.name = in.readString(); |
|
this.statusCode = TorrentStatus.getStatus(in.readInt()); |
|
this.locationDir = in.readString(); |
|
|
|
this.rateDownload = in.readInt(); |
|
this.rateUpload = in.readInt(); |
|
this.seedersConnected = in.readInt(); |
|
this.seedersKnown = in.readInt(); |
|
this.leechersConnected = in.readInt(); |
|
this.leechersKnown = in.readInt(); |
|
this.eta = in.readInt(); |
|
|
|
this.downloadedEver = in.readLong(); |
|
this.uploadedEver = in.readLong(); |
|
this.totalSize = in.readLong(); |
|
this.partDone = in.readFloat(); |
|
this.available = in.readFloat(); |
|
this.label = in.readString(); |
|
this.sequentialDownload = in.readByte() != 0; |
|
this.firstLastPieceDownload = in.readByte() != 0; |
|
|
|
long lDateAdded = in.readLong(); |
|
this.dateAdded = (lDateAdded == -1) ? null : new Date(lDateAdded); |
|
long lDateDone = in.readLong(); |
|
this.dateDone = (lDateDone == -1) ? null : new Date(lDateDone); |
|
this.error = in.readString(); |
|
this.daemon = Daemon.valueOf(in.readString()); |
|
} |
|
|
|
public Torrent(long id, String hash, String name, TorrentStatus statusCode, String locationDir, int rateDownload, |
|
int rateUpload, int seedersConnected, int seedersKnown, int leechersConnected, int leechersKnown, int eta, |
|
long downloadedEver, long uploadedEver, long totalSize, float partDone, float available, String label, |
|
Date dateAdded, Date realDateDone, String error, Daemon daemon) { |
|
this.id = id; |
|
this.hash = hash; |
|
this.name = name; |
|
this.statusCode = statusCode; |
|
this.locationDir = locationDir; |
|
|
|
this.rateDownload = rateDownload; |
|
this.rateUpload = rateUpload; |
|
this.seedersConnected = seedersConnected; |
|
this.seedersKnown = seedersKnown; |
|
this.leechersConnected = leechersConnected; |
|
this.leechersKnown = leechersKnown; |
|
this.eta = eta; |
|
|
|
this.downloadedEver = downloadedEver; |
|
this.uploadedEver = uploadedEver; |
|
this.totalSize = totalSize; |
|
this.partDone = partDone; |
|
this.available = available; |
|
this.label = label; |
|
this.sequentialDownload = false; |
|
this.firstLastPieceDownload = false; |
|
|
|
this.dateAdded = dateAdded; |
|
if (realDateDone != null) { |
|
this.dateDone = realDateDone; |
|
} else { |
|
if (this.partDone == 1) { |
|
// Finished but no finished date: set so move to bottom of list |
|
Calendar cal = Calendar.getInstance(); |
|
cal.clear(); |
|
cal.set(1900, Calendar.DECEMBER, 31); |
|
this.dateDone = cal.getTime(); |
|
} else if (eta == -1 || eta == -2) { |
|
// UNknown eta: move to the top of the list |
|
this.dateDone = new Date(Long.MAX_VALUE); |
|
} else { |
|
Calendar cal = Calendar.getInstance(); |
|
cal.add(Calendar.SECOND, eta); |
|
this.dateDone = cal.getTime(); |
|
} |
|
} |
|
this.error = error; |
|
this.daemon = daemon; |
|
} |
|
|
|
Torrent(Builder builder) { |
|
this.id = builder.id; |
|
this.hash = builder.hash; |
|
this.name = builder.name; |
|
this.statusCode = builder.statusCode; |
|
this.locationDir = builder.locationDir; |
|
|
|
this.rateDownload = builder.rateDownload; |
|
this.rateUpload = builder.rateUpload; |
|
this.seedersConnected = builder.seedersConnected; |
|
this.seedersKnown = builder.seedersKnown; |
|
this.leechersConnected = builder.leechersConnected; |
|
this.leechersKnown = builder.leechersKnown; |
|
this.eta = builder.eta; |
|
|
|
this.downloadedEver = builder.downloadedEver; |
|
this.uploadedEver = builder.uploadedEver; |
|
this.totalSize = builder.totalSize; |
|
this.partDone = builder.partDone; |
|
this.available = builder.available; |
|
this.label = builder.label; |
|
|
|
this.dateAdded = builder.dateAdded; |
|
if (builder.realDateDone != null) { |
|
this.dateDone = builder.realDateDone; |
|
} else { |
|
if (this.partDone == 1) { |
|
// Finished but no finished date: set so move to bottom of list |
|
Calendar cal = Calendar.getInstance(); |
|
cal.clear(); |
|
cal.set(1900, Calendar.DECEMBER, 31); |
|
this.dateDone = cal.getTime(); |
|
} else if (eta == -1 || eta == -2) { |
|
// UNknown eta: move to the top of the list |
|
this.dateDone = new Date(Long.MAX_VALUE); |
|
} else { |
|
Calendar cal = Calendar.getInstance(); |
|
cal.add(Calendar.SECOND, eta); |
|
this.dateDone = cal.getTime(); |
|
} |
|
} |
|
this.error = builder.error; |
|
this.daemon = builder.daemon; |
|
this.sequentialDownload = builder.sequentialDownload; |
|
this.firstLastPieceDownload = builder.firstLastPieceDownload; |
|
|
|
} |
|
|
|
public String getName() { |
|
return name; |
|
} |
|
|
|
public TorrentStatus getStatusCode() { |
|
return statusCode; |
|
} |
|
|
|
public String getLocationDir() { |
|
return locationDir; |
|
} |
|
|
|
public int getRateDownload() { |
|
return rateDownload; |
|
} |
|
|
|
public int getRateUpload() { |
|
return rateUpload; |
|
} |
|
|
|
public int getSeedersConnected() { |
|
return seedersConnected; |
|
} |
|
|
|
public int getSeedersKnown() { |
|
return seedersKnown; |
|
} |
|
|
|
public int getLeechersConnected() { |
|
return leechersConnected; |
|
} |
|
|
|
public int getLeechersKnown() { |
|
return leechersKnown; |
|
} |
|
|
|
public int getEta() { |
|
return eta; |
|
} |
|
|
|
public long getDownloadedEver() { |
|
return downloadedEver; |
|
} |
|
|
|
public long getUploadedEver() { |
|
return uploadedEver; |
|
} |
|
|
|
public long getTotalSize() { |
|
return totalSize; |
|
} |
|
|
|
public float getPartDone() { |
|
return partDone; |
|
} |
|
|
|
public float getAvailability() { |
|
return available; |
|
} |
|
|
|
public String getLabelName() { |
|
return label; |
|
} |
|
|
|
public boolean isSequentiallyDownloading() { |
|
return sequentialDownload; |
|
} |
|
|
|
public boolean isDownloadingFirstLastPieceFirst() { |
|
return firstLastPieceDownload; |
|
} |
|
|
|
public Date getDateAdded() { |
|
return dateAdded; |
|
} |
|
|
|
public Date getDateDone() { |
|
return dateDone; |
|
} |
|
|
|
public String getError() { |
|
return error; |
|
} |
|
|
|
public Daemon getDaemon() { |
|
return daemon; |
|
} |
|
|
|
/** |
|
* Returns the torrent-specific ID, which is the torrent's hash or (if not available) the long number |
|
* |
|
* @return The torrent's (session-transient) unique ID |
|
*/ |
|
public String getUniqueID() { |
|
if (this.hash == null) { |
|
return "" + this.id; |
|
} else { |
|
return this.hash; |
|
} |
|
} |
|
|
|
/** |
|
* Gives the upload/download seed ratio. |
|
* |
|
* @return The ratio in range [0,r] |
|
*/ |
|
public double getRatio() { |
|
return ((double) uploadedEver) / ((double) downloadedEver); |
|
} |
|
|
|
/** |
|
* Gives the percentage of the download that is completed |
|
* |
|
* @return The downloaded percentage in range [0,1] |
|
*/ |
|
public float getDownloadedPercentage() { |
|
return partDone; |
|
} |
|
|
|
/** |
|
* Returns whether this torrents is actively downloading or not. |
|
* |
|
* @param dormantAsInactive If true, dormant (0KB/s, so no data transfer) torrents are never actively downloading |
|
* @return True if this torrent is to be treated as being in a downloading state, that is, it is trying to finish a |
|
* download |
|
*/ |
|
public boolean isDownloading(boolean dormantAsInactive) { |
|
return statusCode == TorrentStatus.Downloading && (!dormantAsInactive || rateDownload > 0); |
|
} |
|
|
|
/** |
|
* Returns whether this torrents is actively seeding or not. |
|
* |
|
* @param dormantAsInactive If true, dormant (0KB/s, so no data transfer) torrents are never actively seeding |
|
* @return True if this torrent is to be treated as being in a seeding state, that is, it is sending data to |
|
* leechers |
|
*/ |
|
public boolean isSeeding(boolean dormantAsInactive) { |
|
return statusCode == TorrentStatus.Seeding && (!dormantAsInactive || rateUpload > 0); |
|
} |
|
|
|
/** |
|
* {@inheritDoc} |
|
*/ |
|
@Override |
|
public boolean isStarted() { |
|
return partDone > 0; |
|
} |
|
|
|
/** |
|
* {@inheritDoc} |
|
*/ |
|
@Override |
|
public boolean isFinished() { |
|
return partDone >= 1; |
|
} |
|
|
|
/** |
|
* Indicates if the torrent can be paused at this moment |
|
* |
|
* @return If it can be paused |
|
*/ |
|
public boolean canPause() { |
|
// Can pause when it is downloading or seeding |
|
return statusCode == TorrentStatus.Downloading || statusCode == TorrentStatus.Seeding || statusCode == TorrentStatus.Queued; |
|
} |
|
|
|
/** |
|
* Indicates whether the torrent can be resumed |
|
* |
|
* @return If it can be resumed |
|
*/ |
|
public boolean canResume() { |
|
// Can resume when it is paused |
|
return statusCode == TorrentStatus.Paused; |
|
} |
|
|
|
/** |
|
* Indicates if the torrent can be started at this moment |
|
* |
|
* @return If it can be started |
|
*/ |
|
public boolean canStart() { |
|
// Can start when it is queued |
|
return statusCode == TorrentStatus.Queued; |
|
} |
|
|
|
/** |
|
* Indicates whether the torrent can be stopped |
|
* |
|
* @return If it can be stopped |
|
*/ |
|
public boolean canStop() { |
|
// Can stop when it is downloading or seeding or paused |
|
return statusCode == TorrentStatus.Downloading || statusCode == TorrentStatus.Seeding |
|
|| statusCode == TorrentStatus.Paused; |
|
} |
|
|
|
public void mimicResume() { |
|
if (getDownloadedPercentage() >= 1) { |
|
statusCode = TorrentStatus.Seeding; |
|
} else { |
|
statusCode = TorrentStatus.Downloading; |
|
} |
|
} |
|
|
|
public void mimicPause() { |
|
statusCode = TorrentStatus.Paused; |
|
} |
|
|
|
public void mimicStart() { |
|
if (getDownloadedPercentage() >= 1) { |
|
statusCode = TorrentStatus.Seeding; |
|
} else { |
|
statusCode = TorrentStatus.Downloading; |
|
} |
|
} |
|
|
|
public void mimicStop() { |
|
statusCode = TorrentStatus.Queued; |
|
} |
|
|
|
public void mimicNewLabel(String newLabel) { |
|
label = newLabel; |
|
} |
|
|
|
public void mimicSequentialDownload(boolean sequentialDownload) { |
|
this.sequentialDownload = sequentialDownload; |
|
} |
|
|
|
public void mimicFirstLastPieceDownload(boolean firstLastPieceDownload) { |
|
this.firstLastPieceDownload = firstLastPieceDownload; |
|
} |
|
|
|
public void mimicCheckingStatus() { |
|
statusCode = TorrentStatus.Checking; |
|
} |
|
|
|
public void mimicNewLocation(String newLocation) { |
|
locationDir = newLocation; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
// (HASH_OR_ID) NAME |
|
return "(" + ((hash != null) ? hash : String.valueOf(id)) + ") " + name; |
|
} |
|
|
|
@Override |
|
public int compareTo(Torrent another) { |
|
// Compare torrent objects on their name (used for sorting only!) |
|
return name.compareTo(another.getName()); |
|
} |
|
|
|
@Override |
|
public int describeContents() { |
|
return 0; |
|
} |
|
|
|
@Override |
|
public void writeToParcel(Parcel dest, int flags) { |
|
dest.writeLong(id); |
|
dest.writeString(hash); |
|
dest.writeString(name); |
|
dest.writeInt(statusCode.getCode()); |
|
dest.writeString(locationDir); |
|
|
|
dest.writeInt(rateDownload); |
|
dest.writeInt(rateUpload); |
|
dest.writeInt(seedersConnected); |
|
dest.writeInt(seedersKnown); |
|
dest.writeInt(leechersConnected); |
|
dest.writeInt(leechersKnown); |
|
dest.writeInt(eta); |
|
|
|
dest.writeLong(downloadedEver); |
|
dest.writeLong(uploadedEver); |
|
dest.writeLong(totalSize); |
|
dest.writeFloat(partDone); |
|
dest.writeFloat(available); |
|
dest.writeString(label); |
|
dest.writeByte((byte) (sequentialDownload ? 1 : 0)); |
|
dest.writeByte((byte) (firstLastPieceDownload ? 1 : 0)); |
|
|
|
dest.writeLong((dateAdded == null) ? -1 : dateAdded.getTime()); |
|
dest.writeLong((dateDone == null) ? -1 : dateDone.getTime()); |
|
dest.writeString(error); |
|
dest.writeString(daemon.name()); |
|
} |
|
|
|
public static class Builder { |
|
|
|
private long id; |
|
private String hash; |
|
private String name; |
|
private TorrentStatus statusCode; |
|
private String locationDir; |
|
private int rateDownload; |
|
private int rateUpload; |
|
private int seedersConnected; |
|
private int seedersKnown; |
|
private int leechersConnected; |
|
private int leechersKnown; |
|
private int eta; |
|
private long downloadedEver; |
|
private long uploadedEver; |
|
private long totalSize; |
|
private float partDone; |
|
private float available; |
|
private String label; |
|
private Date dateAdded; |
|
private Date realDateDone; |
|
private String error; |
|
private Daemon daemon; |
|
private boolean sequentialDownload; |
|
private boolean firstLastPieceDownload; |
|
|
|
public Builder setId(long id) { |
|
this.id = id; |
|
return this; |
|
} |
|
|
|
public Builder setHash(String hash) { |
|
this.hash = hash; |
|
return this; |
|
} |
|
|
|
public Builder setName(String name) { |
|
this.name = name; |
|
return this; |
|
} |
|
|
|
public Builder setStatusCode(TorrentStatus statusCode) { |
|
this.statusCode = statusCode; |
|
return this; |
|
} |
|
|
|
public Builder setLocationDir(String locationDir) { |
|
this.locationDir = locationDir; |
|
return this; |
|
} |
|
|
|
public Builder setRateDownload(int rateDownload) { |
|
this.rateDownload = rateDownload; |
|
return this; |
|
} |
|
|
|
public Builder setRateUpload(int rateUpload) { |
|
this.rateUpload = rateUpload; |
|
return this; |
|
} |
|
|
|
public Builder setSeedersConnected(int seedersConnected) { |
|
this.seedersConnected = seedersConnected; |
|
return this; |
|
} |
|
|
|
public Builder setSeedersKnown(int seedersKnown) { |
|
this.seedersKnown = seedersKnown; |
|
return this; |
|
} |
|
|
|
public Builder setLeechersConnected(int leechersConnected) { |
|
this.leechersConnected = leechersConnected; |
|
return this; |
|
} |
|
|
|
public Builder setLeechersKnown(int leechersKnown) { |
|
this.leechersKnown = leechersKnown; |
|
return this; |
|
} |
|
|
|
public Builder setEta(int eta) { |
|
this.eta = eta; |
|
return this; |
|
} |
|
|
|
public Builder setDownloadedEver(long downloadedEver) { |
|
this.downloadedEver = downloadedEver; |
|
return this; |
|
} |
|
|
|
public Builder setUploadedEver(long uploadedEver) { |
|
this.uploadedEver = uploadedEver; |
|
return this; |
|
} |
|
|
|
public Builder setTotalSize(long totalSize) { |
|
this.totalSize = totalSize; |
|
return this; |
|
} |
|
|
|
public Builder setPartDone(float partDone) { |
|
this.partDone = partDone; |
|
return this; |
|
} |
|
|
|
public Builder setAvailable(float available) { |
|
this.available = available; |
|
return this; |
|
} |
|
|
|
public Builder setLabel(String label) { |
|
this.label = label; |
|
return this; |
|
} |
|
|
|
public Builder setDateAdded(Date dateAdded) { |
|
this.dateAdded = dateAdded; |
|
return this; |
|
} |
|
|
|
public Builder setRealDateDone(Date realDateDone) { |
|
this.realDateDone = realDateDone; |
|
return this; |
|
} |
|
|
|
public Builder setError(String error) { |
|
this.error = error; |
|
return this; |
|
} |
|
|
|
public Builder setDaemon(Daemon daemon) { |
|
this.daemon = daemon; |
|
return this; |
|
} |
|
|
|
public Builder setSequentialDownload(boolean sequentialDownload) { |
|
this.sequentialDownload = sequentialDownload; |
|
return this; |
|
} |
|
|
|
public Builder setFirstLastPieceDownload(boolean firstLastPieceDownload) { |
|
this.firstLastPieceDownload = firstLastPieceDownload; |
|
return this; |
|
} |
|
|
|
public Torrent createTorrent() { |
|
return new Torrent(this); |
|
} |
|
} |
|
}
|
|
|