From fc83f907e18cf43714f3681778935a9c270d984e Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Wed, 6 Mar 2013 03:12:31 +0100 Subject: [PATCH] Build torrent and details fragment UI and started on logic. --- .../daemon/BitComet/BitCometAdapter.java | 6 +- .../daemon/Bitflu/BitfluAdapter.java | 3 +- .../daemon/BuffaloNas/BuffaloNasAdapter.java | 3 +- .../DLinkRouterBT/DLinkRouterBTAdapter.java | 3 +- .../daemon/Deluge/DelugeAdapter.java | 5 +- .../daemon/Ktorrent/StatsParser.java | 4 +- .../Qbittorrent/QbittorrentAdapter.java | 3 +- .../daemon/Rtorrent/RtorrentAdapter.java | 10 +- .../transdroid/daemon/Tfb4rt/StatsParser.java | 4 +- lib/src/org/transdroid/daemon/Torrent.java | 7 +- .../Transmission/TransmissionAdapter.java | 3 +- .../daemon/Utorrent/UtorrentAdapter.java | 3 +- .../transdroid/daemon/Vuze/VuzeAdapter.java | 16 +- .../daemon/util/FileSizeConverter.java | 84 ++++-- lite/.classpath | 2 +- lite/AndroidManifest.xml | 64 ++++- lite/res/layout-w600dp/activity_torrents.xml | 2 +- lite/res/layout-w720dp/activity_torrents.xml | 4 +- lite/res/layout/activity_details.xml | 16 ++ lite/res/layout/activity_torrents.xml | 3 +- lite/res/layout/fragment_details.xml | 1 + lite/res/layout/fragment_details_header.xml | 26 +- lite/res/layout/fragment_torrents.xml | 1 + lite/res/layout/list_item_torrent.xml | 11 +- lite/res/layout/list_item_torrentfile.xml | 58 ++++ lite/res/menu/activity_details.xml | 9 + lite/res/values/strings.xml | 59 +++- lite/res/xml/searchable.xml | 8 + .../app/search/SearchHelper.java | 2 +- .../app/search/SearchResult.java | 2 +- .../{lite => core}/app/search/SearchSite.java | 6 +- .../app/settings/AboutSettings.java | 2 +- .../app/settings/ApplicationSettings.java | 52 +++- .../app/settings/NotificationSettings.java | 2 +- .../app/settings/RssfeedSetting.java | 6 +- .../app/settings/ServerSetting.java | 33 ++- .../app/settings/WebsearchSetting.java | 6 +- .../transdroid/core/gui/DetailsActivity.java | 132 +++++++++ .../transdroid/core/gui/DetailsFagment.java | 113 ++++++++ .../core/gui/SearchHistoryProvider.java | 22 ++ .../transdroid/core/gui/TorrentsActivity.java | 253 ++++++++++++++++++ .../transdroid/core/gui/TorrentsFragment.java | 69 +++++ .../core/gui/lists/DetailsAdapter.java | 141 ++++++++++ .../core/gui/lists/LocalTorrent.java | 233 ++++++++++++++++ .../gui/lists/SimpleListItem.java} | 4 +- .../core/gui/lists/SimpleListItemAdapter.java | 91 +++++++ .../gui/lists/SimpleListItemView.java} | 10 +- .../core/gui/lists/TorrentDetailsView.java | 85 ++++++ .../core/gui/lists/TorrentFileView.java | 53 ++++ .../gui/lists/TorrentProgressBar.java | 2 +- .../core/gui/lists/TorrentView.java | 47 ++++ .../core/gui/lists/TorrentsAdapter.java | 67 +++++ .../gui/navigation/FilterListAdapter.java | 78 ++++++ .../gui/navigation/FilterSeparatorView.java | 5 +- .../{lite => core}/gui/navigation/Label.java | 6 +- .../gui/navigation/NavigationHelper.java | 2 +- .../gui/navigation/StatusType.java | 5 +- .../gui/settings/MainSettingsActivity.java | 20 +- .../gui/settings/OtherSettingsActivity.java | 4 +- .../gui/settings/RssfeedPreference.java | 4 +- .../gui/settings/RssfeedSettingsActivity.java | 4 +- .../gui/settings/ServerPreference.java | 4 +- .../gui/settings/ServerSettingsActivity.java | 4 +- .../gui/settings/WebsearchPreference.java | 4 +- .../settings/WebsearchSettingsActivity.java | 4 +- .../transdroid/lite/gui/DetailsFagment.java | 47 ---- .../transdroid/lite/gui/TorrentsActivity.java | 94 ------- .../transdroid/lite/gui/TorrentsFragment.java | 11 - .../lite/gui/navigation/FilterAdapter.java | 125 --------- 69 files changed, 1850 insertions(+), 422 deletions(-) create mode 100644 lite/res/layout/activity_details.xml create mode 100644 lite/res/layout/list_item_torrentfile.xml create mode 100644 lite/res/menu/activity_details.xml create mode 100644 lite/res/xml/searchable.xml rename lite/src/org/transdroid/{lite => core}/app/search/SearchHelper.java (99%) rename lite/src/org/transdroid/{lite => core}/app/search/SearchResult.java (96%) rename lite/src/org/transdroid/{lite => core}/app/search/SearchSite.java (80%) rename lite/src/org/transdroid/{lite => core}/app/settings/AboutSettings.java (94%) rename lite/src/org/transdroid/{lite => core}/app/settings/ApplicationSettings.java (72%) rename lite/src/org/transdroid/{lite => core}/app/settings/NotificationSettings.java (98%) rename lite/src/org/transdroid/{lite => core}/app/settings/RssfeedSetting.java (89%) rename lite/src/org/transdroid/{lite => core}/app/settings/ServerSetting.java (78%) rename lite/src/org/transdroid/{lite => core}/app/settings/WebsearchSetting.java (85%) create mode 100644 lite/src/org/transdroid/core/gui/DetailsActivity.java create mode 100644 lite/src/org/transdroid/core/gui/DetailsFagment.java create mode 100644 lite/src/org/transdroid/core/gui/SearchHistoryProvider.java create mode 100644 lite/src/org/transdroid/core/gui/TorrentsActivity.java create mode 100644 lite/src/org/transdroid/core/gui/TorrentsFragment.java create mode 100644 lite/src/org/transdroid/core/gui/lists/DetailsAdapter.java create mode 100644 lite/src/org/transdroid/core/gui/lists/LocalTorrent.java rename lite/src/org/transdroid/{lite/gui/navigation/FilterItem.java => core/gui/lists/SimpleListItem.java} (64%) create mode 100644 lite/src/org/transdroid/core/gui/lists/SimpleListItemAdapter.java rename lite/src/org/transdroid/{lite/gui/navigation/FilterItemView.java => core/gui/lists/SimpleListItemView.java} (58%) create mode 100644 lite/src/org/transdroid/core/gui/lists/TorrentDetailsView.java create mode 100644 lite/src/org/transdroid/core/gui/lists/TorrentFileView.java rename lite/src/org/transdroid/{lite => core}/gui/lists/TorrentProgressBar.java (98%) create mode 100644 lite/src/org/transdroid/core/gui/lists/TorrentView.java create mode 100644 lite/src/org/transdroid/core/gui/lists/TorrentsAdapter.java create mode 100644 lite/src/org/transdroid/core/gui/navigation/FilterListAdapter.java rename lite/src/org/transdroid/{lite => core}/gui/navigation/FilterSeparatorView.java (86%) rename lite/src/org/transdroid/{lite => core}/gui/navigation/Label.java (62%) rename lite/src/org/transdroid/{lite => core}/gui/navigation/NavigationHelper.java (94%) rename lite/src/org/transdroid/{lite => core}/gui/navigation/StatusType.java (92%) rename lite/src/org/transdroid/{lite => core}/gui/settings/MainSettingsActivity.java (91%) rename lite/src/org/transdroid/{lite => core}/gui/settings/OtherSettingsActivity.java (87%) rename lite/src/org/transdroid/{lite => core}/gui/settings/RssfeedPreference.java (94%) rename lite/src/org/transdroid/{lite => core}/gui/settings/RssfeedSettingsActivity.java (92%) rename lite/src/org/transdroid/{lite => core}/gui/settings/ServerPreference.java (94%) rename lite/src/org/transdroid/{lite => core}/gui/settings/ServerSettingsActivity.java (97%) rename lite/src/org/transdroid/{lite => core}/gui/settings/WebsearchPreference.java (94%) rename lite/src/org/transdroid/{lite => core}/gui/settings/WebsearchSettingsActivity.java (92%) delete mode 100644 lite/src/org/transdroid/lite/gui/DetailsFagment.java delete mode 100644 lite/src/org/transdroid/lite/gui/TorrentsActivity.java delete mode 100644 lite/src/org/transdroid/lite/gui/TorrentsFragment.java delete mode 100644 lite/src/org/transdroid/lite/gui/navigation/FilterAdapter.java diff --git a/lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java b/lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java index 732a4e8a..525c9c0c 100644 --- a/lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java +++ b/lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java @@ -480,7 +480,8 @@ public class BitCometAdapter implements IDaemonAdapter { comment, dateAdded, null, - null)); + null, + settings.getType())); } } } @@ -557,7 +558,8 @@ public class BitCometAdapter implements IDaemonAdapter { label, dateAdded, null, - null)); // Not supported in the web interface + null, // Not supported in the web interface + settings.getType())); id++; // Stop/start/etc. requests are made by ID, which is the order number in the returned XML list :-S diff --git a/lib/src/org/transdroid/daemon/Bitflu/BitfluAdapter.java b/lib/src/org/transdroid/daemon/Bitflu/BitfluAdapter.java index 14e618bd..cfcc2fe0 100644 --- a/lib/src/org/transdroid/daemon/Bitflu/BitfluAdapter.java +++ b/lib/src/org/transdroid/daemon/Bitflu/BitfluAdapter.java @@ -214,7 +214,8 @@ public class BitfluAdapter implements IDaemonAdapter { null, // label null, // Not available null, // Not available - null)); // Not available + null, // Not available + settings.getType())); } } // Return the list diff --git a/lib/src/org/transdroid/daemon/BuffaloNas/BuffaloNasAdapter.java b/lib/src/org/transdroid/daemon/BuffaloNas/BuffaloNasAdapter.java index dbe6bea8..06db1af9 100644 --- a/lib/src/org/transdroid/daemon/BuffaloNas/BuffaloNasAdapter.java +++ b/lib/src/org/transdroid/daemon/BuffaloNas/BuffaloNasAdapter.java @@ -281,7 +281,8 @@ public class BuffaloNasAdapter implements IDaemonAdapter { null, null, null, - null)); + null, + settings.getType())); } // Return the list diff --git a/lib/src/org/transdroid/daemon/DLinkRouterBT/DLinkRouterBTAdapter.java b/lib/src/org/transdroid/daemon/DLinkRouterBT/DLinkRouterBTAdapter.java index 521333b7..b9eabbaf 100644 --- a/lib/src/org/transdroid/daemon/DLinkRouterBT/DLinkRouterBTAdapter.java +++ b/lib/src/org/transdroid/daemon/DLinkRouterBT/DLinkRouterBTAdapter.java @@ -376,7 +376,8 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { null, null, null, - null); + null, + settings.getType()); torrents.add(new_t); } diff --git a/lib/src/org/transdroid/daemon/Deluge/DelugeAdapter.java b/lib/src/org/transdroid/daemon/Deluge/DelugeAdapter.java index 8a7fa785..6ce49d45 100644 --- a/lib/src/org/transdroid/daemon/Deluge/DelugeAdapter.java +++ b/lib/src/org/transdroid/daemon/Deluge/DelugeAdapter.java @@ -593,8 +593,9 @@ public class DelugeAdapter implements IDaemonAdapter { 0f, // Not available tor.has(RPC_LABEL)? tor.getString(RPC_LABEL): null, tor.has(RPC_TIMEADDED)? new Date(tor.getInt(RPC_TIMEADDED) * 1000L): null, - null, - tor.getString(RPC_MESSAGE))); // Not available + null, // Not available + tor.getString(RPC_MESSAGE), + settings.getType())); } } diff --git a/lib/src/org/transdroid/daemon/Ktorrent/StatsParser.java b/lib/src/org/transdroid/daemon/Ktorrent/StatsParser.java index fb55a519..b6dd76f7 100644 --- a/lib/src/org/transdroid/daemon/Ktorrent/StatsParser.java +++ b/lib/src/org/transdroid/daemon/Ktorrent/StatsParser.java @@ -5,6 +5,7 @@ import java.io.Reader; import java.util.ArrayList; import java.util.List; +import org.transdroid.daemon.Daemon; import org.transdroid.daemon.DaemonException; import org.transdroid.daemon.Torrent; import org.transdroid.daemon.TorrentStatus; @@ -85,7 +86,8 @@ public class StatsParser { null, // Not supported in the web interface null, // Not supported in the web interface null, // Not supported in the web interface - null)); // Not supported in the web interface + null, // Not supported in the web interface + Daemon.KTorrent)); id++; // Stop/start/etc. requests are made by ID, which is the order number in the returned XML list :-S } else if (next == XmlPullParser.START_TAG && name.equals("torrent")){ diff --git a/lib/src/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java b/lib/src/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java index fb6a3fc2..c96e3f82 100644 --- a/lib/src/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java +++ b/lib/src/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java @@ -394,7 +394,8 @@ public class QbittorrentAdapter implements IDaemonAdapter { null, null, // Only available in /json/propertiesGeneral on a per-torrent basis, unfortunately null, - null)); + null, + settings.getType())); } // Return the list diff --git a/lib/src/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java b/lib/src/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java index 7e06af29..fb71300b 100644 --- a/lib/src/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java +++ b/lib/src/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java @@ -350,10 +350,11 @@ public class RtorrentAdapter implements IDaemonAdapter { (Long)info[10], // totalSize ((Long)info[8]).floatValue() / ((Long)info[10]).floatValue(), // partDone 0f, // TODO: Add availability data - label, // See remark on rTorrent/groups above + label, added, finished, - error)); + error, + settings.getType())); } else { @@ -379,10 +380,11 @@ public class RtorrentAdapter implements IDaemonAdapter { (Integer)info[10], // totalSize ((Integer)info[8]).floatValue() / ((Integer)info[10]).floatValue(), // partDone 0f, // TODO: Add availability data - label, // See remark on rTorrent/groups above + label, added, finished, - error)); + error, + settings.getType())); } } diff --git a/lib/src/org/transdroid/daemon/Tfb4rt/StatsParser.java b/lib/src/org/transdroid/daemon/Tfb4rt/StatsParser.java index af7f5cc7..31dc7ae7 100644 --- a/lib/src/org/transdroid/daemon/Tfb4rt/StatsParser.java +++ b/lib/src/org/transdroid/daemon/Tfb4rt/StatsParser.java @@ -5,6 +5,7 @@ import java.io.Reader; import java.util.ArrayList; import java.util.List; +import org.transdroid.daemon.Daemon; import org.transdroid.daemon.DaemonException; import org.transdroid.daemon.Torrent; import org.transdroid.daemon.TorrentStatus; @@ -80,7 +81,8 @@ public class StatsParser { null, // Not supported in the XML stats null, null, - null)); + null, + Daemon.Tfb4rt)); } else if (next == XmlPullParser.START_TAG && name.equals("transfer")){ diff --git a/lib/src/org/transdroid/daemon/Torrent.java b/lib/src/org/transdroid/daemon/Torrent.java index 2d5ec2a9..2125755d 100644 --- a/lib/src/org/transdroid/daemon/Torrent.java +++ b/lib/src/org/transdroid/daemon/Torrent.java @@ -55,6 +55,7 @@ public final class Torrent implements Parcelable, Comparable { final private Date dateAdded; final private Date dateDone; final private String error; + final private Daemon daemon; //public long getID() { return id; } //public String getHash() { return hash; } @@ -80,6 +81,7 @@ public final class Torrent implements Parcelable, Comparable { public Date getDateAdded() { return dateAdded; } public Date getDateDone() { return dateDone; } public String getError() { return error; } + public Daemon getDaemon() { return daemon; } private Torrent(Parcel in) { this.id = in.readLong(); @@ -108,12 +110,13 @@ public final class Torrent implements Parcelable, Comparable { 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 peersGettingFromUs, int peersSendingToUs, int peersConnected, int peersKnown, int eta, long downloadedEver, long uploadedEver, long totalSize, float partDone, float available, String label, - Date dateAdded, Date realDateDone, String error) { + Date dateAdded, Date realDateDone, String error, Daemon daemon) { this.id = id; this.hash = hash; this.name = name; @@ -149,6 +152,7 @@ public final class Torrent implements Parcelable, Comparable { this.dateDone = cal.getTime(); } this.error = error; + this.daemon = daemon; } /** @@ -296,6 +300,7 @@ public final class Torrent implements Parcelable, Comparable { dest.writeLong((dateAdded == null)? -1: dateAdded.getTime()); dest.writeLong((dateDone == null)? -1: dateDone.getTime()); dest.writeString(error); + dest.writeString(daemon.name()); } } diff --git a/lib/src/org/transdroid/daemon/Transmission/TransmissionAdapter.java b/lib/src/org/transdroid/daemon/Transmission/TransmissionAdapter.java index aa4bb4e1..0aeb6d1b 100644 --- a/lib/src/org/transdroid/daemon/Transmission/TransmissionAdapter.java +++ b/lib/src/org/transdroid/daemon/Transmission/TransmissionAdapter.java @@ -500,7 +500,8 @@ public class TransmissionAdapter implements IDaemonAdapter { null, // No label/category/group support in the RPC API for now new Date(tor.getLong(RPC_DATEADDED) * 1000L), new Date(tor.getLong(RPC_DATEDONE) * 1000L), - errorString)); + errorString, + settings.getType())); } // Return the list diff --git a/lib/src/org/transdroid/daemon/Utorrent/UtorrentAdapter.java b/lib/src/org/transdroid/daemon/Utorrent/UtorrentAdapter.java index 40b9214b..982fa079 100644 --- a/lib/src/org/transdroid/daemon/Utorrent/UtorrentAdapter.java +++ b/lib/src/org/transdroid/daemon/Utorrent/UtorrentAdapter.java @@ -515,7 +515,8 @@ public class UtorrentAdapter implements IDaemonAdapter { addedOnDate, completedOnDate, // uTorrent doesn't give the error message, so just remind that there is some error - status == TorrentStatus.Error? "See GUI for error message": null)); + status == TorrentStatus.Error? "See GUI for error message": null, + settings.getType())); } return torrents; diff --git a/lib/src/org/transdroid/daemon/Vuze/VuzeAdapter.java b/lib/src/org/transdroid/daemon/Vuze/VuzeAdapter.java index b19ca94d..ba55e981 100644 --- a/lib/src/org/transdroid/daemon/Vuze/VuzeAdapter.java +++ b/lib/src/org/transdroid/daemon/Vuze/VuzeAdapter.java @@ -31,6 +31,7 @@ import java.util.Map; import org.apache.openjpa.lib.util.Base16Encoder; import org.transdroid.daemon.Daemon; import org.transdroid.daemon.DaemonException; +import org.transdroid.daemon.DaemonException.ExceptionType; import org.transdroid.daemon.DaemonMethod; import org.transdroid.daemon.DaemonSettings; import org.transdroid.daemon.IDaemonAdapter; @@ -38,7 +39,6 @@ import org.transdroid.daemon.Priority; import org.transdroid.daemon.Torrent; import org.transdroid.daemon.TorrentFile; import org.transdroid.daemon.TorrentStatus; -import org.transdroid.daemon.DaemonException.ExceptionType; import org.transdroid.daemon.task.AddByFileTask; import org.transdroid.daemon.task.AddByUrlTask; import org.transdroid.daemon.task.DaemonTask; @@ -99,18 +99,27 @@ public class VuzeAdapter implements IDaemonAdapter { case AddByFile: byte[] bytes; + FileInputStream in = null; try { // Request to add a torrent by local .torrent file String file = ((AddByFileTask)task).getFile(); - FileInputStream in = new FileInputStream(new File(URI.create(file))); + in = new FileInputStream(new File(URI.create(file))); bytes = new byte[in.available()]; in.read(bytes, 0, in.available()); + in.close(); } catch (FileNotFoundException e) { return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.FileAccessError, e.toString())); } catch (IllegalArgumentException e) { return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.FileAccessError, "Invalid local URI")); } catch (Exception e) { return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.FileAccessError, e.toString())); + } finally { + try { + if (in != null) + in.close(); + } catch (IOException e) { + // Ignore; it was already closed or never opened + } } makeVuzeCall(DaemonMethod.AddByFile, "createFromBEncodedData[byte[]]", new String[] { Base16Encoder.encode(bytes) }); return new DaemonTaskSuccessResult(task); @@ -406,7 +415,8 @@ public class VuzeAdapter implements IDaemonAdapter { null, // TODO: Implement Vuze label support new Date((Long) statsinfo.get("time_started")), // dateAdded null, // Unsupported? - error)); + error, + settings.getType())); } diff --git a/lib/src/org/transdroid/daemon/util/FileSizeConverter.java b/lib/src/org/transdroid/daemon/util/FileSizeConverter.java index a31c9966..13f18e38 100644 --- a/lib/src/org/transdroid/daemon/util/FileSizeConverter.java +++ b/lib/src/org/transdroid/daemon/util/FileSizeConverter.java @@ -15,71 +15,99 @@ * along with Transdroid. If not, see . * */ - package org.transdroid.daemon.util; +package org.transdroid.daemon.util; /** * Quick and dirty file size formatter. - * * @author erickok - * */ public class FileSizeConverter { private static final String DECIMAL_FORMATTER = "%.1f"; - + /** * A quantity in which to express a file size. - * * @author erickok - * */ public enum SizeUnit { - B, - KB, - MB, - GB + B, KB, MB, GB } - + private static int INC_SIZE = 1024; - - // Returns a file size given in bytes to a different unit, as a formatted string - public static String getSize(long from, SizeUnit to) - { + + /** + * Returns a file size (in bytes) in a different unit, as a formatted string + * @param from The file size in bytes + * @param to The unit to convert to + * @return A formatted string with number (rounded to one decimal) and unit, e.g. 1177.4MB + */ + public static String getSize(long from, SizeUnit to) { String out; switch (to) { case B: out = String.valueOf(from); break; case KB: - out = String.format(DECIMAL_FORMATTER, ((double)from) / 1024); + out = String.format(DECIMAL_FORMATTER, ((double) from) / INC_SIZE); break; case MB: - out = String.format(DECIMAL_FORMATTER, ((double)from) / 1024 / 1024); + out = String.format(DECIMAL_FORMATTER, ((double) from) / INC_SIZE / INC_SIZE); break; default: - out = String.format(DECIMAL_FORMATTER, ((double)from) / 1024 / 1024 / 1024); + out = String.format(DECIMAL_FORMATTER, ((double) from) / INC_SIZE / INC_SIZE / INC_SIZE); break; } - - return (out + " " + to.toString()); + + return (out + " " + to.toString()); } - // Returns a file size in bytes in a nice readable formatted string + /** + * Returns a file size as nice readable string, with unit, e.g. 1234567890 (bytes) returns 1,15GB + * @param from The file size in bytes + * @return A formatted string with number (rounded to one decimal), with unit text + */ public static String getSize(long from) { return getSize(from, true); } // Returns a file size in bytes in a nice readable formatted string + /** + * Returns a file size as nice readable string, e.g. 1234567890 (bytes) returns 1,15 or 1,15GB + * @param from The file size in bytes + * @param withUnit Whether to also append the appropriate unit (B, KB, MB, GB) as text + * @return A formatted string with number (rounded to one decimal) and optionally unit + */ public static String getSize(long from, boolean withUnit) { if (from < INC_SIZE) { - return String.valueOf(from) + (withUnit? SizeUnit.B.toString(): ""); - } else if (from < (INC_SIZE * INC_SIZE)) { - return String.format(DECIMAL_FORMATTER, ((double)from) / INC_SIZE) + (withUnit? SizeUnit.KB.toString(): ""); + return String.valueOf(from) + (withUnit ? SizeUnit.B.toString() : ""); + } else if (from < (INC_SIZE * INC_SIZE)) { + return String.format(DECIMAL_FORMATTER, ((double) from) / INC_SIZE) + + (withUnit ? SizeUnit.KB.toString() : ""); } else if (from < (INC_SIZE * INC_SIZE * INC_SIZE)) { - return String.format(DECIMAL_FORMATTER, ((double)from) / INC_SIZE / INC_SIZE) + (withUnit? SizeUnit.MB.toString(): ""); - } else { - return String.format(DECIMAL_FORMATTER, ((double)from) / INC_SIZE / INC_SIZE / INC_SIZE) + (withUnit? SizeUnit.GB.toString(): ""); + return String.format(DECIMAL_FORMATTER, ((double) from) / INC_SIZE / INC_SIZE) + + (withUnit ? SizeUnit.MB.toString() : ""); + } else { + return String.format(DECIMAL_FORMATTER, ((double) from) / INC_SIZE / INC_SIZE / INC_SIZE) + + (withUnit ? SizeUnit.GB.toString() : ""); } } - + + /** + * Returns the unit to display some file size (as returned by getSize(long)) in, e.g. 1234567890 (bytes) returns GB + * as it is 1.2GB big + * @param from The file size in bytes + * @return The unit, i.e. B, KB, MB or GB + */ + public static SizeUnit getSizeUnit(long from) { + if (from < INC_SIZE) { + return SizeUnit.B; + } else if (from < (INC_SIZE * INC_SIZE)) { + return SizeUnit.KB; + } else if (from < (INC_SIZE * INC_SIZE * INC_SIZE)) { + return SizeUnit.MB; + } else { + return SizeUnit.GB; + } + } + } diff --git a/lite/.classpath b/lite/.classpath index 350f40f7..08d23ddb 100644 --- a/lite/.classpath +++ b/lite/.classpath @@ -9,6 +9,6 @@ - + diff --git a/lite/AndroidManifest.xml b/lite/AndroidManifest.xml index e33579dd..90b10a33 100644 --- a/lite/AndroidManifest.xml +++ b/lite/AndroidManifest.xml @@ -17,12 +17,13 @@ + + @@ -30,12 +31,59 @@ + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lite/res/layout-w600dp/activity_torrents.xml b/lite/res/layout-w600dp/activity_torrents.xml index 2f96106f..da228be0 100644 --- a/lite/res/layout-w600dp/activity_torrents.xml +++ b/lite/res/layout-w600dp/activity_torrents.xml @@ -21,7 +21,7 @@ android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="3" - class="org.transdroid.gui.TorrentsFragment" + class="org.transdroid.core.gui.TorrentsFragment_" tools:layout="@layout/fragment_torrents" /> \ No newline at end of file diff --git a/lite/res/layout-w720dp/activity_torrents.xml b/lite/res/layout-w720dp/activity_torrents.xml index 6575c30e..dfdbcad1 100644 --- a/lite/res/layout-w720dp/activity_torrents.xml +++ b/lite/res/layout-w720dp/activity_torrents.xml @@ -21,7 +21,7 @@ android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="2" - class="org.transdroid.gui.TorrentsFragment" + class="org.transdroid.core.gui.TorrentsFragment_" tools:layout="@layout/fragment_torrents" /> \ No newline at end of file diff --git a/lite/res/layout/activity_details.xml b/lite/res/layout/activity_details.xml new file mode 100644 index 00000000..f1d8b7c8 --- /dev/null +++ b/lite/res/layout/activity_details.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/lite/res/layout/activity_torrents.xml b/lite/res/layout/activity_torrents.xml index ee4dd9b6..feb3cba7 100644 --- a/lite/res/layout/activity_torrents.xml +++ b/lite/res/layout/activity_torrents.xml @@ -10,6 +10,7 @@ android:id="@+id/torrent_list" android:layout_width="match_parent" android:layout_height="match_parent" - class="org.transdroid.gui.TorrentsFragment" tools:layout="@layout/fragment_torrents"/> + class="org.transdroid.core.gui.TorrentsFragment_" + tools:layout="@layout/fragment_torrents" /> \ No newline at end of file diff --git a/lite/res/layout/fragment_details.xml b/lite/res/layout/fragment_details.xml index bbbcdb0a..245e60fc 100644 --- a/lite/res/layout/fragment_details.xml +++ b/lite/res/layout/fragment_details.xml @@ -19,6 +19,7 @@ android:drawableTop="@drawable/ic_empty_details" android:drawablePadding="8dip" android:text="@string/navigation_emptydetails" + android:gravity="center" android:textIsSelectable="false" android:visibility="gone" /> diff --git a/lite/res/layout/fragment_details_header.xml b/lite/res/layout/fragment_details_header.xml index 56baea74..141517d2 100644 --- a/lite/res/layout/fragment_details_header.xml +++ b/lite/res/layout/fragment_details_header.xml @@ -9,9 +9,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" - android:text="UNLABELED" android:textIsSelectable="true" - style="@style/LabelTextView" /> + style="@style/LabelTextView" + android:visibility="invisible" /> @@ -65,7 +62,6 @@ android:layout_below="@id/uploaded_text" android:layout_toRightOf="@id/separator" android:layout_marginLeft="@dimen/margin_half" - android:text="RATIO 18.7" android:textSize="12sp" android:textIsSelectable="false" /> @@ -77,7 +73,6 @@ android:layout_toRightOf="@id/separator" android:layout_marginLeft="@dimen/margin_half" android:layout_marginBottom="4dip" - android:text="↑1.4MB/s" android:textSize="22sp" android:textColor="#fff" android:fontFamily="sans-serif-light" @@ -90,18 +85,16 @@ android:layout_below="@id/upspeed_text" android:layout_toRightOf="@id/separator" android:layout_marginLeft="@dimen/margin_half" - android:text="56 (1208) SEEDERS" android:textSize="12sp" android:textIsSelectable="false" /> @@ -110,10 +103,9 @@ android:id="@+id/downloaded_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignBaseline="@id/uploaded_unit" - android:layout_toLeftOf="@id/downloaded_unit" + android:layout_alignBaseline="@id/uploadedunit_text" + android:layout_toLeftOf="@id/downloadedunit_text" android:layout_marginRight="4dip" - android:text="325.6" android:textColor="#fff" android:textSize="33sp" android:fontFamily="sans-serif-light" @@ -126,7 +118,6 @@ android:layout_alignBaseline="@id/ratio_text" android:layout_toLeftOf="@id/separator" android:layout_marginRight="@dimen/margin_half" - android:text="/ 388.8MB" android:textSize="12sp" android:textIsSelectable="false" /> @@ -137,7 +128,6 @@ android:layout_alignBaseline="@id/upspeed_text" android:layout_toLeftOf="@id/separator" android:layout_marginRight="@dimen/margin_half" - android:text="400.8KB/s↓" android:textSize="22sp" android:textColor="#fff" android:fontFamily="sans-serif-light" @@ -150,7 +140,6 @@ android:layout_alignBaseline="@id/seeders_text" android:layout_toLeftOf="@id/separator" android:layout_marginRight="@dimen/margin_half" - android:text="128 (128) LEECHERS" android:textSize="12sp" android:textIsSelectable="false" /> @@ -160,7 +149,6 @@ android:layout_height="wrap_content" android:layout_below="@id/separator" android:layout_marginBottom="@dimen/margin_half" - android:text="DOWNLADING (80%): ETA 38 SECONDS" android:textIsSelectable="false" /> \ No newline at end of file diff --git a/lite/res/layout/fragment_torrents.xml b/lite/res/layout/fragment_torrents.xml index 65ae6c53..ae070955 100644 --- a/lite/res/layout/fragment_torrents.xml +++ b/lite/res/layout/fragment_torrents.xml @@ -19,6 +19,7 @@ android:drawableTop="@drawable/ic_empty_details" android:drawablePadding="8dip" android:text="@string/navigation_emptytorrents" + android:gravity="center" android:textIsSelectable="false" android:visibility="gone" /> diff --git a/lite/res/layout/list_item_torrent.xml b/lite/res/layout/list_item_torrent.xml index 4041e535..7f4d8a0f 100644 --- a/lite/res/layout/list_item_torrent.xml +++ b/lite/res/layout/list_item_torrent.xml @@ -15,7 +15,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="@dimen/margin_half" android:layout_marginTop="@dimen/margin_half" - android:src="@drawable/ic_priority_normal" /> + android:src="@drawable/ic_priority_normal" + android:contentDescription="@string/status_priority_normal" /> @@ -52,14 +51,14 @@ android:layout_alignBaseline="@id/ratio_text" android:layout_toLeftOf="@id/ratio_text" android:layout_toRightOf="@id/torrent_checkbox" - android:text="325.6MB OF 388.8MB (0%)" android:textSize="12sp" android:textIsSelectable="false" /> - @@ -71,7 +70,6 @@ android:layout_below="@id/torrent_progressbar" android:layout_alignParentRight="true" android:layout_marginLeft="@dimen/margin_half" - android:text="↓400.8KB/s ↑1.4MB/s" android:textSize="12sp" android:textIsSelectable="false" /> @@ -82,7 +80,6 @@ android:layout_alignBaseline="@id/speed_text" android:layout_toLeftOf="@id/speed_text" android:layout_toRightOf="@id/torrent_checkbox" - android:text="246 (288) LEECHERS" android:textSize="12sp" android:textIsSelectable="false" /> diff --git a/lite/res/layout/list_item_torrentfile.xml b/lite/res/layout/list_item_torrentfile.xml new file mode 100644 index 00000000..01562e78 --- /dev/null +++ b/lite/res/layout/list_item_torrentfile.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + diff --git a/lite/res/menu/activity_details.xml b/lite/res/menu/activity_details.xml new file mode 100644 index 00000000..d9ddd500 --- /dev/null +++ b/lite/res/menu/activity_details.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/lite/res/values/strings.xml b/lite/res/values/strings.xml index 780a7fcf..22fba165 100644 --- a/lite/res/values/strings.xml +++ b/lite/res/values/strings.xml @@ -45,9 +45,46 @@ Uploading Active Inactive - Connected, but no torrent are active on the server + Connected, but no torrent are active within the current filter Select a torrent to view its details + Waiting to check… + Verifying local data… + Waiting to download %s + Error… + %1$s OF %2$s (%3$s) + %1$s, UPLOADED %2$s + SINCE $s + ~ %1$s + ETA %1$s + UNKNOWN ETA + RATIO %1$s + %1$s OF %2$s PEERS + ↑ %1$s + ↓ %1$s + Downloading + Seeding + Paused + Queued + Stopped + Unknown status + Not downloaded + Low priority + Normal priority + High priority + TRACKERS + ERRORS + FILES + + All labels + Unlabeled + New label + Setting a label is not supported by your client + + Torrent search + Search for torrents + + Servers Add new server Search sites @@ -120,6 +157,26 @@ Get support or report a bug About + Error during communication; check your connection + Internal error building request + Error parsing server response (please check your settings) + Web interface not connected to a running daemon + Access denied (please check your settings) + Can\'t read .torrent file + Error while parsing the RSS feed + This URL is not well-formed + Your web search URL is invalid: + Input is not a valid IP address or host name + Port number is always numeric + Directory paths end with a / or \ + Timeout can not be empty and is a positive number + The RSS feed item didn\'t provide an URL enclosure or link tag pointing to the .torrent file + The RSS feed item does not provide a link to browse to + URL is not a (valid) RSS feed + SD card not available to read/write + File does not seem to contain Transdroid settings + There is no settings file found + BitComet Bitflu 1.2+ diff --git a/lite/res/xml/searchable.xml b/lite/res/xml/searchable.xml new file mode 100644 index 00000000..9f811c8c --- /dev/null +++ b/lite/res/xml/searchable.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/lite/src/org/transdroid/lite/app/search/SearchHelper.java b/lite/src/org/transdroid/core/app/search/SearchHelper.java similarity index 99% rename from lite/src/org/transdroid/lite/app/search/SearchHelper.java rename to lite/src/org/transdroid/core/app/search/SearchHelper.java index 11244a19..b72cb7b8 100644 --- a/lite/src/org/transdroid/lite/app/search/SearchHelper.java +++ b/lite/src/org/transdroid/core/app/search/SearchHelper.java @@ -1,4 +1,4 @@ -package org.transdroid.lite.app.search; +package org.transdroid.core.app.search; import java.util.ArrayList; import java.util.List; diff --git a/lite/src/org/transdroid/lite/app/search/SearchResult.java b/lite/src/org/transdroid/core/app/search/SearchResult.java similarity index 96% rename from lite/src/org/transdroid/lite/app/search/SearchResult.java rename to lite/src/org/transdroid/core/app/search/SearchResult.java index 77594909..6bbfaa76 100644 --- a/lite/src/org/transdroid/lite/app/search/SearchResult.java +++ b/lite/src/org/transdroid/core/app/search/SearchResult.java @@ -1,4 +1,4 @@ -package org.transdroid.lite.app.search; +package org.transdroid.core.app.search; import java.util.Date; diff --git a/lite/src/org/transdroid/lite/app/search/SearchSite.java b/lite/src/org/transdroid/core/app/search/SearchSite.java similarity index 80% rename from lite/src/org/transdroid/lite/app/search/SearchSite.java rename to lite/src/org/transdroid/core/app/search/SearchSite.java index 2d7910df..a30ad09e 100644 --- a/lite/src/org/transdroid/lite/app/search/SearchSite.java +++ b/lite/src/org/transdroid/core/app/search/SearchSite.java @@ -1,12 +1,12 @@ -package org.transdroid.lite.app.search; +package org.transdroid.core.app.search; -import org.transdroid.lite.gui.navigation.FilterItem; +import org.transdroid.core.gui.lists.SimpleListItem; /** * Represents an available torrent site that can be searched using the Torrent Search package. * @author Eric Kok */ -public class SearchSite implements FilterItem { +public class SearchSite implements SimpleListItem { private final int id; private final String key; diff --git a/lite/src/org/transdroid/lite/app/settings/AboutSettings.java b/lite/src/org/transdroid/core/app/settings/AboutSettings.java similarity index 94% rename from lite/src/org/transdroid/lite/app/settings/AboutSettings.java rename to lite/src/org/transdroid/core/app/settings/AboutSettings.java index 9dd745c7..577f9fc9 100644 --- a/lite/src/org/transdroid/lite/app/settings/AboutSettings.java +++ b/lite/src/org/transdroid/core/app/settings/AboutSettings.java @@ -1,4 +1,4 @@ -package org.transdroid.lite.app.settings; +package org.transdroid.core.app.settings; import org.androidannotations.annotations.EBean; import org.androidannotations.annotations.RootContext; diff --git a/lite/src/org/transdroid/lite/app/settings/ApplicationSettings.java b/lite/src/org/transdroid/core/app/settings/ApplicationSettings.java similarity index 72% rename from lite/src/org/transdroid/lite/app/settings/ApplicationSettings.java rename to lite/src/org/transdroid/core/app/settings/ApplicationSettings.java index 75a4c09e..7f6393d5 100644 --- a/lite/src/org/transdroid/lite/app/settings/ApplicationSettings.java +++ b/lite/src/org/transdroid/core/app/settings/ApplicationSettings.java @@ -1,4 +1,4 @@ -package org.transdroid.lite.app.settings; +package org.transdroid.core.app.settings; import java.util.ArrayList; import java.util.List; @@ -52,7 +52,8 @@ public class ApplicationSettings { } /** - * Returns the user-specified server settings for a specific server + * Returns the user-specified server settings for a specific server. WARNING: This method does not check if the + * settings actually exist and may rely on empty defaults if called not a non-existing server. * @param order The order number/identifying key of the settings to retrieve * @return The server settings object, loaded from shared preferences */ @@ -71,6 +72,53 @@ public class ApplicationSettings { prefs.getBoolean("server_alarmnew_" + order, false), false); } + /** + * Returns the settings of the server that was last used by the user. As opposed to getLastUsedServerKey(int), this + * method checks whether a server was already registered as being last used and check whether the server still + * exists. It returns the first server if that fails. If no servers are configured, null is returned. + * @return A server settings object of the last used server (or, if not known, the first server), or null if no + * servers exist + */ + public ServerSetting getLastUsedServer() { + int max = getMaxServer(); // Zero-based index, so with max == 0 there is 1 server + if (max < 0) { + // No servers configured + return null; + } + int last = getLastUsedServerKey(); + if (last < 0 || last > max) { + // Last server was never set or no longer exists + return getServerSetting(0); + } + return getServerSetting(last); + } + + /** + * Returns the order number/unique key of the server that the used last used; use with getServerSettings(int) or + * call getLastUsedServer directly. WARNING: the returned integer may no longer refer to a known server settings + * object: check the bounds. + * @return An integer indicating the order number/key or the last used server, or -1 if it was not set + */ + public int getLastUsedServerKey() { + return prefs.getInt("system_lastusedserver", -1); + } + + /** + * Registers some server as being the last used by the user + * @param server The settings of the server that the user last used + */ + public void setLastUsedServer(ServerSetting server) { + setLastUsedServerKey(server.getOrder()); + } + + /** + * Registers the order number/unique key of some server as being last used by the user + * @param order The key identifying the specific server + */ + public void setLastUsedServerKey(int order) { + prefs.edit().putInt("system_lastusedserver", order).commit(); + } + /** * Returns all available user-configured web-based (as opped to in-app) search sites * @return A list of all stored web search site settings objects diff --git a/lite/src/org/transdroid/lite/app/settings/NotificationSettings.java b/lite/src/org/transdroid/core/app/settings/NotificationSettings.java similarity index 98% rename from lite/src/org/transdroid/lite/app/settings/NotificationSettings.java rename to lite/src/org/transdroid/core/app/settings/NotificationSettings.java index 1a68aabd..58bd4003 100644 --- a/lite/src/org/transdroid/lite/app/settings/NotificationSettings.java +++ b/lite/src/org/transdroid/core/app/settings/NotificationSettings.java @@ -1,4 +1,4 @@ -package org.transdroid.lite.app.settings; +package org.transdroid.core.app.settings; import org.androidannotations.annotations.EBean; import org.androidannotations.annotations.EBean.Scope; diff --git a/lite/src/org/transdroid/lite/app/settings/RssfeedSetting.java b/lite/src/org/transdroid/core/app/settings/RssfeedSetting.java similarity index 89% rename from lite/src/org/transdroid/lite/app/settings/RssfeedSetting.java rename to lite/src/org/transdroid/core/app/settings/RssfeedSetting.java index 69ee3823..b964cfb2 100644 --- a/lite/src/org/transdroid/lite/app/settings/RssfeedSetting.java +++ b/lite/src/org/transdroid/core/app/settings/RssfeedSetting.java @@ -1,6 +1,6 @@ -package org.transdroid.lite.app.settings; +package org.transdroid.core.app.settings; -import org.transdroid.lite.gui.navigation.FilterItem; +import org.transdroid.core.gui.lists.SimpleListItem; import android.net.Uri; import android.text.TextUtils; @@ -9,7 +9,7 @@ import android.text.TextUtils; * Represents a user-specified RSS feed. * @author Eric Kok */ -public class RssfeedSetting implements FilterItem { +public class RssfeedSetting implements SimpleListItem { private static final String DEFAULT_NAME = "Default"; diff --git a/lite/src/org/transdroid/lite/app/settings/ServerSetting.java b/lite/src/org/transdroid/core/app/settings/ServerSetting.java similarity index 78% rename from lite/src/org/transdroid/lite/app/settings/ServerSetting.java rename to lite/src/org/transdroid/core/app/settings/ServerSetting.java index 164be4ad..3070c730 100644 --- a/lite/src/org/transdroid/lite/app/settings/ServerSetting.java +++ b/lite/src/org/transdroid/core/app/settings/ServerSetting.java @@ -1,8 +1,10 @@ -package org.transdroid.lite.app.settings; +package org.transdroid.core.app.settings; +import org.transdroid.core.gui.lists.SimpleListItem; import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.DaemonSettings; +import org.transdroid.daemon.IDaemonAdapter; import org.transdroid.daemon.OS; -import org.transdroid.lite.gui.navigation.FilterItem; import android.text.TextUtils; @@ -10,7 +12,7 @@ import android.text.TextUtils; * Represents a user-configured remote server. * @author Eric Kok */ -public class ServerSetting implements FilterItem { +public class ServerSetting implements SimpleListItem { private static final String DEFAULT_NAME = "Default"; @@ -192,4 +194,29 @@ public class ServerSetting implements FilterItem { + (Daemon.supportsCustomFolder(getType()) && getFolder() != null ? getFolder() : ""); } + @Override + public boolean equals(Object o) { + if (o instanceof ServerSetting) { + // Directly compare order numbers/unique keys + return ((ServerSetting) o).getOrder() == this.key; + } else if (o instanceof DaemonSettings) { + // Old-style DaemonSettings objects can be equal if they were constructed from a ServerSettings object: + // idString should reflect the local key/order + return ((DaemonSettings) o).getIdString().equals(Integer.toString(this.key)); + } + // Other objects are never equal to this + return false; + } + + public IDaemonAdapter createServerAdapter() { + // Convert local server settings into an old-style DaemonSetting object + // The local integer key is converted to the idString string + // TODO: Add localaddress and localnetwork to DaemonSettings, or solve properly rework the Connect library + // handling of settings + DaemonSettings daemonSettings = new DaemonSettings(name, type, address, port, ssl, sslTrustAll, sslTrustKey, + folder, useAuthentication, username, password, extraPass, os, downloadDir, ftpUrl, ftpPassword, + timeout, alarmOnFinishedDownload, alarmOnNewTorrent, Integer.toString(key), isAutoGenerated); + return type.createAdapter(daemonSettings); + } + } diff --git a/lite/src/org/transdroid/lite/app/settings/WebsearchSetting.java b/lite/src/org/transdroid/core/app/settings/WebsearchSetting.java similarity index 85% rename from lite/src/org/transdroid/lite/app/settings/WebsearchSetting.java rename to lite/src/org/transdroid/core/app/settings/WebsearchSetting.java index b4da7aed..d278544a 100644 --- a/lite/src/org/transdroid/lite/app/settings/WebsearchSetting.java +++ b/lite/src/org/transdroid/core/app/settings/WebsearchSetting.java @@ -1,6 +1,6 @@ -package org.transdroid.lite.app.settings; +package org.transdroid.core.app.settings; -import org.transdroid.lite.gui.navigation.FilterItem; +import org.transdroid.core.gui.lists.SimpleListItem; import android.net.Uri; import android.text.TextUtils; @@ -9,7 +9,7 @@ import android.text.TextUtils; * Represents a user-specified website that can be searched (by starting the browser, rather than in-app) * @author Eric Kok */ -public class WebsearchSetting implements FilterItem { +public class WebsearchSetting implements SimpleListItem { private static final String DEFAULT_NAME = "Default"; private static final String KEY_PREFIX = "websearch_"; diff --git a/lite/src/org/transdroid/core/gui/DetailsActivity.java b/lite/src/org/transdroid/core/gui/DetailsActivity.java new file mode 100644 index 00000000..5e57cfa3 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/DetailsActivity.java @@ -0,0 +1,132 @@ +package org.transdroid.core.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.AfterViews; +import org.androidannotations.annotations.Background; +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EActivity; +import org.androidannotations.annotations.Extra; +import org.androidannotations.annotations.FragmentById; +import org.androidannotations.annotations.InstanceState; +import org.androidannotations.annotations.OptionsItem; +import org.androidannotations.annotations.OptionsMenu; +import org.androidannotations.annotations.UiThread; +import org.transdroid.core.R; +import org.transdroid.core.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.ServerSetting; +import org.transdroid.core.gui.lists.LocalTorrent; +import org.transdroid.daemon.IDaemonAdapter; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentDetails; +import org.transdroid.daemon.TorrentFile; +import org.transdroid.daemon.task.DaemonTaskFailureResult; +import org.transdroid.daemon.task.DaemonTaskResult; +import org.transdroid.daemon.task.GetFileListTask; +import org.transdroid.daemon.task.GetFileListTaskSuccessResult; +import org.transdroid.daemon.task.GetTorrentDetailsTask; +import org.transdroid.daemon.task.GetTorrentDetailsTaskSuccessResult; +import org.transdroid.daemon.task.RetrieveTask; +import org.transdroid.daemon.task.RetrieveTaskSuccessResult; + +import android.widget.Toast; + +import com.actionbarsherlock.app.SherlockFragmentActivity; + +@EActivity(R.layout.activity_details) +@OptionsMenu(R.menu.activity_details) +public class DetailsActivity extends SherlockFragmentActivity { + + @Extra + @InstanceState + protected Torrent torrent; + + // Settings + @Bean + protected ApplicationSettings applicationSettings; + private IDaemonAdapter currentConnection = null; + + // Details view components + @FragmentById(R.id.torrent_details) + protected DetailsFagment fragmentDetails; + + @AfterViews + protected void init() { + + // Simple action bar with up, torrent name as title and refresh button + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(torrent.getName()); + + // Connect to the last used server + ServerSetting lastUsed = applicationSettings.getLastUsedServer(); + currentConnection = lastUsed.createServerAdapter(); + + // Load fine details and torrent files + refreshTorrentDetails(); + + } + + @OptionsItem(R.id.action_refresh) + protected void refreshScreen() { + refreshTorrent(); + refreshTorrentDetails(); + refreshTorrentFiles(); + } + + @Background + protected void refreshTorrent() { + DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(); + if (result instanceof RetrieveTaskSuccessResult) { + onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels()); + } else { + onCommunicationError((DaemonTaskFailureResult)result); + } + } + + @UiThread + protected void onTorrentsRetrieved(List torrents, List labels) { + // Update the details fragment + fragmentDetails.perhapsUpdateTorrent(torrents); + } + + @Background + protected void refreshTorrentDetails() { + DaemonTaskResult result = GetTorrentDetailsTask.create(currentConnection, torrent).execute(); + if (result instanceof GetTorrentDetailsTaskSuccessResult) { + onTorrentDetailsRetrieved(((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails()); + } else { + onCommunicationError((DaemonTaskFailureResult)result); + } + } + + @UiThread + protected void onTorrentDetailsRetrieved(TorrentDetails torrentDetails) { + // Update the details fragment with the new fine details for the shown torrent + fragmentDetails.updateTorrentDetails(torrentDetails); + } + + @Background + protected void refreshTorrentFiles() { + DaemonTaskResult result = GetFileListTask.create(currentConnection, torrent).execute(); + if (result instanceof GetFileListTaskSuccessResult) { + onTorrentFilesRetrieved(((GetFileListTaskSuccessResult) result).getFiles()); + } else { + onCommunicationError((DaemonTaskFailureResult)result); + } + } + + @UiThread + protected void onTorrentFilesRetrieved(List torrentFiles) { + // Update the details fragment with the newly retrieved list of files + fragmentDetails.updateTorrentFiles(new ArrayList(torrentFiles)); + } + + @UiThread + protected void onCommunicationError(DaemonTaskFailureResult result) { + // TODO: Properly report this error + Toast.makeText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), + Toast.LENGTH_LONG).show(); + } + +} diff --git a/lite/src/org/transdroid/core/gui/DetailsFagment.java b/lite/src/org/transdroid/core/gui/DetailsFagment.java new file mode 100644 index 00000000..f05a8a3b --- /dev/null +++ b/lite/src/org/transdroid/core/gui/DetailsFagment.java @@ -0,0 +1,113 @@ +package org.transdroid.core.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.AfterViews; +import org.androidannotations.annotations.EFragment; +import org.androidannotations.annotations.InstanceState; +import org.androidannotations.annotations.ViewById; +import org.transdroid.core.R; +import org.transdroid.core.gui.lists.DetailsAdapter; +import org.transdroid.core.gui.lists.SimpleListItemAdapter; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentDetails; +import org.transdroid.daemon.TorrentFile; + +import android.widget.TextView; + +import com.actionbarsherlock.app.SherlockFragment; +import com.actionbarsherlock.view.SherlockListView; + +/** + * Fragment that shown detailed statistics about some torrent. These come from some already fetched {@link Torrent} + * object, but it also retrieves further detailed statistics. + * @author Eric Kok + */ +@EFragment(R.layout.fragment_details) +public class DetailsFagment extends SherlockFragment { + + // Local data + @InstanceState + protected Torrent torrent = null; + @InstanceState + protected TorrentDetails torrentDetails = null; + @InstanceState + protected ArrayList torrentFiles = null; + + // Views + @ViewById(R.id.details_list) + protected SherlockListView detailsList; + @ViewById + protected TextView emptyText; + + @AfterViews + protected void init() { + + detailsList.setAdapter(new DetailsAdapter()); + detailsList.setEmptyView(emptyText); // Shows a text that no torrent was selected yet + if (torrent != null) + updateTorrent(torrent); + if (torrentDetails != null) + updateTorrentDetails(torrentDetails); + if (torrentFiles != null) + updateTorrentFiles(torrentFiles); + + } + + /** + * Updates the details adapter header to show the new torrent data + * @param newTorrent The new torrent object + */ + public void updateTorrent(Torrent newTorrent) { + this.torrent = newTorrent; + ((DetailsAdapter) detailsList.getAdapter()).updateTorrent(newTorrent); + } + + /** + * Updates the details adapter to show the list of trackers and tracker errors + * @param newTorrentDetails The new fine details object of some torrent + */ + public void updateTorrentDetails(TorrentDetails newTorrentDetails) { + this.torrentDetails = newTorrentDetails; + ((DetailsAdapter) detailsList.getAdapter()).updateTrackers(SimpleListItemAdapter.SimpleStringItem + .wrapStringsList(newTorrentDetails.getTrackers())); + ((DetailsAdapter) detailsList.getAdapter()).updateErrors(SimpleListItemAdapter.SimpleStringItem + .wrapStringsList(newTorrentDetails.getErrors())); + } + + /** + * Updates the list adapter to show a new list of torrent files, replacing the old files list + * @param newTorrents The new, updated list of torrent file objects + */ + public void updateTorrentFiles(ArrayList newTorrentFiles) { + this.torrentFiles = newTorrentFiles; + ((DetailsAdapter) detailsList.getAdapter()).updateTorrentFiles(newTorrentFiles); + } + + /** + * Can be called if some outside activity returned new torrents, so we can perhaps piggyback on this by update our + * data as well + * @param torrents The last of retrieved torrents + */ + public void perhapsUpdateTorrent(List torrents) { + for (Torrent newTorrent : torrents) { + if (newTorrent.getUniqueID().equals(this.torrent.getUniqueID())) { + // Found, so we can update our data as well + updateTorrent(newTorrent); + break; + } + } + } + + /** + * Clear the screen by fully clearing the internal merge list (with header and other lists) + */ + public void clear() { + detailsList.setAdapter(new DetailsAdapter()); + torrent = null; + torrentDetails = null; + torrentFiles = null; + } + +} diff --git a/lite/src/org/transdroid/core/gui/SearchHistoryProvider.java b/lite/src/org/transdroid/core/gui/SearchHistoryProvider.java new file mode 100644 index 00000000..da59bbf1 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/SearchHistoryProvider.java @@ -0,0 +1,22 @@ +package org.transdroid.core.gui; + +import android.content.Context; +import android.content.SearchRecentSuggestionsProvider; +import android.provider.SearchRecentSuggestions; + +public class SearchHistoryProvider extends SearchRecentSuggestionsProvider { + + public final static String AUTHORITY = "org.transdroid.core.gui.SearchHistoryProvider"; + public final static int MODE = DATABASE_MODE_QUERIES; + + public SearchHistoryProvider() { + super(); + setupSuggestions(AUTHORITY, MODE); + } + + public static void clearHistory(Context context) { + SearchRecentSuggestions suggestions = new SearchRecentSuggestions(context, + SearchHistoryProvider.AUTHORITY, SearchHistoryProvider.MODE); + suggestions.clearHistory(); + } +} diff --git a/lite/src/org/transdroid/core/gui/TorrentsActivity.java b/lite/src/org/transdroid/core/gui/TorrentsActivity.java new file mode 100644 index 00000000..cfd82a53 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/TorrentsActivity.java @@ -0,0 +1,253 @@ +package org.transdroid.core.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.AfterViews; +import org.androidannotations.annotations.Background; +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EActivity; +import org.androidannotations.annotations.FragmentById; +import org.androidannotations.annotations.InstanceState; +import org.androidannotations.annotations.OptionsItem; +import org.androidannotations.annotations.OptionsMenu; +import org.androidannotations.annotations.SystemService; +import org.androidannotations.annotations.UiThread; +import org.androidannotations.annotations.ViewById; +import org.transdroid.core.R; +import org.transdroid.core.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.ServerSetting; +import org.transdroid.core.gui.lists.LocalTorrent; +import org.transdroid.core.gui.lists.SimpleListItem; +import org.transdroid.core.gui.navigation.FilterListAdapter; +import org.transdroid.core.gui.navigation.FilterListAdapter_; +import org.transdroid.core.gui.navigation.Label; +import org.transdroid.core.gui.navigation.NavigationHelper; +import org.transdroid.core.gui.navigation.StatusType; +import org.transdroid.core.gui.navigation.StatusType.StatusTypeFilter; +import org.transdroid.daemon.IDaemonAdapter; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.task.DaemonTaskFailureResult; +import org.transdroid.daemon.task.DaemonTaskResult; +import org.transdroid.daemon.task.RetrieveTask; +import org.transdroid.daemon.task.RetrieveTaskSuccessResult; + +import android.annotation.TargetApi; +import android.app.SearchManager; +import android.os.Build; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.Toast; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.ActionBar.OnNavigationListener; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SherlockListView; +import com.actionbarsherlock.widget.SearchView; + +@EActivity(R.layout.activity_torrents) +@OptionsMenu(R.menu.activity_torrents) +public class TorrentsActivity extends SherlockFragmentActivity implements OnNavigationListener { + + // Navigation components + @Bean + protected NavigationHelper navigationHelper; + @ViewById + protected SherlockListView filtersList; + protected FilterListAdapter navigationListAdapter = null; + protected FilterListAdapter navigationSpinnerAdapter = null; + @SystemService + protected SearchManager searchManager; + + // Settings + @Bean + protected ApplicationSettings applicationSettings; + @InstanceState + boolean firstStart = true; + private IDaemonAdapter currentConnection = null; + + // Torrents list components + @FragmentById(R.id.torrent_list) + protected TorrentsFragment fragmentTorrents; + + // Details view components + @FragmentById(R.id.torrent_details) + protected DetailsFagment fragmentDetails; + + @AfterViews + protected void init() { + + // Set up navigation, with an action bar spinner and possibly (if room) with a filter list + getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + getSupportActionBar().setHomeButtonEnabled(false); + navigationSpinnerAdapter = FilterListAdapter_.getInstance_(this); + // Servers are always added to the action bar spinner + navigationSpinnerAdapter.updateServers(applicationSettings.getServerSettings()); + getSupportActionBar().setListNavigationCallbacks(navigationSpinnerAdapter, this); + if (filtersList != null) { + // There was room for a dedicated filter list; add the status types + navigationListAdapter = FilterListAdapter_.getInstance_(this); + filtersList.setAdapter(navigationListAdapter); + navigationListAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); + filtersList.setOnItemSelectedListener(onFilterListItemSelected); + } else { + // Add status types directly to the action bar spinner + navigationSpinnerAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); + } + + // Connect to the last used server + ServerSetting lastUsed = applicationSettings.getLastUsedServer(); + if (lastUsed == null) { + // No server settings yet; + return; + } + // Set this as selection in the action bar spinner; we can use the server setting key since we have stable ids + // TODO: Does this call the action bar item selection callback? + getSupportActionBar().setSelectedNavigationItem(lastUsed.getOrder()); + + // Handle any start up intents or instead just refresh the torrents list + if (firstStart) { + handleStartIntent(); + } else { + refreshTorrents(); + } + + } + + @Override + protected void onResume() { + super.onResume(); + refreshTorrents(); + } + + @TargetApi(Build.VERSION_CODES.FROYO) + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + // For Android 2.1+, add an expandable SearchView to the action bar + MenuItem item = menu.findItem(R.id.action_search); + if (android.os.Build.VERSION.SDK_INT >= 8) { + final SearchView searchView = new SearchView(this); + searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); + searchView.setQueryRefinementEnabled(true); + item.setActionView(searchView); + } + return true; + } + + /** + * Called when an item in the action bar navigation spinner was selected + */ + @Override + public boolean onNavigationItemSelected(int itemPosition, long itemId) { + Object item = navigationSpinnerAdapter.getItem(itemPosition); + if (item instanceof SimpleListItem) { + // A filter item was selected form the navigation spinner + filterSelected((SimpleListItem) item); + return true; + } + // A header was selected; no action + return false; + } + + // Handles clicks (selections) on the dedicated list of filter items (if it exists) + // NOTE: Unfortunately we cannot use the @ItemSelect(R.id.filters_list) annotation as it throws NPE exceptions when + // the list doesn't exist (read: on small screens) + protected OnItemSelectedListener onFilterListItemSelected = new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + filterSelected((SimpleListItem) filtersList.getAdapter().getItem(position)); + } + @Override + public void onNothingSelected(AdapterView parent) { + // TODO: Check if this happens + } + }; + + /** + * A new filter was selected; update the view over the current data + * @param selected True if the filter item was selected, false if it was deselected + * @param item The touched filter item + */ + protected void filterSelected(SimpleListItem item) { + + // Server selection + if (item instanceof ServerSetting) { + ServerSetting server = (ServerSetting) item; + + if (currentConnection != null && server.equals(currentConnection.getSettings())) { + // Already connected to this server; just ask for a refresh instead + refreshTorrents(); + return; + } + + // Update connection to the newly selected server and refresh + currentConnection = server.createServerAdapter(); + clearScreens(); + refreshTorrents(); + + } + + if (item instanceof StatusTypeFilter) { + // TODO: Update the torrent list view + } + + if (item instanceof Label) { + // TODO: Update the torrent list view + } + + } + + /** + * If required, add torrents, switch to a specific server, etc. + */ + protected void handleStartIntent() { + // TODO: Handle start intent + } + + @OptionsItem(R.id.action_refresh) + protected void refreshScreen() { + refreshTorrents(); + // TODO: Refresh TorentDetails and TorrentFiles as well + } + + private void clearScreens() { + // Clear the currently shown list of torrent and perhaps the details + fragmentTorrents.clear(); + if (fragmentDetails != null) { + fragmentDetails.clear(); + } + } + + @Background + protected void refreshTorrents() { + DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(); + if (result instanceof RetrieveTaskSuccessResult) { + onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels()); + } else { + onCommunicationError((DaemonTaskFailureResult)result); + } + } + + @UiThread + protected void onTorrentsRetrieved(List torrents, List labels) { + // Report the newly retrieved list of torrents to the torrents fragment + fragmentTorrents.updateTorrents(new ArrayList(torrents)); + // Update the details fragment if the currently shown torrent is in the newly retrieved list + if (fragmentDetails != null) { + fragmentDetails.perhapsUpdateTorrent(torrents); + } + // TODO: Update local list of labels + } + + @UiThread + protected void onCommunicationError(DaemonTaskFailureResult result) { + // TODO: Properly report this error + Toast.makeText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), + Toast.LENGTH_LONG).show(); + } + +} diff --git a/lite/src/org/transdroid/core/gui/TorrentsFragment.java b/lite/src/org/transdroid/core/gui/TorrentsFragment.java new file mode 100644 index 00000000..a62dd8d2 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/TorrentsFragment.java @@ -0,0 +1,69 @@ +package org.transdroid.core.gui; + +import java.util.ArrayList; + +import org.androidannotations.annotations.AfterViews; +import org.androidannotations.annotations.EFragment; +import org.androidannotations.annotations.InstanceState; +import org.androidannotations.annotations.ItemClick; +import org.androidannotations.annotations.ViewById; +import org.transdroid.core.R; +import org.transdroid.core.gui.lists.TorrentsAdapter; +import org.transdroid.daemon.Torrent; + +import android.view.View; +import android.widget.TextView; + +import com.actionbarsherlock.app.SherlockFragment; +import com.actionbarsherlock.view.SherlockListView; + +@EFragment(R.layout.fragment_torrents) +public class TorrentsFragment extends SherlockFragment { + + // Local data + @InstanceState + protected ArrayList torrents = null; + + // Views + @ViewById(R.id.torrent_list) + protected SherlockListView torrentsList; + @ViewById + protected TextView emptyText; + + @AfterViews + protected void init() { + torrentsList.setAdapter(new TorrentsAdapter()); + torrentsList.setEmptyView(emptyText); + if (torrents != null) + updateTorrents(torrents); + } + + /** + * Updates the list adapter to show a new list of torrent objects, replacing the old torrents completely + * @param newTorrents The new, updated list of torrents + */ + public void updateTorrents(ArrayList newTorrents) { + torrents = newTorrents; + if (newTorrents == null) { + // Hide list adapter as well as empty text + torrentsList.setVisibility(View.GONE); + emptyText.setVisibility(View.GONE); + } else { + ((TorrentsAdapter)torrentsList.getAdapter()).update(newTorrents); + // NOTE: This will also make visible again the list or empty view + } + } + + /** + * Clear currently visible list of torrents + */ + public void clear() { + updateTorrents(null); + } + + @ItemClick(R.id.torrent_list) + protected void torrentsListClicked(Torrent torrent) { + + } + +} diff --git a/lite/src/org/transdroid/core/gui/lists/DetailsAdapter.java b/lite/src/org/transdroid/core/gui/lists/DetailsAdapter.java new file mode 100644 index 00000000..adb3b573 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/lists/DetailsAdapter.java @@ -0,0 +1,141 @@ +package org.transdroid.core.gui.lists; + +import java.util.List; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.RootContext; +import org.transdroid.core.R; +import org.transdroid.core.gui.navigation.FilterSeparatorView_; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentFile; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +import com.commonsware.cwac.merge.MergeAdapter; + +/** + * List adapter that holds a header view showing torrent details and show the list list contained by the torrent. + * @author Eric Kok + */ +@EBean +public class DetailsAdapter extends MergeAdapter { + + @RootContext + protected Context context; + private TorrentDetailsView torrentDetailsView = null; + private TorrentFilesAdapter torrentFilesAdapter = null; + private SimpleListItemAdapter trackersAdapter = null; + private SimpleListItemAdapter errorsAdapter = null; + + /** + * Update the torrent data in the details header of this merge adapter + * @param torrent The torrent for which detailed data is shown + */ + public void updateTorrent(Torrent torrent) { + if (this.torrentDetailsView == null) { + torrentDetailsView = TorrentDetailsView_.build(context); + addView(torrentDetailsView, false); + } + torrentDetailsView.update(torrent); + } + + /** + * Update the list of files contained in this torrent + * @param torrentFiles The new list of files + */ + public void updateTorrentFiles(List torrentFiles) { + if (this.torrentFilesAdapter == null && torrentFiles != null) { + addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.status_files)), false); + this.torrentFilesAdapter = new TorrentFilesAdapter(context, torrentFiles); + addAdapter(torrentFilesAdapter); + } else if (this.torrentFilesAdapter != null && torrentFiles != null) { + this.torrentFilesAdapter.update(torrentFiles); + } else { + this.torrentFilesAdapter = null; + } + } + + /** + * Update the list of trackers + * @param trackers The new list of trackers known for this torrent + */ + public void updateTrackers(List trackers) { + if (this.trackersAdapter == null && trackers != null) { + addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.status_trackers)), false); + this.trackersAdapter = new SimpleListItemAdapter(context, trackers); + addAdapter(trackersAdapter); + } else if (this.trackersAdapter != null && trackers != null) { + this.trackersAdapter.update(trackers); + } else { + this.trackersAdapter = null; + } + } + + /** + * Update the list of errors + * @param errors The new list of errors known for this torrent + */ + public void updateErrors(List errors) { + if (this.errorsAdapter == null && errors != null) { + addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.status_errors)), false); + this.errorsAdapter = new SimpleListItemAdapter(context, errors); + addAdapter(errorsAdapter); + } else if (this.errorsAdapter != null && errors != null) { + this.errorsAdapter.update(errors); + } else { + this.errorsAdapter = null; + } + } + + protected class TorrentFilesAdapter extends BaseAdapter { + + private final Context context; + private List items; + + public TorrentFilesAdapter(Context context, List items) { + this.context = context; + this.items = items; + } + + /** + * Allows updating of the full data list underlying this adapter, replacing all items + * @param newItems The new list of files to display + */ + public void update(List newItems) { + this.items = newItems; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return items.size(); + } + + @Override + public TorrentFile getItem(int position) { + return items.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TorrentFileView torrentFileView; + if (convertView == null) { + torrentFileView = TorrentFileView_.build(context); + } else { + torrentFileView = (TorrentFileView) convertView; + } + torrentFileView.bind(getItem(position)); + return torrentFileView; + } + + } + +} diff --git a/lite/src/org/transdroid/core/gui/lists/LocalTorrent.java b/lite/src/org/transdroid/core/gui/lists/LocalTorrent.java new file mode 100644 index 00000000..76722530 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/lists/LocalTorrent.java @@ -0,0 +1,233 @@ +package org.transdroid.core.gui.lists; + +import java.util.Locale; + +import org.transdroid.core.R; +import org.transdroid.daemon.DaemonException; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentStatus; +import org.transdroid.daemon.util.FileSizeConverter; +import org.transdroid.daemon.util.TimespanConverter; + +import android.content.res.Resources; + +/** + * Wrapper around Torrent to provide some addition getters that give translatable or otherwise formatted Strings of + * torrent statistics. + * @author Eric Kok + */ +public class LocalTorrent { + + /** + * Creates the LocalTorrent object so that the translatable/formattable version of a Torrent can be used. + * @param torrent The Torrent object + * @return The torrent wrapped as LocalTorrent object + */ + public static LocalTorrent fromTorrent(Torrent torrent) { + return new LocalTorrent(torrent); + } + + private final Torrent t; + + private LocalTorrent(Torrent torrent) { + this.t = torrent; + } + + private static final String DECIMAL_FORMATTER = "%.1f"; + + /** + * Builds a string showing the upload/download seed ratio. If not downloading, it will base the ratio on the total + * size; so if you created the torrent yourself you will have downloaded 0 bytes, but the ratio will pretend you + * have 100%. + * @return A nicely formatted string containing the upload/download seed ratio + */ + public String getRatioString() { + long baseSize = t.getTotalSize(); + if (t.getStatusCode() == TorrentStatus.Downloading) { + baseSize = t.getDownloadedEver(); + } + if (baseSize <= 0) { + return String.format(Locale.getDefault(), DECIMAL_FORMATTER, 0d); + } else if (t.getRatio() == Double.POSITIVE_INFINITY) { + return "\u221E"; + } else { + return String.format(Locale.getDefault(), DECIMAL_FORMATTER, t.getRatio()); + } + } + + /** + * Returns a formatted string indicating the current progress in terms of transferred bytes + * @param r The context resources, to access translations + * @param withAvailability Whether to show file availability in-line + * @return A nicely formatted string indicating torrent status and, if applicable, progress in bytes + */ + public String getProgressSizeText(Resources r, boolean withAvailability) { + + switch (t.getStatusCode()) { + case Waiting: + case Checking: + case Error: + // Not downloading yet + return r.getString(R.string.status_waitingtodl, FileSizeConverter.getSize(t.getTotalSize())); + case Downloading: + // Downloading + return r.getString( + R.string.status_size1, + FileSizeConverter.getSize(t.getDownloadedEver()), + FileSizeConverter.getSize(t.getTotalSize()), + String.format(DECIMAL_FORMATTER, t.getDownloadedPercentage() * 100) + + "%" + + (!withAvailability ? "" : "/" + + String.format(DECIMAL_FORMATTER, t.getAvailability() * 100) + "%")); + case Seeding: + case Paused: + case Queued: + // Seeding or paused + return r.getString(R.string.status_size2, FileSizeConverter.getSize(t.getTotalSize()), + FileSizeConverter.getSize(t.getUploadedEver())); + default: + return ""; + } + + } + + /** + * Returns a formatted string indicating either the expected time to download (ETA) or, when seeding, the ratio + * @param r The context resources, to access translations + * @return A string like '~ 34 seconds', or 'RATIO 8.2' or an empty string + */ + public String getProgressEtaRatioText(Resources r) { + switch (t.getStatusCode()) { + case Downloading: + // Downloading + return getRemainingTimeString(r, true, false); + case Seeding: + case Paused: + case Queued: + // Seeding or paused + return r.getString(R.string.status_ratio, getRatioString()); + case Waiting: + case Checking: + case Error: + default: + return ""; + } + } + + /** + * Returns a formatted string indicating the torrent status and connected peers + * @param r The context resources, to access translations + * @return A string like 'Queued' or, when seeding or leeching, '2 OF 28 PEERS' + */ + public String getProgressConnectionText(Resources r) { + + switch (t.getStatusCode()) { + case Waiting: + return r.getString(R.string.status_waiting); + case Checking: + return r.getString(R.string.status_checking); + case Downloading: + return r.getString(R.string.status_peers, t.getPeersSendingToUs(), t.getPeersConnected()); + case Seeding: + return r.getString(R.string.status_peers, t.getPeersGettingFromUs(), t.getPeersConnected()); + case Paused: + return r.getString(R.string.status_paused); + case Queued: + return r.getString(R.string.status_stopped); + case Error: + return r.getString(R.string.status_error); + default: + return r.getString(R.string.status_unknown); + } + + } + + /** + * Returns a formatted string indicating current transfer speeds for the torrent + * @param r The context resources, to access translations + * @return A string like '↓ 28KB/s ↑ 1.8MB/s', or an empty string when not transferrring + */ + public String getProgressSpeedText(Resources r) { + + switch (t.getStatusCode()) { + case Waiting: + case Checking: + case Paused: + case Queued: + return ""; + case Downloading: + return r.getString(R.string.status_speed_down, FileSizeConverter.getSize(t.getRateDownload()) + "/s") + " " + + r.getString(R.string.status_speed_up, FileSizeConverter.getSize(t.getRateUpload()) + "/s"); + case Seeding: + return r.getString(R.string.status_speed_up, FileSizeConverter.getSize(t.getRateUpload()) + "/s"); + default: + return ""; + } + + } + + public String getProgressStatusEta(Resources r) { + switch (t.getStatusCode()) { + case Waiting: + return r.getString(R.string.status_waiting).toUpperCase(Locale.getDefault()); + case Checking: + return r.getString(R.string.status_checking).toUpperCase(Locale.getDefault()); + case Error: + return r.getString(R.string.status_error).toUpperCase(Locale.getDefault()); + case Downloading: + // Downloading + return r.getString(R.string.status_downloading).toUpperCase(Locale.getDefault()) + " (" + + String.format(DECIMAL_FORMATTER, t.getDownloadedPercentage() * 100) + "%), " + + getRemainingTimeString(r, false, true); + case Seeding: + return r.getString(R.string.status_seeding).toUpperCase(Locale.getDefault()); + case Paused: + return r.getString(R.string.status_paused).toUpperCase(Locale.getDefault()); + case Queued: + return r.getString(R.string.status_queued).toUpperCase(Locale.getDefault()); + default: + return r.getString(R.string.status_unknown).toUpperCase(Locale.getDefault()); + } + } + + /** + * Returns a formatted string indicating the remaining download time + * @param r The context resources, to access translations + * @param inDays Whether to show days or use hours for > 24 hours left instead + * @return A string like '4d 8h 34m 5s' or '2m 3s' + */ + public String getRemainingTimeString(Resources r, boolean abbreviate, boolean inDays) { + if (t.getEta() == -1 || t.getEta() == -2) { + return r.getString(R.string.status_unknowneta); + } + return r.getString(abbreviate ? R.string.status_eta : R.string.status_etalong, + TimespanConverter.getTime(t.getEta(), inDays)); + } + + /** + * Convert a DaemonException to a translatable human-readable error message + * @param e The exception that was thrown by the server + * @return A string resource ID to show to the user + */ + public static int getResourceForDaemonException(DaemonException e) { + switch (e.getType()) { + case MethodUnsupported: + return R.string.error_jsonrequesterror; + case ConnectionError: + return R.string.error_httperror; + case UnexpectedResponse: + return R.string.error_jsonresponseerror; + case ParsingFailed: + return R.string.error_jsonrequesterror; + case NotConnected: + return R.string.error_daemonnotconnected; + case AuthenticationFailure: + return R.string.error_401; + case FileAccessError: + return R.string.error_torrentfile; + default: + return R.string.error_httperror; + } + } + +} diff --git a/lite/src/org/transdroid/lite/gui/navigation/FilterItem.java b/lite/src/org/transdroid/core/gui/lists/SimpleListItem.java similarity index 64% rename from lite/src/org/transdroid/lite/gui/navigation/FilterItem.java rename to lite/src/org/transdroid/core/gui/lists/SimpleListItem.java index 81638d89..605aa3ca 100644 --- a/lite/src/org/transdroid/lite/gui/navigation/FilterItem.java +++ b/lite/src/org/transdroid/core/gui/lists/SimpleListItem.java @@ -1,11 +1,11 @@ -package org.transdroid.lite.gui.navigation; +package org.transdroid.core.gui.lists; /** * Represents a filter item as shown in the navigation list or spinner. * * @author Eric Kok */ -public interface FilterItem { +public interface SimpleListItem { public String getName(); diff --git a/lite/src/org/transdroid/core/gui/lists/SimpleListItemAdapter.java b/lite/src/org/transdroid/core/gui/lists/SimpleListItemAdapter.java new file mode 100644 index 00000000..f44c86f5 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/lists/SimpleListItemAdapter.java @@ -0,0 +1,91 @@ +package org.transdroid.core.gui.lists; + +import java.util.ArrayList; +import java.util.List; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +public class SimpleListItemAdapter extends BaseAdapter { + + private final Context context; + private List items; + + public SimpleListItemAdapter(Context context, List items) { + this.context = context; + this.items = items; + } + + /** + * Allows updating of the full data list underlying this adapter, replacing all items + * @param newItems The new list of filter items to display + */ + public void update(List newItems) { + this.items = newItems; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return items.size(); + } + + @Override + public SimpleListItem getItem(int position) { + return items.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + SimpleListItemView filterItemView; + if (convertView == null) { + filterItemView = SimpleListItemView_.build(context); + } else { + filterItemView = (SimpleListItemView) convertView; + } + filterItemView.bind(getItem(position)); + return filterItemView; + } + + /** + * Represents a very simple list item that only contains a single string to show in the list. Use wrapStringsList to + * wrap an existing list of strings into a list of {@link SimpleListItem}s. + * @author Eric Kok + */ + public static class SimpleStringItem implements SimpleListItem { + + /** + * Wraps a simple string of strings into a list of SimpleStringItem to add as data to a + * {@link SimpleListItemAdapter} + * @param errorStrings A list of string + * @return A list of SimpleStringItem objects representing the input strings + */ + public static List wrapStringsList(List errorStrings) { + ArrayList errors = new ArrayList(); + for (String errorString : errorStrings) { + errors.add(new SimpleStringItem(errorString)); + } + return errors; + } + + private final String string; + + public SimpleStringItem(String string) { + this.string = string; + } + + @Override + public String getName() { + return this.string; + } + + } + +} \ No newline at end of file diff --git a/lite/src/org/transdroid/lite/gui/navigation/FilterItemView.java b/lite/src/org/transdroid/core/gui/lists/SimpleListItemView.java similarity index 58% rename from lite/src/org/transdroid/lite/gui/navigation/FilterItemView.java rename to lite/src/org/transdroid/core/gui/lists/SimpleListItemView.java index 532f85f3..d428939f 100644 --- a/lite/src/org/transdroid/lite/gui/navigation/FilterItemView.java +++ b/lite/src/org/transdroid/core/gui/lists/SimpleListItemView.java @@ -1,4 +1,4 @@ -package org.transdroid.lite.gui.navigation; +package org.transdroid.core.gui.lists; import org.androidannotations.annotations.EViewGroup; import org.androidannotations.annotations.ViewById; @@ -9,20 +9,20 @@ import android.widget.LinearLayout; import android.widget.TextView; /** - * View that represents some {@link FilterItem} object and simple prints out the text (in proper style) + * View that represents some {@link SimpleListItem} object and simple prints out the text (in proper style) * @author Eric Kok */ @EViewGroup(R.layout.list_item_filter) -public class FilterItemView extends LinearLayout { +public class SimpleListItemView extends LinearLayout { @ViewById protected TextView itemText; - public FilterItemView(Context context) { + public SimpleListItemView(Context context) { super(context); } - public void bind(FilterItem filterItem) { + public void bind(SimpleListItem filterItem) { itemText.setText(filterItem.getName()); } diff --git a/lite/src/org/transdroid/core/gui/lists/TorrentDetailsView.java b/lite/src/org/transdroid/core/gui/lists/TorrentDetailsView.java new file mode 100644 index 00000000..c2975105 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/lists/TorrentDetailsView.java @@ -0,0 +1,85 @@ +package org.transdroid.core.gui.lists; + +import org.androidannotations.annotations.EViewGroup; +import org.androidannotations.annotations.ViewById; +import org.transdroid.core.R; +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.util.FileSizeConverter; + +import android.content.Context; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.view.View; +import android.widget.RelativeLayout; +import android.widget.TextView; + +/** + * Represents a group of views that show torrent status, sizes, speeds and other details. + * @author Eric Kok + */ +@EViewGroup(R.layout.fragment_details_header) +public class TorrentDetailsView extends RelativeLayout { + + @ViewById + protected TextView labelText, dateaddedText, uploadedText, uploadedunitText, ratioText, upspeedText, seedersText, + downloadedunitText, downloadedText, totalsizeText, downspeedText, leechersText, statusText; + + public TorrentDetailsView(Context context) { + super(context); + } + + /** + * Update the text fields with new/updated torrent details + * @param torrent The torrent for which to show details + */ + public void update(Torrent torrent) { + + LocalTorrent local = LocalTorrent.fromTorrent(torrent); + + // Set label text + if (Daemon.supportsLabels(torrent.getDaemon())) { + if (TextUtils.isEmpty(torrent.getLabelName())) { + labelText.setText(getResources().getString(R.string.labels_unlabeled)); + } else { + labelText.setText(torrent.getLabelName()); + } + labelText.setVisibility(View.VISIBLE); + } else { + labelText.setVisibility(View.INVISIBLE); + } + + // Set status texts + if (torrent.getDateAdded() != null) { + dateaddedText.setText(getResources().getString( + R.string.status_sincedate, + DateUtils.getRelativeDateTimeString(getContext(), torrent.getDateAdded().getTime(), + DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_ABBREV_MONTH))); + dateaddedText.setVisibility(View.VISIBLE); + } else { + dateaddedText.setVisibility(View.INVISIBLE); + } + statusText.setText(local.getProgressStatusEta(getResources())); + ratioText.setText(getResources().getString(R.string.status_ratio, local.getRatioString())); + // TODO: Implement separate numbers of seeders and leechers + seedersText.setText(getResources().getString(R.string.status_peers, torrent.getPeersSendingToUs(), + torrent.getPeersConnected())); + leechersText.setText(getResources().getString(R.string.status_peers, torrent.getPeersSendingToUs(), + torrent.getPeersConnected())); + // TODO: Add field that displays torrent errors (as opposed to tracker errors) + // TODO: Add field that displays availability + + // Sizes and speeds texts + totalsizeText.setText(FileSizeConverter.getSize(torrent.getTotalSize())); + downloadedText.setText(FileSizeConverter.getSize(torrent.getDownloadedEver(), false)); + downloadedunitText.setText(FileSizeConverter.getSizeUnit(torrent.getDownloadedEver()).toString()); + uploadedText.setText(FileSizeConverter.getSize(torrent.getUploadedEver(), false)); + uploadedunitText.setText(FileSizeConverter.getSizeUnit(torrent.getUploadedEver()).toString()); + downspeedText.setText(getResources().getString(R.string.status_speed_down, + FileSizeConverter.getSize(torrent.getRateDownload()) + "/s")); + upspeedText.setText(getResources().getString(R.string.status_speed_up, + FileSizeConverter.getSize(torrent.getRateUpload()) + "/s")); + + } + +} diff --git a/lite/src/org/transdroid/core/gui/lists/TorrentFileView.java b/lite/src/org/transdroid/core/gui/lists/TorrentFileView.java new file mode 100644 index 00000000..0c9b3684 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/lists/TorrentFileView.java @@ -0,0 +1,53 @@ +package org.transdroid.core.gui.lists; + +import org.androidannotations.annotations.EViewGroup; +import org.androidannotations.annotations.ViewById; +import org.transdroid.core.R; +import org.transdroid.daemon.TorrentFile; + +import android.content.Context; +import android.widget.ImageView; +import android.widget.TextView; +import fr.marvinlabs.widget.CheckableRelativeLayout; + +/** + * View that represents some {@link TorrentFile} object and show the file's name, status and priority + * @author Eric Kok + */ +@EViewGroup(R.layout.list_item_torrentfile) +public class TorrentFileView extends CheckableRelativeLayout { + + @ViewById + protected TextView nameText, progressText, sizesText; + @ViewById + protected ImageView priorityImage; + + public TorrentFileView(Context context) { + super(context, null); + } + + public void bind(TorrentFile torrentFile) { + nameText.setText(torrentFile.getName()); + sizesText.setText(torrentFile.getDownloadedAndTotalSizeText()); + progressText.setText(torrentFile.getProgressText()); + switch (torrentFile.getPriority()) { + case Off: + priorityImage.setImageResource(R.drawable.ic_priority_off); + priorityImage.setContentDescription(getResources().getString(R.string.status_priority_low)); + break; + case Low: + priorityImage.setImageResource(R.drawable.ic_priority_low); + priorityImage.setContentDescription(getResources().getString(R.string.status_priority_normal)); + break; + case Normal: + priorityImage.setImageResource(R.drawable.ic_priority_normal); + priorityImage.setContentDescription(getResources().getString(R.string.status_priority_normal)); + break; + case High: + priorityImage.setImageResource(R.drawable.ic_priority_high); + priorityImage.setContentDescription(getResources().getString(R.string.status_priority_high)); + break; + } + } + +} diff --git a/lite/src/org/transdroid/lite/gui/lists/TorrentProgressBar.java b/lite/src/org/transdroid/core/gui/lists/TorrentProgressBar.java similarity index 98% rename from lite/src/org/transdroid/lite/gui/lists/TorrentProgressBar.java rename to lite/src/org/transdroid/core/gui/lists/TorrentProgressBar.java index 0dbe8fc1..34123a99 100644 --- a/lite/src/org/transdroid/lite/gui/lists/TorrentProgressBar.java +++ b/lite/src/org/transdroid/core/gui/lists/TorrentProgressBar.java @@ -1,4 +1,4 @@ -package org.transdroid.lite.gui.lists; +package org.transdroid.core.gui.lists; import org.transdroid.core.R; diff --git a/lite/src/org/transdroid/core/gui/lists/TorrentView.java b/lite/src/org/transdroid/core/gui/lists/TorrentView.java new file mode 100644 index 00000000..bf8ff662 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/lists/TorrentView.java @@ -0,0 +1,47 @@ +package org.transdroid.core.gui.lists; + +import org.androidannotations.annotations.EViewGroup; +import org.androidannotations.annotations.ViewById; +import org.transdroid.core.R; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentStatus; + +import android.content.Context; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import fr.marvinlabs.widget.CheckableRelativeLayout; + +/** + * View that represents some {@link Torrent} object and displays progress, status, speeds, etc. + * @author Eric Kok + */ +@EViewGroup(R.layout.list_item_torrent) +public class TorrentView extends CheckableRelativeLayout { + + @ViewById + protected ImageView priorityImage; + @ViewById + protected TextView nameText, ratioText, progressText, speedText, peersText; + @ViewById + protected TorrentProgressBar torrentProgressbar; + + public TorrentView(Context context) { + super(context, null); + } + + public void bind(Torrent torrent) { + LocalTorrent local = LocalTorrent.fromTorrent(torrent); + nameText.setText(torrent.getName()); + ratioText.setText(local.getProgressEtaRatioText(getResources())); + progressText.setText(local.getProgressSizeText(getResources(), false)); + speedText.setText(local.getProgressSpeedText(getResources())); + peersText.setText(local.getProgressConnectionText(getResources())); + torrentProgressbar.setProgress((int) (torrent.getDownloadedPercentage() * 100)); + torrentProgressbar.setActive(torrent.canPause());; + torrentProgressbar.setError(torrent.getStatusCode() == TorrentStatus.Error); + // TODO: Implement per-torrent priority and set priorityImage + priorityImage.setVisibility(View.INVISIBLE); + } + +} diff --git a/lite/src/org/transdroid/core/gui/lists/TorrentsAdapter.java b/lite/src/org/transdroid/core/gui/lists/TorrentsAdapter.java new file mode 100644 index 00000000..1ebf2390 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/lists/TorrentsAdapter.java @@ -0,0 +1,67 @@ +package org.transdroid.core.gui.lists; + +import java.util.ArrayList; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.RootContext; +import org.transdroid.core.gui.lists.TorrentView_; +import org.transdroid.daemon.Torrent; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +/** + * Adapter that contains a list of torrent objects to show. + * @author Eric Kok + */ +@EBean +public class TorrentsAdapter extends BaseAdapter { + + private ArrayList torrents = null; + + @RootContext + protected Context context; + + /** + * Allows updating the full internal list of torrents at once, replacing the old list + * @param newTorrents The new list of torrent objects + */ + public void update(ArrayList newTorrents) { + this.torrents = newTorrents; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + if (torrents == null) + return 0; + return torrents.size(); + } + + @Override + public Torrent getItem(int position) { + if (torrents == null) + return null; + return torrents.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TorrentView torrentView; + if (convertView == null) { + torrentView = TorrentView_.build(context); + } else { + torrentView = (TorrentView) convertView; + } + torrentView.bind(getItem(position)); + return torrentView; + } + +} diff --git a/lite/src/org/transdroid/core/gui/navigation/FilterListAdapter.java b/lite/src/org/transdroid/core/gui/navigation/FilterListAdapter.java new file mode 100644 index 00000000..d155e801 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/navigation/FilterListAdapter.java @@ -0,0 +1,78 @@ +package org.transdroid.core.gui.navigation; + +import java.util.List; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.RootContext; +import org.transdroid.core.R; +import org.transdroid.core.gui.lists.SimpleListItem; +import org.transdroid.core.gui.lists.SimpleListItemAdapter; +import org.transdroid.core.gui.navigation.FilterSeparatorView_; + +import android.content.Context; + +import com.commonsware.cwac.merge.MergeAdapter; + +/** + * List adapter that holds filter items, that is, servers, view types and labels. A header item is inserted where + * appropriate. + * @author Eric Kok + */ +@EBean +public class FilterListAdapter extends MergeAdapter { + + @RootContext + protected Context context; + private SimpleListItemAdapter serverItems = null; + private SimpleListItemAdapter statusTypeItems = null; + private SimpleListItemAdapter labelItems = null; + + /** + * Update the list of available servers + * @param servers The new list of available servers + */ + public void updateServers(List servers) { + if (this.serverItems == null && servers != null) { + addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_servers)), false); + this.serverItems = new SimpleListItemAdapter(context, servers); + addAdapter(serverItems); + } else if (this.serverItems != null && servers != null) { + this.serverItems.update(servers); + } else { + this.serverItems = null; + } + } + + /** + * Update the list of available status types + * @param statusTypes The new list of available status types + */ + public void updateStatusTypes(List statusTypes) { + if (this.statusTypeItems == null && statusTypes != null) { + addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_status)), false); + this.statusTypeItems = new SimpleListItemAdapter(context, statusTypes); + addAdapter(statusTypeItems); + } else if (this.statusTypeItems != null && statusTypes != null) { + this.statusTypeItems.update(statusTypes); + } else { + this.statusTypeItems = null; + } + } + + /** + * Update the list of available labels + * @param labels The new list of available labels + */ + public void updateLabels(List labels) { + if (this.labelItems == null && labels != null) { + addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_labels)), false); + this.labelItems = new SimpleListItemAdapter(context, labels); + addAdapter(labelItems); + } else if (this.serverItems != null && labels != null) { + this.labelItems.update(labels); + } else { + this.labelItems = null; + } + } + +} diff --git a/lite/src/org/transdroid/lite/gui/navigation/FilterSeparatorView.java b/lite/src/org/transdroid/core/gui/navigation/FilterSeparatorView.java similarity index 86% rename from lite/src/org/transdroid/lite/gui/navigation/FilterSeparatorView.java rename to lite/src/org/transdroid/core/gui/navigation/FilterSeparatorView.java index ea6deaff..9ea2a411 100644 --- a/lite/src/org/transdroid/lite/gui/navigation/FilterSeparatorView.java +++ b/lite/src/org/transdroid/core/gui/navigation/FilterSeparatorView.java @@ -1,8 +1,9 @@ -package org.transdroid.lite.gui.navigation; +package org.transdroid.core.gui.navigation; import org.androidannotations.annotations.EViewGroup; import org.androidannotations.annotations.ViewById; import org.transdroid.core.R; +import org.transdroid.core.gui.lists.SimpleListItem; import android.content.Context; import android.widget.LinearLayout; @@ -25,7 +26,7 @@ public class FilterSeparatorView extends LinearLayout { super(context); } - public void bind(FilterItem filterItem) { + public void bind(SimpleListItem filterItem) { separatorText.setText(text); } diff --git a/lite/src/org/transdroid/lite/gui/navigation/Label.java b/lite/src/org/transdroid/core/gui/navigation/Label.java similarity index 62% rename from lite/src/org/transdroid/lite/gui/navigation/Label.java rename to lite/src/org/transdroid/core/gui/navigation/Label.java index 6da5d20a..2a27e6b6 100644 --- a/lite/src/org/transdroid/lite/gui/navigation/Label.java +++ b/lite/src/org/transdroid/core/gui/navigation/Label.java @@ -1,10 +1,12 @@ -package org.transdroid.lite.gui.navigation; +package org.transdroid.core.gui.navigation; + +import org.transdroid.core.gui.lists.SimpleListItem; /** * Represents some label that is active or available on the server. * @author Eric Kok */ -public class Label implements FilterItem { +public class Label implements SimpleListItem { private final String name; diff --git a/lite/src/org/transdroid/lite/gui/navigation/NavigationHelper.java b/lite/src/org/transdroid/core/gui/navigation/NavigationHelper.java similarity index 94% rename from lite/src/org/transdroid/lite/gui/navigation/NavigationHelper.java rename to lite/src/org/transdroid/core/gui/navigation/NavigationHelper.java index dd2ec575..c5d0d364 100644 --- a/lite/src/org/transdroid/lite/gui/navigation/NavigationHelper.java +++ b/lite/src/org/transdroid/core/gui/navigation/NavigationHelper.java @@ -1,4 +1,4 @@ -package org.transdroid.lite.gui.navigation; +package org.transdroid.core.gui.navigation; import org.androidannotations.annotations.EBean; import org.androidannotations.annotations.RootContext; diff --git a/lite/src/org/transdroid/lite/gui/navigation/StatusType.java b/lite/src/org/transdroid/core/gui/navigation/StatusType.java similarity index 92% rename from lite/src/org/transdroid/lite/gui/navigation/StatusType.java rename to lite/src/org/transdroid/core/gui/navigation/StatusType.java index 2aaac124..d7a86cad 100644 --- a/lite/src/org/transdroid/lite/gui/navigation/StatusType.java +++ b/lite/src/org/transdroid/core/gui/navigation/StatusType.java @@ -1,9 +1,10 @@ -package org.transdroid.lite.gui.navigation; +package org.transdroid.core.gui.navigation; import java.util.Arrays; import java.util.List; import org.transdroid.core.R; +import org.transdroid.core.gui.lists.SimpleListItem; import android.content.Context; @@ -57,7 +58,7 @@ public enum StatusType { */ abstract StatusTypeFilter getFilterItem(Context context); - public static class StatusTypeFilter implements FilterItem { + public static class StatusTypeFilter implements SimpleListItem { private final String name; diff --git a/lite/src/org/transdroid/lite/gui/settings/MainSettingsActivity.java b/lite/src/org/transdroid/core/gui/settings/MainSettingsActivity.java similarity index 91% rename from lite/src/org/transdroid/lite/gui/settings/MainSettingsActivity.java rename to lite/src/org/transdroid/core/gui/settings/MainSettingsActivity.java index 5240fee5..dce00a47 100644 --- a/lite/src/org/transdroid/lite/gui/settings/MainSettingsActivity.java +++ b/lite/src/org/transdroid/core/gui/settings/MainSettingsActivity.java @@ -1,4 +1,4 @@ -package org.transdroid.lite.gui.settings; +package org.transdroid.core.gui.settings; import java.util.ArrayList; import java.util.List; @@ -6,15 +6,15 @@ import java.util.List; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.transdroid.core.R; -import org.transdroid.lite.app.search.SearchHelper; -import org.transdroid.lite.app.search.SearchSite; -import org.transdroid.lite.app.settings.ApplicationSettings; -import org.transdroid.lite.app.settings.RssfeedSetting; -import org.transdroid.lite.app.settings.ServerSetting; -import org.transdroid.lite.app.settings.WebsearchSetting; -import org.transdroid.lite.gui.settings.RssfeedPreference.OnRssfeedClickedListener; -import org.transdroid.lite.gui.settings.ServerPreference.OnServerClickedListener; -import org.transdroid.lite.gui.settings.WebsearchPreference.OnWebsearchClickedListener; +import org.transdroid.core.app.search.SearchHelper; +import org.transdroid.core.app.search.SearchSite; +import org.transdroid.core.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.RssfeedSetting; +import org.transdroid.core.app.settings.ServerSetting; +import org.transdroid.core.app.settings.WebsearchSetting; +import org.transdroid.core.gui.settings.RssfeedPreference.OnRssfeedClickedListener; +import org.transdroid.core.gui.settings.ServerPreference.OnServerClickedListener; +import org.transdroid.core.gui.settings.WebsearchPreference.OnWebsearchClickedListener; import android.os.Bundle; import android.preference.ListPreference; diff --git a/lite/src/org/transdroid/lite/gui/settings/OtherSettingsActivity.java b/lite/src/org/transdroid/core/gui/settings/OtherSettingsActivity.java similarity index 87% rename from lite/src/org/transdroid/lite/gui/settings/OtherSettingsActivity.java rename to lite/src/org/transdroid/core/gui/settings/OtherSettingsActivity.java index 5e23ee79..fdd62df7 100644 --- a/lite/src/org/transdroid/lite/gui/settings/OtherSettingsActivity.java +++ b/lite/src/org/transdroid/core/gui/settings/OtherSettingsActivity.java @@ -1,9 +1,9 @@ -package org.transdroid.lite.gui.settings; +package org.transdroid.core.gui.settings; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.Extra; -import org.transdroid.lite.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.ApplicationSettings; import android.os.Bundle; diff --git a/lite/src/org/transdroid/lite/gui/settings/RssfeedPreference.java b/lite/src/org/transdroid/core/gui/settings/RssfeedPreference.java similarity index 94% rename from lite/src/org/transdroid/lite/gui/settings/RssfeedPreference.java rename to lite/src/org/transdroid/core/gui/settings/RssfeedPreference.java index 49a81046..59d86415 100644 --- a/lite/src/org/transdroid/lite/gui/settings/RssfeedPreference.java +++ b/lite/src/org/transdroid/core/gui/settings/RssfeedPreference.java @@ -1,6 +1,6 @@ -package org.transdroid.lite.gui.settings; +package org.transdroid.core.gui.settings; -import org.transdroid.lite.app.settings.RssfeedSetting; +import org.transdroid.core.app.settings.RssfeedSetting; import android.content.Context; import android.preference.Preference; diff --git a/lite/src/org/transdroid/lite/gui/settings/RssfeedSettingsActivity.java b/lite/src/org/transdroid/core/gui/settings/RssfeedSettingsActivity.java similarity index 92% rename from lite/src/org/transdroid/lite/gui/settings/RssfeedSettingsActivity.java rename to lite/src/org/transdroid/core/gui/settings/RssfeedSettingsActivity.java index 2b83f52f..e0e8e44c 100644 --- a/lite/src/org/transdroid/lite/gui/settings/RssfeedSettingsActivity.java +++ b/lite/src/org/transdroid/core/gui/settings/RssfeedSettingsActivity.java @@ -1,10 +1,10 @@ -package org.transdroid.lite.gui.settings; +package org.transdroid.core.gui.settings; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.Extra; import org.transdroid.core.R; -import org.transdroid.lite.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.ApplicationSettings; import android.os.Bundle; diff --git a/lite/src/org/transdroid/lite/gui/settings/ServerPreference.java b/lite/src/org/transdroid/core/gui/settings/ServerPreference.java similarity index 94% rename from lite/src/org/transdroid/lite/gui/settings/ServerPreference.java rename to lite/src/org/transdroid/core/gui/settings/ServerPreference.java index 802873e3..0aa0bbb2 100644 --- a/lite/src/org/transdroid/lite/gui/settings/ServerPreference.java +++ b/lite/src/org/transdroid/core/gui/settings/ServerPreference.java @@ -1,6 +1,6 @@ -package org.transdroid.lite.gui.settings; +package org.transdroid.core.gui.settings; -import org.transdroid.lite.app.settings.ServerSetting; +import org.transdroid.core.app.settings.ServerSetting; import android.content.Context; import android.preference.Preference; diff --git a/lite/src/org/transdroid/lite/gui/settings/ServerSettingsActivity.java b/lite/src/org/transdroid/core/gui/settings/ServerSettingsActivity.java similarity index 97% rename from lite/src/org/transdroid/lite/gui/settings/ServerSettingsActivity.java rename to lite/src/org/transdroid/core/gui/settings/ServerSettingsActivity.java index 148a8b56..6398d547 100644 --- a/lite/src/org/transdroid/lite/gui/settings/ServerSettingsActivity.java +++ b/lite/src/org/transdroid/core/gui/settings/ServerSettingsActivity.java @@ -1,11 +1,11 @@ -package org.transdroid.lite.gui.settings; +package org.transdroid.core.gui.settings; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.Extra; import org.transdroid.daemon.Daemon; import org.transdroid.core.R; -import org.transdroid.lite.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.ApplicationSettings; import android.content.SharedPreferences; import android.os.Bundle; diff --git a/lite/src/org/transdroid/lite/gui/settings/WebsearchPreference.java b/lite/src/org/transdroid/core/gui/settings/WebsearchPreference.java similarity index 94% rename from lite/src/org/transdroid/lite/gui/settings/WebsearchPreference.java rename to lite/src/org/transdroid/core/gui/settings/WebsearchPreference.java index 929f66a9..a4a5d173 100644 --- a/lite/src/org/transdroid/lite/gui/settings/WebsearchPreference.java +++ b/lite/src/org/transdroid/core/gui/settings/WebsearchPreference.java @@ -1,6 +1,6 @@ -package org.transdroid.lite.gui.settings; +package org.transdroid.core.gui.settings; -import org.transdroid.lite.app.settings.WebsearchSetting; +import org.transdroid.core.app.settings.WebsearchSetting; import android.content.Context; import android.preference.Preference; diff --git a/lite/src/org/transdroid/lite/gui/settings/WebsearchSettingsActivity.java b/lite/src/org/transdroid/core/gui/settings/WebsearchSettingsActivity.java similarity index 92% rename from lite/src/org/transdroid/lite/gui/settings/WebsearchSettingsActivity.java rename to lite/src/org/transdroid/core/gui/settings/WebsearchSettingsActivity.java index d5daae2e..53194bcc 100644 --- a/lite/src/org/transdroid/lite/gui/settings/WebsearchSettingsActivity.java +++ b/lite/src/org/transdroid/core/gui/settings/WebsearchSettingsActivity.java @@ -1,10 +1,10 @@ -package org.transdroid.lite.gui.settings; +package org.transdroid.core.gui.settings; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.Extra; import org.transdroid.core.R; -import org.transdroid.lite.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.ApplicationSettings; import android.os.Bundle; diff --git a/lite/src/org/transdroid/lite/gui/DetailsFagment.java b/lite/src/org/transdroid/lite/gui/DetailsFagment.java deleted file mode 100644 index c43b8135..00000000 --- a/lite/src/org/transdroid/lite/gui/DetailsFagment.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.transdroid.lite.gui; - -import org.androidannotations.annotations.AfterViews; -import org.androidannotations.annotations.EFragment; -import org.androidannotations.annotations.FragmentArg; -import org.androidannotations.annotations.InstanceState; -import org.androidannotations.annotations.ViewById; -import org.transdroid.daemon.Torrent; -import org.transdroid.daemon.TorrentDetails; -import org.transdroid.core.R; - -import android.view.View; -import android.widget.TextView; - -import com.actionbarsherlock.app.SherlockFragment; - -/** - * Fragment that shown detailed statistics about some torrent. These come from some already fetched {@link Torrent} - * object, but it also retrieves further detailed statistics. - * - * @author Eric Kok - */ -@EFragment(R.layout.fragment_details) -public class DetailsFagment extends SherlockFragment { - - @FragmentArg - @InstanceState - protected Torrent torrent = null; - @InstanceState - protected TorrentDetails torrentDetails; - - @ViewById - protected TextView emptyText; - - @AfterViews - protected void init() { - - if (torrent == null) { - // No torrent specified; show the placeholder layout only - emptyText.setVisibility(View.VISIBLE); - } - - // TODO: Show the torrent details and load the advanced statistics - - } - -} diff --git a/lite/src/org/transdroid/lite/gui/TorrentsActivity.java b/lite/src/org/transdroid/lite/gui/TorrentsActivity.java deleted file mode 100644 index 1af298df..00000000 --- a/lite/src/org/transdroid/lite/gui/TorrentsActivity.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.transdroid.lite.gui; - -import org.androidannotations.annotations.AfterViews; -import org.androidannotations.annotations.Bean; -import org.androidannotations.annotations.EActivity; -import org.androidannotations.annotations.FragmentById; -import org.androidannotations.annotations.ItemSelect; -import org.androidannotations.annotations.OptionsMenu; -import org.androidannotations.annotations.ViewById; -import org.transdroid.core.R; -import org.transdroid.lite.app.settings.ApplicationSettings; -import org.transdroid.lite.gui.navigation.FilterAdapter; -import org.transdroid.lite.gui.navigation.FilterItem; -import org.transdroid.lite.gui.navigation.NavigationHelper; -import org.transdroid.lite.gui.navigation.StatusType; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.ActionBar.OnNavigationListener; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.SherlockListView; - -@EActivity(R.layout.activity_torrents) -@OptionsMenu(R.menu.activity_torrents) -public class TorrentsActivity extends SherlockFragmentActivity implements OnNavigationListener { - - // Navigation components - @Bean - protected NavigationHelper navigationHelper; - @ViewById - protected SherlockListView filtersList; - protected FilterAdapter navigationListAdapter = null; - protected FilterAdapter navigationSpinnerAdapter = null; - - // Settings - @Bean - protected ApplicationSettings applicationSettings; - - // Torrents list components - @FragmentById(R.id.torrent_list) - protected TorrentsFragment fragmentTorrents; - - // Details view components - @FragmentById(R.id.torrent_details) - protected DetailsFagment fragmentDetails; - - @AfterViews - protected void init() { - - // Set up navigation, with an action bar spinner and possibly (if room) with a filter list - getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - getSupportActionBar().setHomeButtonEnabled(false); - navigationSpinnerAdapter = new FilterAdapter(this); - // Servers are always added to the action bar spinner - navigationSpinnerAdapter.updateServers(applicationSettings.getServerSettings()); - getSupportActionBar().setListNavigationCallbacks(navigationSpinnerAdapter, this); - if (filtersList != null) { - // There was room for a dedicated filter list; add the status types - navigationListAdapter = new FilterAdapter(this); - filtersList.setAdapter(navigationListAdapter); - navigationListAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); - } else { - // Add status types directly to the action bar spinner - navigationSpinnerAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); - } - - - } - - /** - * Called when an item in the action bar navigation spinner was selected - */ - @Override - public boolean onNavigationItemSelected(int itemPosition, long itemId) { - Object item = navigationSpinnerAdapter.getItem(itemPosition); - if (item instanceof FilterItem) { - // A filter item was selected form the navigation spinner - filterSelected(true, (FilterItem) item); - return true; - } - // A header was selected; no action - return false; - } - - /** - * A new filter was selected; update the view over the current data - * @param selected True if the filter item was selected, false if it was deselected - * @param item The touched filter item - */ - @ItemSelect(R.id.filters_list) - protected void filterSelected(boolean selected, FilterItem item) { - // TODO: Update the torrent list view - } - -} diff --git a/lite/src/org/transdroid/lite/gui/TorrentsFragment.java b/lite/src/org/transdroid/lite/gui/TorrentsFragment.java deleted file mode 100644 index 5dd796bb..00000000 --- a/lite/src/org/transdroid/lite/gui/TorrentsFragment.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.transdroid.lite.gui; - -import org.androidannotations.annotations.EFragment; -import org.transdroid.core.R; - -import com.actionbarsherlock.app.SherlockFragment; - -@EFragment(R.layout.fragment_torrents) -public class TorrentsFragment extends SherlockFragment { - -} diff --git a/lite/src/org/transdroid/lite/gui/navigation/FilterAdapter.java b/lite/src/org/transdroid/lite/gui/navigation/FilterAdapter.java deleted file mode 100644 index 22f9dbe6..00000000 --- a/lite/src/org/transdroid/lite/gui/navigation/FilterAdapter.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.transdroid.lite.gui.navigation; - -import java.util.List; - -import org.transdroid.core.R; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; - -import com.commonsware.cwac.merge.MergeAdapter; - -/** - * List adapter that holds filter items, that is, servers, view types and labels. A header item is intersted where - * appropriate. - * @author Eric Kok - */ -public class FilterAdapter extends MergeAdapter { - - private Context context; - private FilterItemAdapter serverItems = null; - private FilterItemAdapter statusTypeItems = null; - private FilterItemAdapter labelItems = null; - - public FilterAdapter(Context context) { - this.context = context; - } - - /** - * Update the list of available servers. - * @param servers The new list of available servers - */ - public void updateServers(List servers) { - if (this.serverItems == null && servers != null) { - addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_servers)), false); - this.serverItems = new FilterItemAdapter(context, servers); - addAdapter(serverItems); - } else if (this.serverItems != null && servers != null) { - this.serverItems.update(servers); - } else { - this.serverItems = null; - } - } - - /** - * Update the list of available status types. - * @param statusTypes The new list of available status types - */ - public void updateStatusTypes(List statusTypes) { - if (this.statusTypeItems == null && statusTypes != null) { - addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_status)), false); - this.statusTypeItems = new FilterItemAdapter(context, statusTypes); - addAdapter(statusTypeItems); - } else if (this.statusTypeItems != null && statusTypes != null) { - this.statusTypeItems.update(statusTypes); - } else { - this.statusTypeItems = null; - } - } - - /** - * Update the list of available labels. - * @param labels The new list of available labels - */ - public void updateLabels(List labels) { - if (this.labelItems == null && labels != null) { - addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_labels)), false); - this.labelItems = new FilterItemAdapter(context, labels); - addAdapter(labelItems); - } else if (this.serverItems != null && labels != null) { - this.labelItems.update(labels); - } else { - this.labelItems = null; - } - } - - protected class FilterItemAdapter extends BaseAdapter { - - private final Context context; - private List items; - - public FilterItemAdapter(Context context, List items) { - this.context = context; - this.items = items; - } - - /** - * Allows updating of the full data list underlying this adapter, replacing all items - * @param newItems The new list of filter items to display - */ - public void update(List newItems) { - this.items = newItems; - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return items.size(); - } - - @Override - public FilterItem getItem(int position) { - return items.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - FilterItemView filterItemView; - if (convertView == null) { - filterItemView = FilterItemView_.build(context); - } else { - filterItemView = (FilterItemView) convertView; - } - filterItemView.bind(getItem(position)); - return filterItemView; - } - - } -}