From 02ab41cffca0fa770ec852e32645562f1835ad06 Mon Sep 17 00:00:00 2001 From: Phillip Dykman Date: Wed, 4 Dec 2019 20:54:55 -0800 Subject: [PATCH 1/2] Simple text mockup of pieces map --- .../transdroid/core/gui/DetailsFragment.java | 2 ++ .../core/gui/lists/DetailsAdapter.java | 24 +++++++++++++++++ .../Qbittorrent/QbittorrentAdapter.java | 14 +++++++--- .../org/transdroid/daemon/TorrentDetails.java | 27 +++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java b/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java index 79843c00..75a53e8a 100644 --- a/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java +++ b/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java @@ -203,6 +203,8 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen .updateTrackers(SimpleListItemAdapter.SimpleStringItem.wrapStringsList(newTorrentDetails.getTrackers())); ((DetailsAdapter) detailsList.getAdapter()) .updateErrors(SimpleListItemAdapter.SimpleStringItem.wrapStringsList(newTorrentDetails.getErrors())); + ((DetailsAdapter) detailsList.getAdapter()) + .updatePieces(newTorrentDetails.getPieces()); } /** diff --git a/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java b/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java index d1d374fe..dcfb7a1f 100644 --- a/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java +++ b/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java @@ -29,6 +29,7 @@ import android.text.util.Linkify; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; +import android.widget.TextView; /** * List adapter that holds a header view showing torrent details and show the list list contained by the torrent. @@ -38,6 +39,8 @@ public class DetailsAdapter extends MergeAdapter { private ViewHolderAdapter torrentDetailsViewAdapter = null; private TorrentDetailsView torrentDetailsView = null; + private ViewHolderAdapter piecesMapViewAdapter = null; + private TextView piecesMapView = null; private ViewHolderAdapter trackersSeparatorAdapter = null; private SimpleListItemAdapter trackersAdapter = null; private ViewHolderAdapter errorsSeparatorAdapter = null; @@ -56,6 +59,13 @@ public class DetailsAdapter extends MergeAdapter { torrentDetailsViewAdapter.setViewVisibility(View.GONE); addAdapter(torrentDetailsViewAdapter); + // Pieces map + piecesMapView = new TextView(context); + piecesMapViewAdapter = new ViewHolderAdapter(piecesMapView); + piecesMapViewAdapter.setViewEnabled(true); + piecesMapViewAdapter.setViewVisibility(View.VISIBLE); + addAdapter(piecesMapViewAdapter); + // Tracker errors errorsSeparatorAdapter = new ViewHolderAdapter(FilterSeparatorView_.build(context).setText( context.getString(R.string.status_errors))); @@ -137,6 +147,20 @@ public class DetailsAdapter extends MergeAdapter { } } + public void updatePieces(List pieces) { + if (pieces == null || pieces.isEmpty()) { + //errorsAdapter.update(new ArrayList()); + //errorsSeparatorAdapter.setViewVisibility(View.GONE); + } else { + String piecesText = ""; + for (int piece : pieces) { + piecesText += piece; + } + piecesMapView.setText(piecesText.substring(0,40)); + //errorsSeparatorAdapter.setViewVisibility(View.VISIBLE); + } + } + /** * Clear currently visible torrent, including header and shown lists */ diff --git a/app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java b/app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java index 0982053d..713c347c 100644 --- a/app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java +++ b/app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java @@ -204,7 +204,8 @@ public class QbittorrentAdapter implements IDaemonAdapter { String mhash = task.getTargetTorrent().getUniqueID(); JSONArray messages = new JSONArray(makeRequest(log, (version >= 30200 ? "/query/propertiesTrackers/" : "/json/propertiesTrackers/") + mhash)); - return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, parseJsonTorrentDetails(messages)); + JSONArray pieces = new JSONArray(makeRequest(log, "/query/getPieceStates/" + mhash)); + return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, parseJsonTorrentDetails(messages, pieces)); case GetFileList: @@ -439,7 +440,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() + proxyFolder + path; } - private TorrentDetails parseJsonTorrentDetails(JSONArray messages) throws JSONException { + private TorrentDetails parseJsonTorrentDetails(JSONArray messages, JSONArray pieceStates) throws JSONException { ArrayList trackers = new ArrayList<>(); ArrayList errors = new ArrayList<>(); @@ -455,8 +456,15 @@ public class QbittorrentAdapter implements IDaemonAdapter { } } + ArrayList pieces = new ArrayList<>(); + if (pieceStates.length() > 0) { + for (int i = 0; i < pieceStates.length(); i++) { + pieces.add(pieceStates.getInt(i)); + } + } + // Return the list - return new TorrentDetails(trackers, errors); + return new TorrentDetails(trackers, errors, pieces); } diff --git a/app/src/main/java/org/transdroid/daemon/TorrentDetails.java b/app/src/main/java/org/transdroid/daemon/TorrentDetails.java index 6f3f2be4..45a02899 100644 --- a/app/src/main/java/org/transdroid/daemon/TorrentDetails.java +++ b/app/src/main/java/org/transdroid/daemon/TorrentDetails.java @@ -18,6 +18,7 @@ package org.transdroid.daemon; import java.util.List; +import java.util.ArrayList; import android.os.Parcel; import android.os.Parcelable; @@ -32,15 +33,29 @@ public final class TorrentDetails implements Parcelable { private final List trackers; private final List errors; + private final List pieces; public TorrentDetails(List trackers, List errors) { this.trackers = trackers; this.errors = errors; + this.pieces = null; + } + + public TorrentDetails(List trackers, List errors, List pieces) { + this.trackers = trackers; + this.errors = errors; + this.pieces = pieces; } private TorrentDetails(Parcel in) { this.trackers = in.createStringArrayList(); this.errors = in.createStringArrayList(); + + int[] piecesarray = in.createIntArray(); + this.pieces = new ArrayList(piecesarray.length); + for (int i : piecesarray) { + this.pieces.add(i); + } } public List getTrackers() { @@ -77,6 +92,10 @@ public final class TorrentDetails implements Parcelable { return errorsText; } + public List getPieces() { + return this.pieces; + } + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public TorrentDetails createFromParcel(Parcel in) { return new TorrentDetails(in); @@ -96,6 +115,14 @@ public final class TorrentDetails implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeStringList(trackers); dest.writeStringList(errors); + + int[] piecesarray = new int[this.pieces.size()]; + for(int i = 0; i < this.pieces.size(); i++) { + if (this.pieces.get(i) != null) { + piecesarray[i] = this.pieces.get(i); + } + } + dest.writeIntArray(piecesarray); } } From db2892981b434094c86bba50ac6e9eeb61f7c8d5 Mon Sep 17 00:00:00 2001 From: Phillip Dykman Date: Thu, 5 Dec 2019 23:52:56 -0800 Subject: [PATCH 2/2] Full pieces view graphic --- .../core/gui/lists/DetailsAdapter.java | 34 ++-- .../core/gui/lists/PiecesMapView.java | 150 ++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 172 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/org/transdroid/core/gui/lists/PiecesMapView.java diff --git a/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java b/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java index dcfb7a1f..d04927b7 100644 --- a/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java +++ b/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java @@ -21,6 +21,7 @@ import java.util.List; import org.transdroid.R; import org.transdroid.core.gui.navigation.*; +import org.transdroid.core.gui.lists.PiecesMapView; import org.transdroid.daemon.Torrent; import org.transdroid.daemon.TorrentFile; @@ -29,7 +30,6 @@ import android.text.util.Linkify; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; -import android.widget.TextView; /** * List adapter that holds a header view showing torrent details and show the list list contained by the torrent. @@ -39,8 +39,9 @@ public class DetailsAdapter extends MergeAdapter { private ViewHolderAdapter torrentDetailsViewAdapter = null; private TorrentDetailsView torrentDetailsView = null; + private ViewHolderAdapter piecesSeparatorAdapter = null; private ViewHolderAdapter piecesMapViewAdapter = null; - private TextView piecesMapView = null; + private PiecesMapView piecesMapView = null; private ViewHolderAdapter trackersSeparatorAdapter = null; private SimpleListItemAdapter trackersAdapter = null; private ViewHolderAdapter errorsSeparatorAdapter = null; @@ -60,10 +61,15 @@ public class DetailsAdapter extends MergeAdapter { addAdapter(torrentDetailsViewAdapter); // Pieces map - piecesMapView = new TextView(context); + piecesSeparatorAdapter = new ViewHolderAdapter(FilterSeparatorView_.build(context).setText( + context.getString(R.string.status_pieces))); + piecesSeparatorAdapter.setViewEnabled(false); + piecesSeparatorAdapter.setViewVisibility(View.GONE); + addAdapter(piecesSeparatorAdapter); + piecesMapView = new PiecesMapView(context); piecesMapViewAdapter = new ViewHolderAdapter(piecesMapView); - piecesMapViewAdapter.setViewEnabled(true); - piecesMapViewAdapter.setViewVisibility(View.VISIBLE); + piecesMapViewAdapter.setViewEnabled(false); + piecesMapViewAdapter.setViewVisibility(View.GONE); addAdapter(piecesMapViewAdapter); // Tracker errors @@ -149,15 +155,17 @@ public class DetailsAdapter extends MergeAdapter { public void updatePieces(List pieces) { if (pieces == null || pieces.isEmpty()) { - //errorsAdapter.update(new ArrayList()); - //errorsSeparatorAdapter.setViewVisibility(View.GONE); + piecesSeparatorAdapter.setViewEnabled(false); + piecesSeparatorAdapter.setViewVisibility(View.GONE); + piecesMapViewAdapter.setViewEnabled(false); + piecesMapViewAdapter.setViewVisibility(View.GONE); } else { - String piecesText = ""; - for (int piece : pieces) { - piecesText += piece; - } - piecesMapView.setText(piecesText.substring(0,40)); - //errorsSeparatorAdapter.setViewVisibility(View.VISIBLE); + piecesMapView.setPieces(pieces); + + piecesMapViewAdapter.setViewEnabled(true); + piecesMapViewAdapter.setViewVisibility(View.VISIBLE); + piecesSeparatorAdapter.setViewEnabled(true); + piecesSeparatorAdapter.setViewVisibility(View.VISIBLE); } } diff --git a/app/src/main/java/org/transdroid/core/gui/lists/PiecesMapView.java b/app/src/main/java/org/transdroid/core/gui/lists/PiecesMapView.java new file mode 100644 index 00000000..805a3cbd --- /dev/null +++ b/app/src/main/java/org/transdroid/core/gui/lists/PiecesMapView.java @@ -0,0 +1,150 @@ +package org.transdroid.core.gui.lists; + +import org.transdroid.R; + +import android.content.Context; +import android.view.View; +import android.graphics.Canvas; +import android.graphics.Paint; + +import java.util.ArrayList; +import java.util.List; +import java.lang.Math; + +class PiecesMapView extends View { + + private final float scale = getContext().getResources().getDisplayMetrics().density; + private final int MINIMUM_HEIGHT = (int) (25 * scale); + private final int MINIMUM_PIECE_WIDTH = (int) (2 * scale); + + private ArrayList pieces = null; + + private final Paint downloadingPaint = new Paint(); + private final Paint donePaint = new Paint(); + private final Paint partialDonePaint = new Paint(); + + public PiecesMapView(Context context) { + super(context); + initPaints(); + } + + private void initPaints() { + downloadingPaint.setColor(getResources().getColor(R.color.torrent_downloading)); + donePaint.setColor(getResources().getColor(R.color.torrent_seeding)); + partialDonePaint.setColor(getResources().getColor(R.color.file_low)); + } + + public void setPieces(List pieces) { + this.pieces = new ArrayList(pieces); + invalidate(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int ws = MeasureSpec.getSize(widthMeasureSpec); + int hs = Math.max(getHeight(), MINIMUM_HEIGHT); + setMeasuredDimension(ws, hs); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (this.pieces == null) { + return; + } + + int height = getHeight(); + int width = getWidth(); + + // downscale + ArrayList piecesScaled; + int pieceWidth; + + pieceWidth = MINIMUM_PIECE_WIDTH; + piecesScaled = new ArrayList(); + + int bucketCount = (int) Math.ceil((double) width / (double) pieceWidth); + int bucketSize = (int) Math.floor((double)this.pieces.size() / (double) bucketCount); + + // loop buckets + for (int i = 0; i < bucketCount; i++) { + + // Get segment of pieces that fall into bucket + int start = i * bucketSize; + + // If this is the last bucket, throw the remainder of the pieces array into it + int end = (i == bucketCount-1) ? this.pieces.size() : (i+1) * bucketSize; + + ArrayList bucket = new ArrayList(this.pieces.subList(start, end)); + + int doneCount = 0; + int downloadingCount = 0; + + // loop pieces in bucket + for(int j = 0; j < bucket.size(); j++) { + // Count downloading pieces + if (bucket.get(j) == 1) { + downloadingCount++; + } + // Count finished pieces + else if (bucket.get(j) == 2) { + doneCount++; + } + } + + int state; + // If a piece is downloading show bucket as downloading + if (downloadingCount > 0) { + state = 1; + } + // If all pieces are done, show bucket as done + else if (doneCount == bucket.size()) { + state = 2; + } + // Some done pieces, show bucket as partially done + else if (doneCount > 0) { + state = 3; + } + // bucket is not downloaded + else { + state = 0; + } + + piecesScaled.add(state); + } + + String scaledPiecesString = ""; + for (int s : piecesScaled) + { + scaledPiecesString += s; + } + + // Draw downscaled peices + for (int i = 0; i < piecesScaled.size(); i++) { + int piece = piecesScaled.get(i); + + if (piece == 0) { + continue; + } + + Paint paint = new Paint(); + switch (piece) { + case 1: + paint = downloadingPaint; + break; + case 2: + paint = donePaint; + break; + case 3: + paint = partialDonePaint; + break; + } + int x = i * pieceWidth; + + canvas.drawRect(x, 0, x + pieceWidth, height, paint); + } + } + +} + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7eb5dc56..d0a76a70 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -128,6 +128,7 @@ Low priority Normal priority High priority + PIECES TRACKERS ERRORS FILES