Browse Source

Refactored logging to not rely on static access and a global context.

pull/187/head
Eric Kok 10 years ago
parent
commit
04a9da420f
  1. 1
      app/build.gradle
  2. 2
      app/src/main/AndroidManifest.xml
  3. 56
      app/src/main/java/org/transdroid/core/app/settings/ServerSetting.java
  4. 30
      app/src/main/java/org/transdroid/core/gui/DetailsActivity.java
  5. 12
      app/src/main/java/org/transdroid/core/gui/DetailsFragment.java
  6. 3
      app/src/main/java/org/transdroid/core/gui/ServerStatusView.java
  7. 62
      app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java
  8. 10
      app/src/main/java/org/transdroid/core/gui/TorrentsFragment.java
  9. 2
      app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java
  10. 6
      app/src/main/java/org/transdroid/core/gui/log/ErrorLogSender.java
  11. 50
      app/src/main/java/org/transdroid/core/gui/log/Log.java
  12. 7
      app/src/main/java/org/transdroid/core/gui/log/LogUncaughtExceptionHandler.java
  13. 2
      app/src/main/java/org/transdroid/core/gui/navigation/DialogHelper.java
  14. 7
      app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java
  15. 2
      app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java
  16. 157
      app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java
  17. 92
      app/src/main/java/org/transdroid/core/gui/search/SearchActivity.java
  18. 187
      app/src/main/java/org/transdroid/core/gui/search/SearchResultsFragment.java
  19. 265
      app/src/main/java/org/transdroid/core/gui/settings/MainSettingsActivity.java
  20. 4
      app/src/main/java/org/transdroid/core/gui/settings/ServerSettingsActivity.java
  21. 8
      app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettingsActivity.java
  22. 15
      app/src/main/java/org/transdroid/core/service/AppUpdateService.java
  23. 52
      app/src/main/java/org/transdroid/core/service/BootReceiver.java
  24. 56
      app/src/main/java/org/transdroid/core/service/ControlService.java
  25. 50
      app/src/main/java/org/transdroid/core/service/RssCheckerService.java
  26. 10
      app/src/main/java/org/transdroid/core/service/ServerCheckerService.java
  27. 178
      app/src/main/java/org/transdroid/core/widget/ListWidgetConfigActivity.java
  28. 113
      app/src/main/java/org/transdroid/core/widget/ListWidgetProvider.java
  29. 100
      app/src/main/java/org/transdroid/core/widget/ListWidgetViewsService.java
  30. 16
      app/src/main/java/org/transdroid/daemon/AlphanumComparator.java
  31. 388
      app/src/main/java/org/transdroid/daemon/Aria2c/Aria2Adapter.java
  32. 653
      app/src/main/java/org/transdroid/daemon/BitComet/BitCometAdapter.java
  33. 111
      app/src/main/java/org/transdroid/daemon/Bitflu/BitfluAdapter.java
  34. 139
      app/src/main/java/org/transdroid/daemon/BuffaloNas/BuffaloNasAdapter.java
  35. 199
      app/src/main/java/org/transdroid/daemon/DLinkRouterBT/DLinkRouterBTAdapter.java
  36. 4
      app/src/main/java/org/transdroid/daemon/Daemon.java
  37. 636
      app/src/main/java/org/transdroid/daemon/Deluge/DelugeAdapter.java
  38. 10
      app/src/main/java/org/transdroid/daemon/DummyAdapter.java
  39. 3
      app/src/main/java/org/transdroid/daemon/IDaemonAdapter.java
  40. 392
      app/src/main/java/org/transdroid/daemon/Ktorrent/KtorrentAdapter.java
  41. 95
      app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java
  42. 372
      app/src/main/java/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java
  43. 271
      app/src/main/java/org/transdroid/daemon/Synology/SynologyAdapter.java
  44. 181
      app/src/main/java/org/transdroid/daemon/TaskQueue.java
  45. 39
      app/src/main/java/org/transdroid/daemon/Tfb4rt/Tfb4rtAdapter.java
  46. 4
      app/src/main/java/org/transdroid/daemon/TorrentFilesComparator.java
  47. 20
      app/src/main/java/org/transdroid/daemon/TorrentsComparator.java
  48. 621
      app/src/main/java/org/transdroid/daemon/Transmission/TransmissionAdapter.java
  49. 607
      app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java
  50. 28
      app/src/main/java/org/transdroid/daemon/Vuze/VuzeAdapter.java
  51. 76
      app/src/main/java/org/transdroid/daemon/task/DaemonTask.java
  52. 7
      app/src/main/java/org/transdroid/daemon/util/Collections2.java
  53. 59
      app/src/main/java/org/transdroid/daemon/util/DLog.java
  54. 158
      app/src/main/java/org/transdroid/daemon/util/HttpHelper.java
  55. 44
      app/src/main/java/org/transdroid/daemon/util/ITLogger.java
  56. 77
      app/src/main/java/org/transdroid/daemon/util/Pair.java
  57. 19
      app/src/main/java/org/transdroid/daemon/util/SelfSignedTrustManager.java
  58. 3
      app/src/main/java/org/transdroid/daemon/util/TlsSniSocketFactory.java

1
app/build.gradle

@ -49,6 +49,7 @@ dependencies { @@ -49,6 +49,7 @@ dependencies {
compile 'com.github.chrisbanes.actionbarpulltorefresh:library:0.8'
compile 'de.keyboardsurfer.android.widget:crouton:1.8.+'
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.+'
compile 'com.android.support:support-annotations:20.0.0'
apt "org.androidannotations:androidannotations:3.1"
}

2
app/src/main/AndroidManifest.xml

@ -245,7 +245,7 @@ @@ -245,7 +245,7 @@
<service android:name="org.transdroid.core.service.AppUpdateService_" />
<receiver android:name="org.transdroid.core.service.AlarmReceiver_" />
<receiver android:name="org.transdroid.core.service.BootReceiver" >
<receiver android:name="org.transdroid.core.service.BootReceiver_" >
<intent-filter>
<action
android:name="android.intent.action.BOOT_COMPLETED"

56
app/src/main/java/org/transdroid/core/app/settings/ServerSetting.java

@ -16,17 +16,17 @@ @@ -16,17 +16,17 @@
*/
package org.transdroid.core.app.settings;
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
import org.transdroid.core.gui.lists.SimpleListItem;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.log.*;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.OS;
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
/**
* Represents a user-configured remote server.
* @author Eric Kok
@ -81,10 +81,10 @@ public class ServerSetting implements SimpleListItem { @@ -81,10 +81,10 @@ public class ServerSetting implements SimpleListItem {
* @param isAutoGenerated Whether this setting was generated rather than manually inputed by the user
*/
public ServerSetting(int key, String name, Daemon type, String address, String localAddress, int localPort,
String localNetwork, int port, boolean ssl, boolean sslTrustAll, String sslTrustKey, String folder,
boolean useAuthentication, String username, String password, String extraPass, OS os, String downloadDir,
String ftpUrl, String ftpPassword, int timeout, boolean alarmOnFinishedDownload, boolean alarmOnNewTorrent,
boolean isAutoGenerated) {
String localNetwork, int port, boolean ssl, boolean sslTrustAll, String sslTrustKey,
String folder, boolean useAuthentication, String username, String password, String extraPass,
OS os, String downloadDir, String ftpUrl, String ftpPassword, int timeout,
boolean alarmOnFinishedDownload, boolean alarmOnNewTorrent, boolean isAutoGenerated) {
this.key = key;
this.name = name;
this.type = type;
@ -113,8 +113,9 @@ public class ServerSetting implements SimpleListItem { @@ -113,8 +113,9 @@ public class ServerSetting implements SimpleListItem {
@Override
public String getName() {
if (!TextUtils.isEmpty(name))
if (!TextUtils.isEmpty(name)) {
return name;
}
if (!TextUtils.isEmpty(address)) {
String host = Uri.parse(address).getHost();
return host == null ? DEFAULT_NAME : host;
@ -221,13 +222,13 @@ public class ServerSetting implements SimpleListItem { @@ -221,13 +222,13 @@ public class ServerSetting implements SimpleListItem {
public String getHumanReadableIdentifier() {
if (isAutoGenerated) {
// Hide the 'implementation details'; just give the username and server
return (this.shouldUseAuthentication() && !TextUtils.isEmpty(this.getUsername()) ? this.getUsername() + "@"
: "") + getAddress();
return (this.shouldUseAuthentication() && !TextUtils.isEmpty(this.getUsername()) ?
this.getUsername() + "@" : "") + getAddress();
}
return (this.ssl ? "https://" : "http://")
+ (this.shouldUseAuthentication() && !TextUtils.isEmpty(this.getUsername()) ? this.getUsername() + "@"
: "") + getAddress() + ":" + getPort()
+ (Daemon.supportsCustomFolder(getType()) && getFolder() != null ? getFolder() : "");
return (this.ssl ? "https://" : "http://") +
(this.shouldUseAuthentication() && !TextUtils.isEmpty(this.getUsername()) ? this.getUsername() + "@" :
"") + getAddress() + ":" + getPort() +
(Daemon.supportsCustomFolder(getType()) && getFolder() != null ? getFolder() : "");
}
/**
@ -235,11 +236,12 @@ public class ServerSetting implements SimpleListItem { @@ -235,11 +236,12 @@ public class ServerSetting implements SimpleListItem {
* order/index. THis may be used to store additional details about this server elsewhere. It may change if the user
* changes server settings, but not with name or notification settings.
* @return A unique identifying string, based primarily on the configured address, port number, SSL settings and
* user name; returns null if the server is not yet fully identifiable (during configuration, for example)
* user name; returns null if the server is not yet fully identifiable (during configuration, for example)
*/
public String getUniqueIdentifier() {
if (getType() == null || getAddress() == null || getAddress().equals(""))
if (getType() == null || getAddress() == null || getAddress().equals("")) {
return null;
}
return getType().toString() + "|" + getHumanReadableIdentifier();
}
@ -260,8 +262,8 @@ public class ServerSetting implements SimpleListItem { @@ -260,8 +262,8 @@ public class ServerSetting implements SimpleListItem {
/**
* Returns the appropriate daemon adapter to which tasks can be executed, in accordance with this server's settings
* @param connectedToNetwork The name of the (wifi) network we are currently connected to, or null if this could not
* be determined
* @param context
* be determined
* @param context A context to access the logger
* @return An IDaemonAdapter instance of the specific torrent client daemon type
*/
public IDaemonAdapter createServerAdapter(String connectedToNetwork, Context context) {
@ -271,7 +273,7 @@ public class ServerSetting implements SimpleListItem { @@ -271,7 +273,7 @@ public class ServerSetting implements SimpleListItem {
/**
* Converts local server settings into an old-style {@link DaemonSettings} object.
* @param connectedToNetwork The name of the (wifi) network we are currently connected to, or null if this could not
* be determined
* be determined
* @param caller A context to access the logger
* @return A {@link DaemonSettings} object to execute server commands against
*/
@ -279,13 +281,15 @@ public class ServerSetting implements SimpleListItem { @@ -279,13 +281,15 @@ public class ServerSetting implements SimpleListItem {
// The local integer key is converted to the idString string.
// The host name address used is dependent on the network that we are currently connected to (to allow a
// distinct connection IP or host name when connected to a local network).
if (!TextUtils.isEmpty(localNetwork))
Log.d(caller, "Creating adapter for " + name + " of type " + type.name() + ": connected to "
+ connectedToNetwork + " and configured local network is " + localNetwork);
if (!TextUtils.isEmpty(localNetwork)) {
Log_.getInstance_(caller)
.d("ServerSetting", "Creating adapter for " + name + " of type " + type.name() + ": connected to " +
connectedToNetwork + " and configured local network is " + localNetwork);
}
String addressToUse = address;
int portToUse = port;
if (!TextUtils.isEmpty(localNetwork) && !TextUtils.isEmpty(localAddress)
&& !TextUtils.isEmpty(connectedToNetwork)) {
if (!TextUtils.isEmpty(localNetwork) && !TextUtils.isEmpty(localAddress) &&
!TextUtils.isEmpty(connectedToNetwork)) {
String[] localNetworks = localNetwork.split("\\|");
for (String network : localNetworks) {
if (connectedToNetwork.equals(network)) {

30
app/src/main/java/org/transdroid/core/gui/DetailsActivity.java

@ -95,6 +95,8 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -95,6 +95,8 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
// Settings
@Bean
protected Log log;
@Bean
protected NavigationHelper navigationHelper;
@Bean
protected ConnectivityHelper connectivityHelper;
@ -185,7 +187,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -185,7 +187,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Background
protected void refreshTorrent() {
DaemonTaskResult result = RetrieveTask.create(currentConnection).execute();
DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(log);
if (result instanceof RetrieveTaskSuccessResult) {
onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(),
((RetrieveTaskSuccessResult) result).getLabels());
@ -198,7 +200,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -198,7 +200,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
public void refreshTorrentDetails(Torrent torrent) {
if (!Daemon.supportsFineDetails(torrent.getDaemon()))
return;
DaemonTaskResult result = GetTorrentDetailsTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = GetTorrentDetailsTask.create(currentConnection, torrent).execute(log);
if (result instanceof GetTorrentDetailsTaskSuccessResult) {
onTorrentDetailsRetrieved(torrent, ((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails());
} else {
@ -210,7 +212,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -210,7 +212,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
public void refreshTorrentFiles(Torrent torrent) {
if (!Daemon.supportsFileListing(torrent.getDaemon()))
return;
DaemonTaskResult result = GetFileListTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = GetFileListTask.create(currentConnection, torrent).execute(log);
if (result instanceof GetFileListTaskSuccessResult) {
onTorrentFilesRetrieved(torrent, ((GetFileListTaskSuccessResult) result).getFiles());
} else {
@ -222,7 +224,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -222,7 +224,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Override
public void resumeTorrent(Torrent torrent) {
torrent.mimicResume();
DaemonTaskResult result = ResumeTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = ResumeTask.create(currentConnection, torrent).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_resumed, torrent.getName()));
} else {
@ -234,7 +236,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -234,7 +236,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Override
public void pauseTorrent(Torrent torrent) {
torrent.mimicPause();
DaemonTaskResult result = PauseTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = PauseTask.create(currentConnection, torrent).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_paused, torrent.getName()));
} else {
@ -246,7 +248,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -246,7 +248,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Override
public void startTorrent(Torrent torrent, boolean forced) {
torrent.mimicStart();
DaemonTaskResult result = StartTask.create(currentConnection, torrent, forced).execute();
DaemonTaskResult result = StartTask.create(currentConnection, torrent, forced).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_started, torrent.getName()));
} else {
@ -258,7 +260,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -258,7 +260,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Override
public void stopTorrent(Torrent torrent) {
torrent.mimicStop();
DaemonTaskResult result = StopTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = StopTask.create(currentConnection, torrent).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_stopped, torrent.getName()));
} else {
@ -269,7 +271,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -269,7 +271,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Background
@Override
public void removeTorrent(Torrent torrent, boolean withData) {
DaemonTaskResult result = RemoveTask.create(currentConnection, torrent, withData).execute();
DaemonTaskResult result = RemoveTask.create(currentConnection, torrent, withData).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
// Close the details activity (as the torrent is now removed)
closeActivity(getString(withData ? R.string.result_removed_with_data : R.string.result_removed,
@ -293,7 +295,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -293,7 +295,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
public void updateLabel(Torrent torrent, String newLabel) {
torrent.mimicNewLabel(newLabel);
DaemonTaskResult result = SetLabelTask.create(currentConnection, torrent, newLabel == null ? "" : newLabel)
.execute();
.execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_labelset, newLabel));
} else {
@ -305,7 +307,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -305,7 +307,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Override
public void forceRecheckTorrent(Torrent torrent) {
torrent.mimicCheckingStatus();
DaemonTaskResult result = ForceRecheckTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = ForceRecheckTask.create(currentConnection, torrent).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result,
getString(R.string.result_recheckedstarted, torrent.getName()));
@ -317,7 +319,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -317,7 +319,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Background
@Override
public void updateTrackers(Torrent torrent, List<String> newTrackers) {
DaemonTaskResult result = SetTrackersTask.create(currentConnection, torrent, newTrackers).execute();
DaemonTaskResult result = SetTrackersTask.create(currentConnection, torrent, newTrackers).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_trackersupdated));
} else {
@ -328,7 +330,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -328,7 +330,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Background
@Override
public void updateLocation(Torrent torrent, String newLocation) {
DaemonTaskResult result = SetDownloadLocationTask.create(currentConnection, torrent, newLocation).execute();
DaemonTaskResult result = SetDownloadLocationTask.create(currentConnection, torrent, newLocation).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_locationset, newLocation));
} else {
@ -340,7 +342,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -340,7 +342,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Override
public void updatePriority(Torrent torrent, List<TorrentFile> files, Priority priority) {
DaemonTaskResult result = SetFilePriorityTask.create(currentConnection, torrent, priority,
new ArrayList<TorrentFile>(files)).execute();
new ArrayList<TorrentFile>(files)).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_priotitiesset));
} else {
@ -373,7 +375,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -373,7 +375,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@UiThread
protected void onCommunicationError(DaemonTaskFailureResult result, boolean isCritical) {
Log.i(this, result.getException().toString());
log.i(this, result.getException().toString());
String error = getString(LocalTorrent.getResourceForDaemonException(result.getException()));
fragmentDetails.updateIsLoading(false, isCritical ? error : null);
Crouton.showText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())),

12
app/src/main/java/org/transdroid/core/gui/DetailsFragment.java

@ -29,16 +29,10 @@ import org.androidannotations.annotations.OptionsItem; @@ -29,16 +29,10 @@ import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.gui.lists.DetailsAdapter;
import org.transdroid.core.gui.lists.SimpleListItemAdapter;
import org.transdroid.core.gui.navigation.Label;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.navigation.NavigationHelper_;
import org.transdroid.core.gui.navigation.RefreshableActivity;
import org.transdroid.core.gui.navigation.SelectionManagerMode;
import org.transdroid.core.gui.navigation.SetLabelDialog;
import org.transdroid.core.gui.navigation.*;
import org.transdroid.core.gui.navigation.SetLabelDialog.OnLabelPickedListener;
import org.transdroid.core.gui.navigation.SetStorageLocationDialog;
import org.transdroid.core.gui.navigation.SetStorageLocationDialog.OnStorageLocationUpdatedListener;
@ -180,7 +174,7 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -180,7 +174,7 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
/**
* Updates the list adapter to show a new list of torrent files, replacing the old files list.
* @param checkTorrent The torrent for which the details were retrieved
* @param newTorrents The new, updated list of torrent file objects
* @param newTorrentFiles The new, updated list of torrent file objects
*/
public void updateTorrentFiles(Torrent checkTorrent, ArrayList<TorrentFile> newTorrentFiles) {
// Check if these are actually the details of the torrent we are now showing

3
app/src/main/java/org/transdroid/core/gui/ServerStatusView.java

@ -54,7 +54,7 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis @@ -54,7 +54,7 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis
/**
* Updates the statistics as shown in the action bar through this server status view.
* @param torrents The most recently received list of torrents
* @param dormantAsInactive
* @param dormantAsInactive Whether to treat dormant (0KB/s) torrent as inactive state torrents
*/
public void update(List<Torrent> torrents, boolean dormantAsInactive) {
@ -66,6 +66,7 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis @@ -66,6 +66,7 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis
downcountSign.setVisibility(View.INVISIBLE);
upcountSign.setVisibility(View.INVISIBLE);
speedswrapperLayout.setOnClickListener(null);
return;
}
int downcount = 0, upcount = 0, downspeed = 0, upspeed = 0;

62
app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java

@ -92,7 +92,6 @@ import org.transdroid.daemon.task.SetTrackersTask; @@ -92,7 +92,6 @@ import org.transdroid.daemon.task.SetTrackersTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.task.StartTask;
import org.transdroid.daemon.task.StopTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher;
@ -136,6 +135,8 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -136,6 +135,8 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
// Navigation components
@Bean
protected Log log;
@Bean
protected NavigationHelper navigationHelper;
@Bean
protected ConnectivityHelper connectivityHelper;
@ -231,9 +232,6 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -231,9 +232,6 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
}
actionBar.setListNavigationCallbacks(navigationSpinnerAdapter, this);
// Log messages from the server daemons using our singleton logger
DLog.setLogger(Log_.getInstance_(this));
// Load the default server or a server that was explicitly supplied in the starting intent
ServerSetting defaultServer = applicationSettings.getDefaultServer();
if (defaultServer == null) {
@ -246,7 +244,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -246,7 +244,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
// A server settings order ID was provided in this org.transdroid.START_SERVER action intent
int serverId = getIntent().getExtras().getInt(ListWidgetProvider.EXTRA_SERVER);
if (serverId < 0 || serverId > applicationSettings.getMaxOfAllServers()) {
Log.e(this, "Tried to start with " + ListWidgetProvider.EXTRA_SERVER + " intent but " + serverId
log.e(this, "Tried to start with " + ListWidgetProvider.EXTRA_SERVER + " intent but " + serverId
+ " is not an existing server order id");
} else {
defaultServer = applicationSettings.getServerSetting(serverId);
@ -853,7 +851,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -853,7 +851,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
protected void refreshTorrents() {
String startConnectionId = currentConnection.getSettings().getIdString();
DaemonTaskResult result = RetrieveTask.create(currentConnection).execute();
DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(log);
if (!startConnectionId.equals(currentConnection.getSettings().getIdString())) {
// During the command execution the user changed the server, so we are no longer interested in the result
return;
@ -871,7 +869,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -871,7 +869,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
if (!Daemon.supportsFineDetails(currentConnection.getType()))
return;
String startConnectionId = currentConnection.getSettings().getIdString();
DaemonTaskResult result = GetTorrentDetailsTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = GetTorrentDetailsTask.create(currentConnection, torrent).execute(log);
if (!startConnectionId.equals(currentConnection.getSettings().getIdString())) {
// During the command execution the user changed the server, so we are no longer interested in the result
return;
@ -888,7 +886,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -888,7 +886,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
if (!Daemon.supportsFileListing(currentConnection.getType()))
return;
String startConnectionId = currentConnection.getSettings().getIdString();
DaemonTaskResult result = GetFileListTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = GetFileListTask.create(currentConnection, torrent).execute(log);
if (!startConnectionId.equals(currentConnection.getSettings().getIdString())) {
// During the command execution the user changed the server, so we are no longer interested in the result
return;
@ -903,7 +901,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -903,7 +901,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
protected void getAdditionalStats() {
String startConnectionId = currentConnection.getSettings().getIdString();
DaemonTaskResult result = GetStatsTask.create(currentConnection).execute();
DaemonTaskResult result = GetStatsTask.create(currentConnection).execute(log);
if (!startConnectionId.equals(currentConnection.getSettings().getIdString())) {
// During the command execution the user changed the server, so we are no longer interested in the result
return;
@ -918,7 +916,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -918,7 +916,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
protected void updateTurtleMode(boolean enable) {
String startConnectionId = currentConnection.getSettings().getIdString();
DaemonTaskResult result = SetAlternativeModeTask.create(currentConnection, enable).execute();
DaemonTaskResult result = SetAlternativeModeTask.create(currentConnection, enable).execute(log);
if (!startConnectionId.equals(currentConnection.getSettings().getIdString())) {
// During the command execution the user changed the server, so we are no longer interested in the result
return;
@ -933,7 +931,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -933,7 +931,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
public void addTorrentByUrl(String url, String title) {
DaemonTaskResult result = AddByUrlTask.create(currentConnection, url, title).execute();
DaemonTaskResult result = AddByUrlTask.create(currentConnection, url, title).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title));
refreshTorrents();
@ -944,7 +942,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -944,7 +942,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
public void addTorrentByMagnetUrl(String url, String title) {
DaemonTaskResult result = AddByMagnetUrlTask.create(currentConnection, url).execute();
DaemonTaskResult result = AddByMagnetUrlTask.create(currentConnection, url).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title));
refreshTorrents();
@ -955,7 +953,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -955,7 +953,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
protected void addTorrentByFile(String localFile, String title) {
DaemonTaskResult result = AddByFileTask.create(currentConnection, localFile).execute();
DaemonTaskResult result = AddByFileTask.create(currentConnection, localFile).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title));
refreshTorrents();
@ -971,10 +969,10 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -971,10 +969,10 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
addTorrentFromStream(getContentResolver().openInputStream(contentUri), title);
} catch (SecurityException e) {
// No longer access to this file
Log.e(this, "No access given to " + contentUri.toString() + ": " + e.toString());
log.e(this, "No access given to " + contentUri.toString() + ": " + e.toString());
Crouton.showText(this, R.string.error_torrentfile, NavigationHelper.CROUTON_ERROR_STYLE);
} catch (FileNotFoundException e) {
Log.e(this, contentUri.toString() + " does not exist: " + e.toString());
log.e(this, contentUri.toString() + " does not exist: " + e.toString());
Crouton.showText(this, R.string.error_torrentfile, NavigationHelper.CROUTON_ERROR_STYLE);
}
}
@ -986,7 +984,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -986,7 +984,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
InputStream input = SearchHelper_.getInstance_(this).getFile(source, url);
addTorrentFromStream(input, title);
} catch (Exception e) {
Log.e(this, "Can't download private site torrent " + url + " from " + source + ": " + e.toString());
log.e(this, "Can't download private site torrent " + url + " from " + source + ": " + e.toString());
Crouton.showText(this, R.string.error_torrentfile, NavigationHelper.CROUTON_ERROR_STYLE);
}
@ -1014,7 +1012,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1014,7 +1012,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED
|| response.getStatusLine().getStatusCode() == HttpStatus.SC_FORBIDDEN
|| response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) {
Log.e(this, "Can't retrieve web torrent " + url + ": Unexpected HTTP response status code "
log.e(this, "Can't retrieve web torrent " + url + ": Unexpected HTTP response status code "
+ response.getStatusLine().toString());
Crouton.showText(this, R.string.error_401, NavigationHelper.CROUTON_ERROR_STYLE);
return;
@ -1022,7 +1020,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1022,7 +1020,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
InputStream input = response.getEntity().getContent();
addTorrentFromStream(input, title);
} catch (Exception e) {
Log.e(this, "Can't retrieve web torrent " + url + ": " + e.toString());
log.e(this, "Can't retrieve web torrent " + url + ": " + e.toString());
Crouton.showText(this, R.string.error_torrentfile, NavigationHelper.CROUTON_ERROR_STYLE);
}
}
@ -1047,14 +1045,14 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1047,14 +1045,14 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
output.close();
}
} catch (IOException e) {
Log.e(this, "Can't write input stream to " + tempFile.toString() + ": " + e.toString());
log.e(this, "Can't write input stream to " + tempFile.toString() + ": " + e.toString());
Crouton.showText(this, R.string.error_torrentfile, NavigationHelper.CROUTON_ERROR_STYLE);
} finally {
try {
if (input != null)
input.close();
} catch (IOException e) {
Log.e(this, "Error closing the input stream " + tempFile.toString() + ": " + e.toString());
log.e(this, "Error closing the input stream " + tempFile.toString() + ": " + e.toString());
Crouton.showText(this, R.string.error_torrentfile, NavigationHelper.CROUTON_ERROR_STYLE);
}
}
@ -1064,7 +1062,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1064,7 +1062,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Override
public void resumeTorrent(Torrent torrent) {
torrent.mimicResume();
DaemonTaskResult result = ResumeTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = ResumeTask.create(currentConnection, torrent).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_resumed, torrent.getName()));
} else {
@ -1076,7 +1074,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1076,7 +1074,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Override
public void pauseTorrent(Torrent torrent) {
torrent.mimicPause();
DaemonTaskResult result = PauseTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = PauseTask.create(currentConnection, torrent).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_paused, torrent.getName()));
} else {
@ -1088,7 +1086,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1088,7 +1086,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Override
public void startTorrent(Torrent torrent, boolean forced) {
torrent.mimicStart();
DaemonTaskResult result = StartTask.create(currentConnection, torrent, forced).execute();
DaemonTaskResult result = StartTask.create(currentConnection, torrent, forced).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_started, torrent.getName()));
} else {
@ -1100,7 +1098,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1100,7 +1098,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Override
public void stopTorrent(Torrent torrent) {
torrent.mimicStop();
DaemonTaskResult result = StopTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = StopTask.create(currentConnection, torrent).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_stopped, torrent.getName()));
} else {
@ -1111,7 +1109,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1111,7 +1109,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
@Override
public void removeTorrent(Torrent torrent, boolean withData) {
DaemonTaskResult result = RemoveTask.create(currentConnection, torrent, withData).execute();
DaemonTaskResult result = RemoveTask.create(currentConnection, torrent, withData).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded(
(DaemonTaskSuccessResult) result,
@ -1126,7 +1124,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1126,7 +1124,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
public void updateLabel(Torrent torrent, String newLabel) {
torrent.mimicNewLabel(newLabel);
DaemonTaskResult result = SetLabelTask.create(currentConnection, torrent, newLabel == null ? "" : newLabel)
.execute();
.execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded(
(DaemonTaskSuccessResult) result,
@ -1141,7 +1139,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1141,7 +1139,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Override
public void forceRecheckTorrent(Torrent torrent) {
torrent.mimicCheckingStatus();
DaemonTaskResult result = ForceRecheckTask.create(currentConnection, torrent).execute();
DaemonTaskResult result = ForceRecheckTask.create(currentConnection, torrent).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result,
getString(R.string.result_recheckedstarted, torrent.getName()));
@ -1153,7 +1151,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1153,7 +1151,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
@Override
public void updateTrackers(Torrent torrent, List<String> newTrackers) {
DaemonTaskResult result = SetTrackersTask.create(currentConnection, torrent, newTrackers).execute();
DaemonTaskResult result = SetTrackersTask.create(currentConnection, torrent, newTrackers).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_trackersupdated));
} else {
@ -1164,7 +1162,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1164,7 +1162,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
@Override
public void updateLocation(Torrent torrent, String newLocation) {
DaemonTaskResult result = SetDownloadLocationTask.create(currentConnection, torrent, newLocation).execute();
DaemonTaskResult result = SetDownloadLocationTask.create(currentConnection, torrent, newLocation).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_locationset, newLocation));
} else {
@ -1176,7 +1174,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1176,7 +1174,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Override
public void updatePriority(Torrent torrent, List<TorrentFile> files, Priority priority) {
DaemonTaskResult result = SetFilePriorityTask.create(currentConnection, torrent, priority,
new ArrayList<TorrentFile>(files)).execute();
new ArrayList<TorrentFile>(files)).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_priotitiesset));
} else {
@ -1187,7 +1185,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1187,7 +1185,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@Background
public void updateMaxSpeeds(Integer maxDownloadSpeed, Integer maxUploadSpeed) {
DaemonTaskResult result = SetTransferRatesTask.create(currentConnection, maxUploadSpeed, maxDownloadSpeed)
.execute();
.execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_maxspeedsset));
} else {
@ -1204,7 +1202,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener, @@ -1204,7 +1202,7 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@UiThread
protected void onCommunicationError(DaemonTaskFailureResult result, boolean isCritical) {
Log.i(this, result.getException().toString());
log.i(this, result.getException().toString());
String error = getString(LocalTorrent.getResourceForDaemonException(result.getException()));
Crouton.showText(this, error, NavigationHelper.CROUTON_ERROR_STYLE);
fragmentTorrents.updateIsLoading(false);

10
app/src/main/java/org/transdroid/core/gui/TorrentsFragment.java

@ -31,8 +31,7 @@ import org.androidannotations.annotations.ViewById; @@ -31,8 +31,7 @@ import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.SystemSettings;
import org.transdroid.core.gui.lists.TorrentsAdapter;
import org.transdroid.core.gui.lists.TorrentsAdapter_;
import org.transdroid.core.gui.lists.*;
import org.transdroid.core.gui.navigation.Label;
import org.transdroid.core.gui.navigation.NavigationFilter;
import org.transdroid.core.gui.navigation.RefreshableActivity;
@ -158,7 +157,8 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -158,7 +157,8 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
/**
* Clears the currently visible list of torrents.
* @param b
* @param clearError Also clear any error message
* @param clearFilter Also clear any selected filter
*/
public void clear(boolean clearError, boolean clearFilter) {
this.torrents = null;
@ -213,14 +213,14 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -213,14 +213,14 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
// Filter the list of torrents to show according to navigation and text filters
ArrayList<Torrent> filteredTorrents = new ArrayList<Torrent>(torrents);
if (filteredTorrents != null && currentNavigationFilter != null) {
if (currentNavigationFilter != null) {
// Remove torrents that do not match the selected navigation filter
for (Iterator<Torrent> torrentIter = filteredTorrents.iterator(); torrentIter.hasNext();) {
if (!currentNavigationFilter.matches(torrentIter.next(), systemSettings.treatDormantAsInactive()))
torrentIter.remove();
}
}
if (filteredTorrents != null && currentTextFilter != null) {
if (currentTextFilter != null) {
// Remove torrent that do not contain the text filter string
for (Iterator<Torrent> torrentIter = filteredTorrents.iterator(); torrentIter.hasNext();) {
if (!torrentIter.next().getName().toLowerCase(Locale.getDefault())

2
app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java

@ -20,7 +20,7 @@ import java.util.ArrayList; @@ -20,7 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import org.transdroid.R;
import org.transdroid.core.gui.navigation.FilterSeparatorView_;
import org.transdroid.core.gui.navigation.*;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentFile;

6
app/src/main/java/org/transdroid/core/gui/log/ErrorLogSender.java

@ -35,6 +35,8 @@ import com.j256.ormlite.dao.Dao; @@ -35,6 +35,8 @@ import com.j256.ormlite.dao.Dao;
@EBean
public class ErrorLogSender {
@Bean
protected Log log;
@Bean
protected NavigationHelper navigationHelper;
@OrmLiteDao(helper = DatabaseHelper.class, model = ErrorLogEntry.class)
@ -81,11 +83,11 @@ public class ErrorLogSender { @@ -81,11 +83,11 @@ public class ErrorLogSender {
callingActivity.startActivity(Intent.createChooser(target,
callingActivity.getString(R.string.pref_sendlog)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} catch (ActivityNotFoundException e) {
Log.i(callingActivity, "Tried to send error log, but there is no email app installed.");
log.i(callingActivity, "Tried to send error log, but there is no email app installed.");
}
} catch (SQLException e) {
Log.e(callingActivity, "Cannot read the error log to build an error report to send: " + e.toString());
log.e(callingActivity, "Cannot read the error log to build an error report to send: " + e.toString());
}
}

50
app/src/main/java/org/transdroid/core/gui/log/Log.java

@ -16,44 +16,36 @@ @@ -16,44 +16,36 @@
*/
package org.transdroid.core.gui.log;
import java.sql.SQLException;
import java.util.Date;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.DeleteBuilder;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.EBean.Scope;
import org.androidannotations.annotations.OrmLiteDao;
import org.transdroid.BuildConfig;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.daemon.util.ITLogger;
import android.content.Context;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.DeleteBuilder;
import java.util.Date;
/**
* Application-wide logging class that registers entries in the database (for a certain time).
* @author Eric Kok
*/
@EBean(scope = Scope.Singleton)
public class Log implements ITLogger {
public class Log {
public static final String LOG_NAME = "Transdroid";
private static final long MAX_LOG_AGE = 15 * 60 * 1000; // 15 minutes
// Access to resources and database in local singleton instance
private Context context;
@OrmLiteDao(helper = DatabaseHelper.class, model = ErrorLogEntry.class)
Dao<ErrorLogEntry, Integer> errorLogDao;
protected Log(Context context) {
this.context = context;
protected void log(Object object, int priority, String message) {
log(object instanceof String ? (String) object : object.getClass().getSimpleName(), priority, message);
}
protected void log(String logName, int priority, String message) {
if (BuildConfig.DEBUG)
if (BuildConfig.DEBUG) {
android.util.Log.println(priority, LOG_NAME, message);
}
try {
// Store this log message to the database
errorLogDao.create(new ErrorLogEntry(priority, logName, message));
@ -65,27 +57,17 @@ public class Log implements ITLogger { @@ -65,27 +57,17 @@ public class Log implements ITLogger {
android.util.Log.e(LOG_NAME, "Cannot write log message to database: " + e.toString());
}
}
public static void e(Context caller, String message) {
Log_.getInstance_(caller).log(caller.getClass().toString(), android.util.Log.ERROR, message);
}
public static void i(Context caller, String message) {
Log_.getInstance_(caller).log(caller.getClass().toString(), android.util.Log.INFO, message);
}
public static void d(Context caller, String message) {
Log_.getInstance_(caller).log(caller.getClass().toString(), android.util.Log.DEBUG, message);
public void d(Object object, String msg) {
log(object, android.util.Log.DEBUG, msg);
}
@Override
public void d(String self, String msg) {
Log.d(context, msg);
public void i(Object object, String msg) {
log(object, android.util.Log.DEBUG, msg);
}
@Override
public void e(String self, String msg) {
Log.e(context, msg);
public void e(Object object, String msg) {
log(object, android.util.Log.ERROR, msg);
}
}

7
app/src/main/java/org/transdroid/core/gui/log/LogUncaughtExceptionHandler.java

@ -33,14 +33,15 @@ public class LogUncaughtExceptionHandler implements Thread.UncaughtExceptionHand @@ -33,14 +33,15 @@ public class LogUncaughtExceptionHandler implements Thread.UncaughtExceptionHand
// Write exception stack trace to the log
String prefix = "E: ";
Log.e(context, prefix + ex.toString());
Log_ log = Log_.getInstance_(context);
log.e(this, prefix + ex.toString());
if (ex.getCause() != null) {
for (StackTraceElement e : ex.getCause().getStackTrace()) {
Log.e(context, prefix + e.toString());
log.e(this, prefix + e.toString());
}
}
for (StackTraceElement e : ex.getStackTrace()) {
Log.e(context, prefix + e.toString());
log.e(this, prefix + e.toString());
}
// Rely on default Android exception handling

2
app/src/main/java/org/transdroid/core/gui/navigation/DialogHelper.java

@ -20,7 +20,7 @@ import java.io.Serializable; @@ -20,7 +20,7 @@ import java.io.Serializable;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.Extra;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.*;
import android.app.Activity;
import android.app.Dialog;

7
app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java

@ -49,6 +49,8 @@ public class RssfeedsActivity extends Activity { @@ -49,6 +49,8 @@ public class RssfeedsActivity extends Activity {
// Settings and local data
@Bean
protected Log log;
@Bean
protected ApplicationSettings applicationSettings;
protected List<RssfeedLoader> loaders;
@ -116,7 +118,7 @@ public class RssfeedsActivity extends Activity { @@ -116,7 +118,7 @@ public class RssfeedsActivity extends Activity {
} catch (Exception e) {
// Catch any error that may occurred and register this failure
handleRssfeedResult(loader, null, true);
Log.i(this, "RSS feed " + loader.getSetting().getUrl() + " error: " + e.toString());
log.i(this, "RSS feed " + loader.getSetting().getUrl() + " error: " + e.toString());
}
}
@ -182,8 +184,7 @@ public class RssfeedsActivity extends Activity { @@ -182,8 +184,7 @@ public class RssfeedsActivity extends Activity {
if (TextUtils.isEmpty(name))
name = loader.getSetting().getName();
if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(loader.getSetting().getUrl())) {
String host = Uri.parse(loader.getSetting().getUrl()).getHost();
name = host;
name = Uri.parse(loader.getSetting().getUrl()).getHost();
}
RssitemsActivity_.intent(this).rssfeed(loader.getChannel()).rssfeedName(name).start();

2
app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java

@ -26,7 +26,7 @@ import org.androidannotations.annotations.OptionsItem; @@ -26,7 +26,7 @@ import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.gui.settings.MainSettingsActivity_;
import org.transdroid.core.gui.settings.*;
import android.app.Fragment;
import android.view.Menu;

157
app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java

@ -16,23 +16,6 @@ @@ -16,23 +16,6 @@
*/
package org.transdroid.core.gui.rss;
import java.util.ArrayList;
import java.util.List;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.ItemClick;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.navigation.SelectionManagerMode;
import org.transdroid.core.gui.search.SearchActivity_;
import org.transdroid.core.rssparser.Channel;
import org.transdroid.core.rssparser.Item;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@ -53,6 +36,24 @@ import android.widget.AbsListView.MultiChoiceModeListener; @@ -53,6 +36,24 @@ import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.ItemClick;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.gui.*;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.navigation.SelectionManagerMode;
import org.transdroid.core.gui.search.*;
import org.transdroid.core.rssparser.Channel;
import org.transdroid.core.rssparser.Item;
import java.util.ArrayList;
import java.util.List;
import de.keyboardsurfer.android.widget.crouton.Crouton;
/**
@ -70,56 +71,6 @@ public class RssitemsFragment extends Fragment { @@ -70,56 +71,6 @@ public class RssitemsFragment extends Fragment {
// Views
@ViewById(resName = "rssitems_list")
protected ListView rssitemsList;
@Bean
protected RssitemsAdapter rssitemsAdapter;
@ViewById
protected TextView emptyText;
@AfterViews
protected void init() {
// Set up the list adapter, which allows multi-select
rssitemsList.setAdapter(rssitemsAdapter);
rssitemsList.setMultiChoiceModeListener(onItemsSelected);
update(rssfeed, hasError);
}
/**
* Update the shown RSS items in the list.
* @param channel The loaded RSS content channel object
* @param hasError True if there were errors in loading the channel, in which case an error text is shown; false
* otherwise
*/
public void update(Channel channel, boolean hasError) {
rssitemsAdapter.update(channel);
rssitemsList.setVisibility(View.GONE);
emptyText.setVisibility(View.VISIBLE);
if (hasError) {
emptyText.setText(R.string.rss_error);
return;
}
if (channel == null) {
emptyText.setText(R.string.rss_noselection);
return;
}
if (channel.getItems().size() == 0) {
emptyText.setText(R.string.rss_empty);
return;
}
rssitemsList.setVisibility(View.VISIBLE);
emptyText.setVisibility(View.INVISIBLE);
}
@ItemClick(resName = "rssitems_list")
protected void onItemClicked(Item item) {
// Don't broadcast this intent; we can safely assume this is intended for Transdroid only
Intent i = TorrentsActivity_.intent(getActivity()).get();
i.setData(item.getTheLinkUri());
i.putExtra("TORRENT_TITLE", item.getTitle());
startActivity(i);
}
private MultiChoiceModeListener onItemsSelected = new MultiChoiceModeListener() {
SelectionManagerMode selectionManagerMode;
@ -143,13 +94,14 @@ public class RssitemsFragment extends Fragment { @@ -143,13 +94,14 @@ public class RssitemsFragment extends Fragment {
// Get checked torrents
List<Item> checked = new ArrayList<Item>();
for (int i = 0; i < rssitemsList.getCheckedItemPositions().size(); i++) {
if (rssitemsList.getCheckedItemPositions().valueAt(i))
if (rssitemsList.getCheckedItemPositions().valueAt(i)) {
checked.add(rssitemsAdapter.getItem(rssitemsList.getCheckedItemPositions().keyAt(i)));
}
}
int itemId = item.getItemId();
if (itemId == R.id.action_addall) {
// Start an Intent that adds multiple items at once, by supplying the urls and titles as string array
// extras and setting the Intent action to ADD_MULTIPLE
Intent intent = new Intent("org.transdroid.ADD_MULTIPLE");
@ -169,21 +121,23 @@ public class RssitemsFragment extends Fragment { @@ -169,21 +121,23 @@ public class RssitemsFragment extends Fragment {
StringBuilder names = new StringBuilder();
for (int f = 0; f < checked.size(); f++) {
if (f != 0)
if (f != 0) {
names.append("\n");
}
names.append(checked.get(f).getTitle());
}
ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(
Context.CLIPBOARD_SERVICE);
ClipboardManager clipboardManager =
(ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
clipboardManager.setPrimaryClip(ClipData.newPlainText("Transdroid", names.toString()));
mode.finish();
return true;
} else {
// The other items only operate on one (the first) selected item
if (checked.size() < 1)
if (checked.size() < 1) {
return false;
}
final Item first = checked.get(0);
if (itemId == R.id.action_showdetails) {
// Show a dialog box with the RSS item description text
@ -191,7 +145,9 @@ public class RssitemsFragment extends Fragment { @@ -191,7 +145,9 @@ public class RssitemsFragment extends Fragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity()).setMessage(first.getDescription())
.setPositiveButton(R.string.action_close, null).create();
};
}
;
}.show(getFragmentManager(), "RssItemDescription");
} else if (itemId == R.id.action_openwebsite) {
// Open the browser to show the website contained in the item's link tag
@ -212,7 +168,7 @@ public class RssitemsFragment extends Fragment { @@ -212,7 +168,7 @@ public class RssitemsFragment extends Fragment {
}
mode.finish();
return true;
}
}
@ -227,5 +183,54 @@ public class RssitemsFragment extends Fragment { @@ -227,5 +183,54 @@ public class RssitemsFragment extends Fragment {
}
};
@Bean
protected RssitemsAdapter rssitemsAdapter;
@ViewById
protected TextView emptyText;
@AfterViews
protected void init() {
// Set up the list adapter, which allows multi-select
rssitemsList.setAdapter(rssitemsAdapter);
rssitemsList.setMultiChoiceModeListener(onItemsSelected);
update(rssfeed, hasError);
}
/**
* Update the shown RSS items in the list.
* @param channel The loaded RSS content channel object
* @param hasError True if there were errors in loading the channel, in which case an error text is shown; false
* otherwise
*/
public void update(Channel channel, boolean hasError) {
rssitemsAdapter.update(channel);
rssitemsList.setVisibility(View.GONE);
emptyText.setVisibility(View.VISIBLE);
if (hasError) {
emptyText.setText(R.string.rss_error);
return;
}
if (channel == null) {
emptyText.setText(R.string.rss_noselection);
return;
}
if (channel.getItems().size() == 0) {
emptyText.setText(R.string.rss_empty);
return;
}
rssitemsList.setVisibility(View.VISIBLE);
emptyText.setVisibility(View.INVISIBLE);
}
@ItemClick(resName = "rssitems_list")
protected void onItemClicked(Item item) {
// Don't broadcast this intent; we can safely assume this is intended for Transdroid only
Intent i = TorrentsActivity_.intent(getActivity()).get();
i.setData(item.getTheLinkUri());
i.putExtra("TORRENT_TITLE", item.getTitle());
startActivity(i);
}
}

92
app/src/main/java/org/transdroid/core/gui/search/SearchActivity.java

@ -16,25 +16,6 @@ @@ -16,25 +16,6 @@
*/
package org.transdroid.core.gui.search;
import java.util.List;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.FragmentById;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.SystemService;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
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.SystemSettings_;
import org.transdroid.core.app.settings.WebsearchSetting;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.navigation.NavigationHelper;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
@ -54,6 +35,23 @@ import android.widget.ListView; @@ -54,6 +35,23 @@ import android.widget.ListView;
import android.widget.SearchView;
import android.widget.TextView;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.FragmentById;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.SystemService;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.search.SearchHelper;
import org.transdroid.core.app.search.SearchSite;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.gui.*;
import org.transdroid.core.gui.navigation.NavigationHelper;
import java.util.List;
/**
* An activity that shows search results to the user (after a query was supplied by the standard Android search manager)
* and either shows the list of search sites on the left (e.g. on tablets) or allows switching between search sites via
@ -79,11 +77,19 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -79,11 +77,19 @@ public class SearchActivity extends Activity implements OnNavigationListener {
@SystemService
protected SearchManager searchManager;
private MenuItem searchMenu = null;
private SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, SearchHistoryProvider.AUTHORITY,
SearchHistoryProvider.MODE);
private SearchRecentSuggestions suggestions =
new SearchRecentSuggestions(this, SearchHistoryProvider.AUTHORITY, SearchHistoryProvider.MODE);
private List<SearchSetting> searchSites;
private SearchSetting lastUsedSite;
private OnItemClickListener onSearchSiteClicked = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
lastUsedSite = searchSites.get(position);
refreshSearch();
}
};
private String lastUsedQuery;
@Override
@ -129,16 +135,18 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -129,16 +135,18 @@ public class SearchActivity extends Activity implements OnNavigationListener {
searchsitesList.setAdapter(searchSitesAdapter);
searchsitesList.setOnItemClickListener(onSearchSiteClicked);
// Select the last used site; this also starts the search!
if (lastUsedPosition >= 0)
if (lastUsedPosition >= 0) {
searchsitesList.setItemChecked(lastUsedPosition, true);
}
} else {
// Use the action bar spinner to select sites
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
getActionBar().setDisplayShowTitleEnabled(false);
getActionBar().setListNavigationCallbacks(new SearchSettingsDropDownAdapter(this, searchSites), this);
// Select the last used site; this also starts the search!
if (lastUsedPosition >= 0)
if (lastUsedPosition >= 0) {
getActionBar().setSelectedNavigationItem(lastUsedPosition);
}
}
}
@ -166,12 +174,14 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -166,12 +174,14 @@ public class SearchActivity extends Activity implements OnNavigationListener {
menu.findItem(R.id.action_search).setVisible(searchInstalled);
menu.findItem(R.id.action_refresh).setVisible(searchInstalled);
menu.findItem(R.id.action_downloadsearch).setVisible(!searchInstalled);
if (searchsitesList != null)
if (searchsitesList != null) {
searchsitesList.setVisibility(searchInstalled ? View.VISIBLE : View.GONE);
if (searchInstalled)
}
if (searchInstalled) {
getFragmentManager().beginTransaction().show(fragmentResults).commit();
else
} else {
getFragmentManager().beginTransaction().hide(fragmentResults).commit();
}
installmoduleText.setVisibility(searchInstalled ? View.GONE : View.VISIBLE);
return true;
@ -186,17 +196,15 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -186,17 +196,15 @@ public class SearchActivity extends Activity implements OnNavigationListener {
private void handleIntent(Intent intent) {
lastUsedQuery = parseQuery(intent);
// Is this actually a full HTTP URL? Then redirect this request to add the URL directly
if (lastUsedQuery != null
&& (lastUsedQuery.startsWith("http") || lastUsedQuery.startsWith("https")
|| lastUsedQuery.startsWith("magnet") || lastUsedQuery.startsWith("file"))) {
if (lastUsedQuery != null && (lastUsedQuery.startsWith("http") || lastUsedQuery.startsWith("https") ||
lastUsedQuery.startsWith("magnet") || lastUsedQuery.startsWith("file"))) {
// Don't broadcast this intent; we can safely assume this is intended for Transdroid only
Intent i = TorrentsActivity_.intent(this).get();
i.setData(Uri.parse(lastUsedQuery));
startActivity(i);
finish();
return;
}
}
@ -215,15 +223,6 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -215,15 +223,6 @@ public class SearchActivity extends Activity implements OnNavigationListener {
TorrentsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start();
}
private OnItemClickListener onSearchSiteClicked = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
lastUsedSite = searchSites.get(position);
refreshSearch();
}
};
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
lastUsedSite = searchSites.get(itemPosition);
@ -239,11 +238,10 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -239,11 +238,10 @@ public class SearchActivity extends Activity implements OnNavigationListener {
String query = null;
if (intent.getAction().equals(Intent.ACTION_SEARCH)) {
query = intent.getStringExtra(SearchManager.QUERY);
query = intent.getStringExtra(SearchManager.QUERY).trim();
} else if (intent.getAction().equals(Intent.ACTION_SEND)) {
query = SendIntentHelper.cleanUpText(intent);
query = SendIntentHelper.cleanUpText(intent).trim();
}
query = query.trim();
if (query != null && query.length() > 0) {
// Remember this search query to later show as a suggestion
@ -267,7 +265,8 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -267,7 +265,8 @@ public class SearchActivity extends Activity implements OnNavigationListener {
// Start a browser page directly to the requested search results
WebsearchSetting websearch = (WebsearchSetting) lastUsedSite;
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(websearch.getBaseUrl().replace("%s", lastUsedQuery))));
startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(websearch.getBaseUrl().replace("%s", lastUsedQuery))));
finish();
} else if (lastUsedSite instanceof SearchSite) {
@ -275,9 +274,8 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -275,9 +274,8 @@ public class SearchActivity extends Activity implements OnNavigationListener {
// Save the search site currently used to search for future usage
applicationSettings.setLastUsedSearchSite(lastUsedSite);
// Update the activity title (only shown on large devices)
getActionBar().setTitle(
NavigationHelper.buildCondensedFontString(getString(R.string.search_queryonsite, lastUsedQuery,
lastUsedSite.getName())));
getActionBar().setTitle(NavigationHelper.buildCondensedFontString(
getString(R.string.search_queryonsite, lastUsedQuery, lastUsedSite.getName())));
// Ask the results fragment to start a search for the specified query
fragmentResults.startSearch(lastUsedQuery, (SearchSite) lastUsedSite);

187
app/src/main/java/org/transdroid/core/gui/search/SearchResultsFragment.java

@ -16,8 +16,18 @@ @@ -16,8 +16,18 @@
*/
package org.transdroid.core.gui.search;
import java.util.ArrayList;
import java.util.List;
import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Background;
@ -32,24 +42,13 @@ import org.transdroid.core.app.search.SearchHelper; @@ -32,24 +42,13 @@ import org.transdroid.core.app.search.SearchHelper;
import org.transdroid.core.app.search.SearchHelper.SearchSortOrder;
import org.transdroid.core.app.search.SearchResult;
import org.transdroid.core.app.search.SearchSite;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.navigation.NavigationHelper_;
import org.transdroid.core.gui.navigation.SelectionManagerMode;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.gui.*;
import org.transdroid.core.gui.navigation.*;
import java.util.ArrayList;
import java.util.List;
import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import de.keyboardsurfer.android.widget.crouton.Crouton;
/**
@ -69,76 +68,6 @@ public class SearchResultsFragment extends Fragment { @@ -69,76 +68,6 @@ public class SearchResultsFragment extends Fragment {
// Views
@ViewById(resName = "searchresults_list")
protected ListView resultsList;
@Bean
protected SearchResultsAdapter resultsAdapter;
@ViewById
protected TextView emptyText;
@ViewById
protected ProgressBar loadingProgress;
@AfterViews
protected void init() {
// On large screens where this fragment is shown next to the sites list; we show a continues grey vertical line
// to separate the lists visually
if (!NavigationHelper_.getInstance_(getActivity()).isSmallScreen()) {
if (SystemSettings_.getInstance_(getActivity()).useDarkTheme()) {
resultsList.setBackgroundResource(R.drawable.details_list_background_dark);
} else {
resultsList.setBackgroundResource(R.drawable.details_list_background_light);
}
}
// Set up the list adapter, which allows multi-select
resultsList.setAdapter(resultsAdapter);
resultsList.setMultiChoiceModeListener(onItemsSelected);
if (results != null)
showResults();
}
public void startSearch(String query, SearchSite site) {
loadingProgress.setVisibility(View.VISIBLE);
resultsList.setVisibility(View.GONE);
emptyText.setVisibility(View.GONE);
performSearch(query, site);
}
@Background
protected void performSearch(String query, SearchSite site) {
results = searchHelper.search(query, site, SearchSortOrder.BySeeders);
resultsSource = site.isPrivate()? site.getKey(): null;
showResults();
}
@UiThread
protected void showResults() {
loadingProgress.setVisibility(View.GONE);
if (results == null || results.size() == 0) {
resultsList.setVisibility(View.GONE);
emptyText.setVisibility(View.VISIBLE);
return;
}
resultsAdapter.update(results);
resultsList.setVisibility(View.VISIBLE);
emptyText.setVisibility(View.GONE);
}
@ItemClick(resName = "searchresults_list")
protected void onItemClicked(SearchResult item) {
if (item.getTorrentUrl() == null) {
Crouton.showText(getActivity(), R.string.error_notorrentfile, NavigationHelper.CROUTON_ERROR_STYLE);
return;
}
// Don't broadcast this intent; we can safely assume this is intended for Transdroid only
Intent i = TorrentsActivity_.intent(getActivity()).get();
i.setData(Uri.parse(item.getTorrentUrl()));
i.putExtra("TORRENT_TITLE", item.getName());
if (resultsSource != null)
i.putExtra("PRIVATE_SOURCE", resultsSource);
startActivity(i);
}
private MultiChoiceModeListener onItemsSelected = new MultiChoiceModeListener() {
SelectionManagerMode selectionManagerMode;
@ -162,8 +91,9 @@ public class SearchResultsFragment extends Fragment { @@ -162,8 +91,9 @@ public class SearchResultsFragment extends Fragment {
// Get checked torrents
List<SearchResult> checked = new ArrayList<SearchResult>();
for (int i = 0; i < resultsList.getCheckedItemPositions().size(); i++) {
if (resultsList.getCheckedItemPositions().valueAt(i))
if (resultsList.getCheckedItemPositions().valueAt(i)) {
checked.add(resultsAdapter.getItem(resultsList.getCheckedItemPositions().keyAt(i)));
}
}
int itemId = item.getItemId();
@ -179,17 +109,19 @@ public class SearchResultsFragment extends Fragment { @@ -179,17 +109,19 @@ public class SearchResultsFragment extends Fragment {
}
intent.putExtra("TORRENT_URLS", urls);
intent.putExtra("TORRENT_TITLES", titles);
if (resultsSource != null)
if (resultsSource != null) {
intent.putExtra("PRIVATE_SOURCE", resultsSource);
}
startActivity(intent);
mode.finish();
return true;
} else if (itemId == R.id.action_showdetails) {
SearchResult first = checked.get(0);
// Open the torrent's web page in the browser
if (checked.size() > 1)
if (checked.size() > 1) {
Toast.makeText(getActivity(), getString(R.string.search_openingdetails, first.getName()),
Toast.LENGTH_LONG).show();
}
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(first.getDetailsUrl())));
return true;
} else {
@ -208,5 +140,76 @@ public class SearchResultsFragment extends Fragment { @@ -208,5 +140,76 @@ public class SearchResultsFragment extends Fragment {
}
};
@Bean
protected SearchResultsAdapter resultsAdapter;
@ViewById
protected TextView emptyText;
@ViewById
protected ProgressBar loadingProgress;
@AfterViews
protected void init() {
// On large screens where this fragment is shown next to the sites list; we show a continues grey vertical line
// to separate the lists visually
if (!NavigationHelper_.getInstance_(getActivity()).isSmallScreen()) {
if (SystemSettings_.getInstance_(getActivity()).useDarkTheme()) {
resultsList.setBackgroundResource(R.drawable.details_list_background_dark);
} else {
resultsList.setBackgroundResource(R.drawable.details_list_background_light);
}
}
// Set up the list adapter, which allows multi-select
resultsList.setAdapter(resultsAdapter);
resultsList.setMultiChoiceModeListener(onItemsSelected);
if (results != null) {
showResults();
}
}
public void startSearch(String query, SearchSite site) {
loadingProgress.setVisibility(View.VISIBLE);
resultsList.setVisibility(View.GONE);
emptyText.setVisibility(View.GONE);
performSearch(query, site);
}
@Background
protected void performSearch(String query, SearchSite site) {
results = searchHelper.search(query, site, SearchSortOrder.BySeeders);
resultsSource = site.isPrivate() ? site.getKey() : null;
showResults();
}
@UiThread
protected void showResults() {
loadingProgress.setVisibility(View.GONE);
if (results == null || results.size() == 0) {
resultsList.setVisibility(View.GONE);
emptyText.setVisibility(View.VISIBLE);
return;
}
resultsAdapter.update(results);
resultsList.setVisibility(View.VISIBLE);
emptyText.setVisibility(View.GONE);
}
@ItemClick(resName = "searchresults_list")
protected void onItemClicked(SearchResult item) {
if (item.getTorrentUrl() == null) {
Crouton.showText(getActivity(), R.string.error_notorrentfile, NavigationHelper.CROUTON_ERROR_STYLE);
return;
}
// Don't broadcast this intent; we can safely assume this is intended for Transdroid only
Intent i = TorrentsActivity_.intent(getActivity()).get();
i.setData(Uri.parse(item.getTorrentUrl()));
i.putExtra("TORRENT_TITLE", item.getName());
if (resultsSource != null) {
i.putExtra("PRIVATE_SOURCE", resultsSource);
}
startActivity(i);
}
}

265
app/src/main/java/org/transdroid/core/gui/settings/MainSettingsActivity.java

@ -16,8 +16,20 @@ @@ -16,8 +16,20 @@
*/
package org.transdroid.core.gui.settings;
import java.util.ArrayList;
import java.util.List;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.view.View;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
@ -29,7 +41,7 @@ import org.transdroid.core.app.settings.ApplicationSettings; @@ -29,7 +41,7 @@ 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.TorrentsActivity_;
import org.transdroid.core.gui.*;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.settings.OverflowPreference.OnOverflowClicked;
import org.transdroid.core.gui.settings.RssfeedPreference.OnRssfeedClickedListener;
@ -39,20 +51,8 @@ import org.transdroid.core.seedbox.SeedboxPreference; @@ -39,20 +51,8 @@ import org.transdroid.core.seedbox.SeedboxPreference;
import org.transdroid.core.seedbox.SeedboxPreference.OnSeedboxClickedListener;
import org.transdroid.core.seedbox.SeedboxProvider;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* The main activity that provides access to all application settings. It shows the configured serves, web search sites
@ -63,7 +63,13 @@ import android.view.View; @@ -63,7 +63,13 @@ import android.view.View;
public class MainSettingsActivity extends PreferenceActivity {
protected static final int DIALOG_ADDSEEDBOX = 0;
private OnOverflowClicked onOverflowClicked = new OnOverflowClicked() {
@SuppressWarnings("deprecation")
@Override
public void onOverflowClicked(View overflowButton) {
showDialog(DIALOG_ADDSEEDBOX);
}
};
@Bean
protected NavigationHelper navigationHelper;
@Bean
@ -71,6 +77,83 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -71,6 +77,83 @@ public class MainSettingsActivity extends PreferenceActivity {
@Bean
protected SearchHelper searchHelper;
protected SharedPreferences prefs;
private OnPreferenceClickListener onAddServer = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
ServerSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnPreferenceClickListener onAddWebsearch = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
WebsearchSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnPreferenceClickListener onAddRssfeed = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
RssfeedSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnPreferenceClickListener onBackgroundSettings = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
NotificationSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnPreferenceClickListener onSystemSettings = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SystemSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnPreferenceClickListener onHelpSettings = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
HelpSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnServerClickedListener onServerClicked = new OnServerClickedListener() {
@Override
public void onServerClicked(ServerSetting serverSetting) {
ServerSettingsActivity_.intent(MainSettingsActivity.this).key(serverSetting.getOrder()).start();
}
};
private OnSeedboxClickedListener onSeedboxClicked = new OnSeedboxClickedListener() {
@Override
public void onSeedboxClicked(ServerSetting serverSetting, SeedboxProvider provider, int seedboxOffset) {
// NOTE: The seedboxOffset is the seedbox type-unique order that we need to supply uin the Extras bundle to
// edit this specific seedbox
startActivity(provider.getSettings().getSettingsActivityIntent(MainSettingsActivity.this)
.putExtra("key", seedboxOffset));
}
};
private OnWebsearchClickedListener onWebsearchClicked = new OnWebsearchClickedListener() {
@Override
public void onWebsearchClicked(WebsearchSetting websearchSetting) {
WebsearchSettingsActivity_.intent(MainSettingsActivity.this).key(websearchSetting.getOrder()).start();
}
};
private OnRssfeedClickedListener onRssfeedClicked = new OnRssfeedClickedListener() {
@Override
public void onRssfeedClicked(RssfeedSetting rssfeedSetting) {
RssfeedSettingsActivity_.intent(MainSettingsActivity.this).key(rssfeedSetting.getOrder()).start();
}
};
private OnClickListener onAddSeedbox = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Start the configuration activity for this specific chosen seedbox
startActivity(
SeedboxProvider.values()[which].getSettings().getSettingsActivityIntent(MainSettingsActivity.this));
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -86,8 +169,9 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -86,8 +169,9 @@ public class MainSettingsActivity extends PreferenceActivity {
getActionBar().setDisplayHomeAsUpEnabled(true);
prefs = getPreferenceManager().getSharedPreferences();
if (getPreferenceScreen() != null)
if (getPreferenceScreen() != null) {
getPreferenceScreen().removeAll();
}
boolean enableSearchUi = navigationHelper.enableSearchUi();
boolean enableRssUi = navigationHelper.enableRssUi();
@ -96,14 +180,17 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -96,14 +180,17 @@ public class MainSettingsActivity extends PreferenceActivity {
addPreferencesFromResource(R.xml.pref_main);
OverflowPreference addServerPrefernce = (OverflowPreference) findPreference("header_addserver");
addServerPrefernce.setOnPreferenceClickListener(onAddServer);
if (navigationHelper.enableSeedboxes())
if (navigationHelper.enableSeedboxes()) {
addServerPrefernce.setOnOverflowClickedListener(onOverflowClicked);
else
} else {
addServerPrefernce.hideOverflowButton();
if (enableSearchUi)
}
if (enableSearchUi) {
findPreference("header_addwebsearch").setOnPreferenceClickListener(onAddWebsearch);
if (enableRssUi)
}
if (enableRssUi) {
findPreference("header_addrssfeed").setOnPreferenceClickListener(onAddRssfeed);
}
findPreference("header_background").setOnPreferenceClickListener(onBackgroundSettings);
findPreference("header_system").setOnPreferenceClickListener(onSystemSettings);
findPreference("header_help").setOnPreferenceClickListener(onHelpSettings);
@ -115,13 +202,12 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -115,13 +202,12 @@ public class MainSettingsActivity extends PreferenceActivity {
serverCodes.add(Integer.toString(ApplicationSettings.DEFAULTSERVER_ASKONADD));
serverNames.add(getString(R.string.pref_defaultserver_lastused));
serverNames.add(getString(R.string.pref_defaultserver_askonadd));
// Add existing servers
List<ServerSetting> servers = applicationSettings.getNormalServerSettings();
for (ServerSetting serverSetting : servers) {
getPreferenceScreen().addPreference(
new ServerPreference(this).setServerSetting(serverSetting).setOnServerClickedListener(
onServerClicked));
getPreferenceScreen().addPreference(new ServerPreference(this).setServerSetting(serverSetting)
.setOnServerClickedListener(onServerClicked));
if (serverSetting.getUniqueIdentifier() != null) {
serverCodes.add(Integer.toString(serverSetting.getOrder()));
serverNames.add(serverSetting.getName());
@ -156,9 +242,8 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -156,9 +242,8 @@ public class MainSettingsActivity extends PreferenceActivity {
} else {
List<RssfeedSetting> rssfeeds = applicationSettings.getRssfeedSettings();
for (RssfeedSetting rssfeedSetting : rssfeeds) {
getPreferenceScreen().addPreference(
new RssfeedPreference(this).setRssfeedSetting(rssfeedSetting).setOnRssfeedClickedListener(
onRssfeedClicked));
getPreferenceScreen().addPreference(new RssfeedPreference(this).setRssfeedSetting(rssfeedSetting)
.setOnRssfeedClickedListener(onRssfeedClicked));
}
}
@ -171,17 +256,17 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -171,17 +256,17 @@ public class MainSettingsActivity extends PreferenceActivity {
// Add existing websearch sites
List<WebsearchSetting> websearches = applicationSettings.getWebsearchSettings();
for (WebsearchSetting websearchSetting : websearches) {
getPreferenceScreen().addPreference(
new WebsearchPreference(this).setWebsearchSetting(websearchSetting).setOnWebsearchClickedListener(
onWebsearchClicked));
getPreferenceScreen().addPreference(new WebsearchPreference(this).setWebsearchSetting(websearchSetting)
.setOnWebsearchClickedListener(onWebsearchClicked));
}
// Construct list of all available search sites, in-app and web
ListPreference setSite = (ListPreference) findPreference("header_setsearchsite");
// Retrieve the available in-app search sites (using the Torrent Search package)
List<SearchSite> searchsites = searchHelper.getAvailableSites();
if (searchsites == null)
if (searchsites == null) {
searchsites = new ArrayList<SearchSite>();
}
List<String> siteNames = new ArrayList<String>(websearches.size() + searchsites.size());
List<String> siteValues = new ArrayList<String>(websearches.size() + searchsites.size());
for (SearchSite searchSite : searchsites) {
@ -210,114 +295,18 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -210,114 +295,18 @@ public class MainSettingsActivity extends PreferenceActivity {
super.onBuildHeaders(target);
}
private OnPreferenceClickListener onAddServer = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
ServerSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnOverflowClicked onOverflowClicked = new OnOverflowClicked() {
@SuppressWarnings("deprecation")
@Override
public void onOverflowClicked(View overflowButton) {
showDialog(DIALOG_ADDSEEDBOX);
}
};
private OnPreferenceClickListener onAddWebsearch = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
WebsearchSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnPreferenceClickListener onAddRssfeed = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
RssfeedSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnPreferenceClickListener onBackgroundSettings = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
NotificationSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnPreferenceClickListener onSystemSettings = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SystemSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnPreferenceClickListener onHelpSettings = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
HelpSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
};
private OnServerClickedListener onServerClicked = new OnServerClickedListener() {
@Override
public void onServerClicked(ServerSetting serverSetting) {
ServerSettingsActivity_.intent(MainSettingsActivity.this).key(serverSetting.getOrder()).start();
}
};
private OnSeedboxClickedListener onSeedboxClicked = new OnSeedboxClickedListener() {
@Override
public void onSeedboxClicked(ServerSetting serverSetting, SeedboxProvider provider, int seedboxOffset) {
// NOTE: The seedboxOffset is the seedbox type-unique order that we need to supply uin the Extras bundle to
// edit this specific seedbox
startActivity(provider.getSettings().getSettingsActivityIntent(MainSettingsActivity.this)
.putExtra("key", seedboxOffset));
}
};
private OnWebsearchClickedListener onWebsearchClicked = new OnWebsearchClickedListener() {
@Override
public void onWebsearchClicked(WebsearchSetting websearchSetting) {
WebsearchSettingsActivity_.intent(MainSettingsActivity.this).key(websearchSetting.getOrder()).start();
}
};
private OnRssfeedClickedListener onRssfeedClicked = new OnRssfeedClickedListener() {
@Override
public void onRssfeedClicked(RssfeedSetting rssfeedSetting) {
RssfeedSettingsActivity_.intent(MainSettingsActivity.this).key(rssfeedSetting.getOrder()).start();
}
};
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_ADDSEEDBOX:
// Open dialog to pick one of the supported seedbox providers
String[] seedboxes = new String[SeedboxProvider.values().length];
for (int i = 0; i < seedboxes.length; i++) {
seedboxes[i] = getString(R.string.pref_seedbox_addseedbox, SeedboxProvider.values()[i].getSettings()
.getName());
}
return new AlertDialog.Builder(this).setItems(seedboxes, onAddSeedbox).create();
case DIALOG_ADDSEEDBOX:
// Open dialog to pick one of the supported seedbox providers
String[] seedboxes = new String[SeedboxProvider.values().length];
for (int i = 0; i < seedboxes.length; i++) {
seedboxes[i] = getString(R.string.pref_seedbox_addseedbox,
SeedboxProvider.values()[i].getSettings().getName());
}
return new AlertDialog.Builder(this).setItems(seedboxes, onAddSeedbox).create();
}
return null;
}
private OnClickListener onAddSeedbox = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Start the configuration activity for this specific chosen seedbox
startActivity(SeedboxProvider.values()[which].getSettings().getSettingsActivityIntent(
MainSettingsActivity.this));
}
};
}

4
app/src/main/java/org/transdroid/core/gui/settings/ServerSettingsActivity.java

@ -119,8 +119,8 @@ public class ServerSettingsActivity extends KeyBoundPreferencesActivity { @@ -119,8 +119,8 @@ public class ServerSettingsActivity extends KeyBoundPreferencesActivity {
extraPass.setEnabled(Daemon.supportsExtraPassword(daemonType));
extraPass.setTitle(getString(daemonType == Daemon.Deluge ? R.string.pref_extrapassword : R.string.pref_secret));
extraPass.setDialogTitle(extraPass.getTitle());
folder.setEnabled(daemonType == null ? false : Daemon.supportsCustomFolder(daemonType));
downloadDir.setEnabled(daemonType == null ? false : Daemon.needsManualPathSpecified(daemonType));
folder.setEnabled(daemonType != null && Daemon.supportsCustomFolder(daemonType));
downloadDir.setEnabled(daemonType != null && Daemon.needsManualPathSpecified(daemonType));
// sslTrustKey.setEnabled(sslValue && !sslTAValue);
// Adjust title texts accordingly

8
app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettingsActivity.java

@ -18,6 +18,7 @@ package org.transdroid.core.seedbox; @@ -18,6 +18,7 @@ package org.transdroid.core.seedbox;
import java.io.InputStream;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
@ -51,6 +52,9 @@ import de.keyboardsurfer.android.widget.crouton.Crouton; @@ -51,6 +52,9 @@ import de.keyboardsurfer.android.widget.crouton.Crouton;
@OptionsMenu(resName = "activity_deleteableprefs")
public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity {
@Bean
protected Log log;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -99,7 +103,7 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity { @@ -99,7 +103,7 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity {
} catch (Exception e) {
Log.d(XirvikSharedSettingsActivity.this,
log.d(XirvikSharedSettingsActivity.this,
"Could not retrieve the Xirvik shared seedbox RPC mount point setting: " + e.toString());
return null;
@ -126,7 +130,7 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity { @@ -126,7 +130,7 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity {
edit.putString("seedbox_xirvikshared_rpc_" + key, result);
pref.setSummary(result);
}
edit.commit();
edit.apply();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)

15
app/src/main/java/org/transdroid/core/service/AppUpdateService.java

@ -55,6 +55,8 @@ public class AppUpdateService extends IntentService { @@ -55,6 +55,8 @@ public class AppUpdateService extends IntentService {
private static final String DOWNLOAD_URL_APP = "http://www.transdroid.org/latest";
private static final String DOWNLOAD_URL_SEARCH = "http://www.transdroid.org/latest-search";
@Bean
protected Log log;
@Bean
protected NavigationHelper navigationHelper;
@Bean
@ -78,7 +80,7 @@ public class AppUpdateService extends IntentService { @@ -78,7 +80,7 @@ public class AppUpdateService extends IntentService {
return;
if (!connectivityHelper.shouldPerformBackgroundActions() || !systemSettings.checkForUpdates()) {
Log.d(this, "Skip the app update service, as background data is disabled, the service is explicitly " +
log.d(this, "Skip the app update service, as background data is disabled, the service is explicitly " +
"disabled or we are not connected.");
return;
}
@ -87,7 +89,7 @@ public class AppUpdateService extends IntentService { @@ -87,7 +89,7 @@ public class AppUpdateService extends IntentService {
Calendar lastDay = Calendar.getInstance();
lastDay.add(Calendar.DAY_OF_MONTH, -1);
if (lastChecked != null && lastChecked.after(lastDay.getTime())) {
Log.d(this, "Skip the update service, as we already checked the last 24 hours (or to be exact at "
log.d(this, "Skip the update service, as we already checked the last 24 hours (or to be exact at "
+ lastChecked.toString() + ").");
return;
}
@ -106,7 +108,7 @@ public class AppUpdateService extends IntentService { @@ -106,7 +108,7 @@ public class AppUpdateService extends IntentService {
// New version of the app?
try {
PackageInfo appPackage = getPackageManager().getPackageInfo(getPackageName(), 0);
Log.d(this, "Local Transdroid is at " + appPackage.versionCode + " and the reported latest version is "
log.d(this, "Local Transdroid is at " + appPackage.versionCode + " and the reported latest version is "
+ appVersion);
if (appPackage.versionCode < appVersion) {
// New version available! Notify the user.
@ -122,7 +124,7 @@ public class AppUpdateService extends IntentService { @@ -122,7 +124,7 @@ public class AppUpdateService extends IntentService {
// New version of the search module?
try {
PackageInfo searchPackage = getPackageManager().getPackageInfo("org.transdroid.search", 0);
Log.d(this, "Local Transdroid Seach is at " + searchPackage.versionCode
log.d(this, "Local Transdroid Seach is at " + searchPackage.versionCode
+ " and the reported latest version is " + searchVersion);
if (searchPackage.versionCode < searchVersion) {
// New version available! Notify the user.
@ -142,7 +144,7 @@ public class AppUpdateService extends IntentService { @@ -142,7 +144,7 @@ public class AppUpdateService extends IntentService {
} catch (Exception e) {
// Cannot check right now for some reason; log and ignore
Log.d(this, "Cannot retrieve latest app or search module version code from the site: " + e.toString());
log.d(this, "Cannot retrieve latest app or search module version code from the site: " + e.toString());
}
}
@ -156,8 +158,7 @@ public class AppUpdateService extends IntentService { @@ -156,8 +158,7 @@ public class AppUpdateService extends IntentService {
* @throws ClientProtocolException Thrown when the provided URL is invalid
* @throws IOException Thrown when the last version information could not be retrieved
*/
private String[] retrieveLatestVersion(AbstractHttpClient httpclient, String url) throws ClientProtocolException,
IOException {
private String[] retrieveLatestVersion(AbstractHttpClient httpclient, String url) throws IOException {
HttpResponse request = httpclient.execute(new HttpGet(url));
InputStream stream = request.getEntity().getContent();
String appVersion[] = HttpHelper.convertStreamToString(stream).split("\\|");

52
app/src/main/java/org/transdroid/core/service/BootReceiver.java

@ -16,13 +16,6 @@ @@ -16,13 +16,6 @@
*/
package org.transdroid.core.service;
import org.transdroid.core.app.settings.NotificationSettings;
import org.transdroid.core.app.settings.NotificationSettings_;
import org.transdroid.core.app.settings.SystemSettings;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.navigation.NavigationHelper_;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@ -30,10 +23,17 @@ import android.content.Context; @@ -30,10 +23,17 @@ import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EReceiver;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.gui.log.*;
import org.transdroid.core.gui.navigation.*;
/**
* Receives the intent that the device has been started in order to set up proper alarms for all background services.
* @author Eric Kok
*/
@EReceiver
public class BootReceiver extends BroadcastReceiver {
public static final int ALARM_SERVERCHECKER = 0;
@ -42,26 +42,24 @@ public class BootReceiver extends BroadcastReceiver { @@ -42,26 +42,24 @@ public class BootReceiver extends BroadcastReceiver {
public static PendingIntent piServerChecker = null, piRssChecker = null, piAppUpdates = null;
@Override
public void onReceive(Context context, Intent intent) {
startBackgroundServices(context, false);
startAppUpdatesService(context);
}
@Bean
protected Log log;
public static void startBackgroundServices(Context context, boolean forceReload) {
NotificationSettings notificationSettings = NotificationSettings_.getInstance_(context);
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// Start the alarms if one of the notifications are enabled and we do not yet have the alarms running
// (or should reload it forcefully)
if ((notificationSettings.isEnabledForRss() || notificationSettings.isEnabledForTorrents())
&& (forceReload || (piServerChecker == null && piRssChecker == null))) {
if ((notificationSettings.isEnabledForRss() || notificationSettings.isEnabledForTorrents()) &&
(forceReload || (piServerChecker == null && piRssChecker == null))) {
Log.d(context, "Boot signal received, starting server and rss checker background services");
Log_.getInstance_(context)
.d("BootReceiver", "Boot signal received, starting server and rss checker background services");
// Schedule repeating alarms, with the first being (somewhat) in 1 second from now
piServerChecker = PendingIntent.getBroadcast(context, ALARM_SERVERCHECKER, new Intent(context,
AlarmReceiver_.class).putExtra("service", ALARM_SERVERCHECKER), 0);
piRssChecker = PendingIntent.getBroadcast(context, ALARM_RSSCHECKER, new Intent(context,
AlarmReceiver_.class).putExtra("service", ALARM_RSSCHECKER), 0);
piServerChecker = PendingIntent.getBroadcast(context, ALARM_SERVERCHECKER,
new Intent(context, AlarmReceiver_.class).putExtra("service", ALARM_SERVERCHECKER), 0);
piRssChecker = PendingIntent.getBroadcast(context, ALARM_RSSCHECKER,
new Intent(context, AlarmReceiver_.class).putExtra("service", ALARM_RSSCHECKER), 0);
alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000,
notificationSettings.getInvervalInMilliseconds(), piServerChecker);
alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000,
@ -73,13 +71,13 @@ public class BootReceiver extends BroadcastReceiver { @@ -73,13 +71,13 @@ public class BootReceiver extends BroadcastReceiver {
public static void startAppUpdatesService(Context context) {
SystemSettings systemSettings = SystemSettings_.getInstance_(context);
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (NavigationHelper_.getInstance_(context).enableUpdateChecker() && systemSettings.checkForUpdates()
&& piAppUpdates == null) {
if (NavigationHelper_.getInstance_(context).enableUpdateChecker() && systemSettings.checkForUpdates() &&
piAppUpdates == null) {
Log.d(context, "Boot signal received, starting app update checker service");
Log_.getInstance_(context).d("BootReceiver", "Boot signal received, starting app update checker service");
// Schedule a daily, with the first being (somewhat) in 1 second from now
piAppUpdates = PendingIntent.getBroadcast(context, ALARM_APPUPDATES, new Intent(context,
AlarmReceiver_.class).putExtra("service", ALARM_APPUPDATES), 0);
piAppUpdates = PendingIntent.getBroadcast(context, ALARM_APPUPDATES,
new Intent(context, AlarmReceiver_.class).putExtra("service", ALARM_APPUPDATES), 0);
alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000,
AlarmManager.INTERVAL_DAY, piAppUpdates);
@ -106,4 +104,10 @@ public class BootReceiver extends BroadcastReceiver { @@ -106,4 +104,10 @@ public class BootReceiver extends BroadcastReceiver {
}
}
@Override
public void onReceive(Context context, Intent intent) {
startBackgroundServices(context, false);
startAppUpdatesService(context);
}
}

56
app/src/main/java/org/transdroid/core/service/ControlService.java

@ -1,13 +1,16 @@ @@ -1,13 +1,16 @@
package org.transdroid.core.service;
import android.app.IntentService;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EService;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.widget.ListWidgetConfig;
import org.transdroid.core.widget.ListWidgetProvider;
import org.transdroid.core.widget.ListWidgetProvider_;
import org.transdroid.core.widget.*;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.task.DaemonTask;
import org.transdroid.daemon.task.DaemonTaskResult;
@ -18,10 +21,6 @@ import org.transdroid.daemon.task.SetTransferRatesTask; @@ -18,10 +21,6 @@ import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.task.StartAllTask;
import org.transdroid.daemon.task.StopAllTask;
import android.app.IntentService;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
@EService
public class ControlService extends IntentService {
@ -35,6 +34,8 @@ public class ControlService extends IntentService { @@ -35,6 +34,8 @@ public class ControlService extends IntentService {
public static final String EXTRA_UPLOAD_RATE = "UPLOAD_RATE";
public static final String EXTRA_DOWNLOAD_RATE = "DOWNLOAD_RATE";
@Bean
protected Log log;
@Bean
protected ConnectivityHelper connectivityHelper;
@Bean
@ -47,11 +48,12 @@ public class ControlService extends IntentService { @@ -47,11 +48,12 @@ public class ControlService extends IntentService {
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null)
if (intent == null) {
return;
}
// We should have been supplied either am EXTRA_DAEMON or an AppWidgetManager.EXTRA_APPWIDGET_ID
ServerSetting server = null;
ServerSetting server;
int appWidgetId = -1;
if (intent.hasExtra(EXTRA_DAEMON)) {
@ -59,8 +61,8 @@ public class ControlService extends IntentService { @@ -59,8 +61,8 @@ public class ControlService extends IntentService {
int serverId = intent.getIntExtra(EXTRA_DAEMON, -1);
if (serverId < 0 || serverId > applicationSettings.getMaxOfAllServers()) {
// This server does not exist (any more) or no valid EXTRA_DAEMON value was supplied
Log.e(this, "The control service can be started with a DAEMON extra zero-based server id, but the"
+ "supplied id was invalid or no longer points to an existing server.");
log.e(this, "The control service can be started with a DAEMON extra zero-based server id, but the" +
"supplied id was invalid or no longer points to an existing server.");
return;
}
server = applicationSettings.getServerSetting(serverId);
@ -71,14 +73,14 @@ public class ControlService extends IntentService { @@ -71,14 +73,14 @@ public class ControlService extends IntentService {
appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
ListWidgetConfig config = applicationSettings.getWidgetConfig(appWidgetId);
if (config == null) {
Log.e(this,
"The control service can be started by a widget using the AppWidgetManager.EXTRA_APPWIDGET_ID, "
+ "but the id that was supplied does not point to an existing home screen widget.");
log.e(this,
"The control service can be started by a widget using the AppWidgetManager.EXTRA_APPWIDGET_ID, " +
"but the id that was supplied does not point to an existing home screen widget.");
return;
}
int serverId = config.getServerId();
if (serverId < 0 || serverId > applicationSettings.getMaxOfAllServers()) {
Log.e(this, "The home screen widget points to a server that no longer exists.");
log.e(this, "The home screen widget points to a server that no longer exists.");
return;
}
server = applicationSettings.getServerSetting(serverId);
@ -92,7 +94,7 @@ public class ControlService extends IntentService { @@ -92,7 +94,7 @@ public class ControlService extends IntentService {
// Still no server? Then we don't have one specified yet
if (server == null) {
Log.e(this, "The control service was called, but there are nog servers configured at all.");
log.e(this, "The control service was called, but there are nog servers configured at all.");
return;
}
@ -111,44 +113,44 @@ public class ControlService extends IntentService { @@ -111,44 +113,44 @@ public class ControlService extends IntentService {
// NOTE: If the upload or download rate was not specified, it will be reset on the server instead
int uploadRate = intent.getIntExtra(EXTRA_UPLOAD_RATE, -1);
int downloadRate = intent.getIntExtra(EXTRA_DOWNLOAD_RATE, -1);
task = SetTransferRatesTask.create(adapter, uploadRate == -1 ? null : uploadRate, downloadRate == -1 ? null
: downloadRate);
task = SetTransferRatesTask
.create(adapter, uploadRate == -1 ? null : uploadRate, downloadRate == -1 ? null : downloadRate);
}
// Execute the task, if we have one now
if (task == null) {
Log.e(this, "The control service was started, but no (valid) action was specified, such as "
+ "org.transdroid.control.START_ALL or org.transdroid.control.SET_TRANSFER_RATES");
log.e(this, "The control service was started, but no (valid) action was specified, such as " +
"org.transdroid.control.START_ALL or org.transdroid.control.SET_TRANSFER_RATES");
return;
}
DaemonTaskResult result = task.execute();
DaemonTaskResult result = task.execute(log);
if (result instanceof DaemonTaskSuccessResult) {
Log.i(this,
log.i(this,
task.getMethod().name() + " was successfully executed on " + server.getHumanReadableIdentifier());
} else {
Log.i(this,
task.getMethod().name() + " was NOT succcessfully executed on "
+ server.getHumanReadableIdentifier() + " (and we are NOT trying again)");
log.i(this, task.getMethod().name() + " was NOT succcessfully executed on " +
server.getHumanReadableIdentifier() + " (and we are NOT trying again)");
// No need to continue now
return;
}
// The task was successful, so maybe we need to update the original calling widget now too
if (appWidgetId >= 0) {
// Just wait for (max) two seconds, to give the server time to finish its last action
try {
Thread.sleep(2000);
} catch (Exception e) {
// Sleep
}
// Ask the app widget provider to update this specific widget
Intent update = new Intent(this, ListWidgetProvider_.class);
update.setAction("android.appwidget.action.APPWIDGET_UPDATE");
update.putExtra(ListWidgetProvider.EXTRA_REFRESH, appWidgetId);
update.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
sendBroadcast(update);
}
}

50
app/src/main/java/org/transdroid/core/service/RssCheckerService.java

@ -16,8 +16,12 @@ @@ -16,8 +16,12 @@
*/
package org.transdroid.core.service;
import java.util.LinkedHashSet;
import java.util.Set;
import android.app.IntentService;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EService;
@ -27,17 +31,13 @@ import org.transdroid.core.app.settings.ApplicationSettings; @@ -27,17 +31,13 @@ import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.NotificationSettings;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.rss.RssfeedsActivity_;
import org.transdroid.core.gui.rss.*;
import org.transdroid.core.rssparser.Item;
import org.transdroid.core.rssparser.RssParser;
import org.transdroid.daemon.util.Collections2;
import android.app.IntentService;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* A background service that checks all user-configured RSS feeds for new items.
@ -46,6 +46,8 @@ import android.content.Intent; @@ -46,6 +46,8 @@ import android.content.Intent;
@EService
public class RssCheckerService extends IntentService {
@Bean
protected Log log;
@Bean
protected ConnectivityHelper connectivityHelper;
@Bean
@ -64,7 +66,7 @@ public class RssCheckerService extends IntentService { @@ -64,7 +66,7 @@ public class RssCheckerService extends IntentService {
protected void onHandleIntent(Intent intent) {
if (!connectivityHelper.shouldPerformBackgroundActions() || !notificationSettings.isEnabledForRss()) {
Log.d(this,
log.d(this,
"Skip the RSS checker service, as background data is disabled, the service is disabled or we are not connected.");
return;
}
@ -76,15 +78,16 @@ public class RssCheckerService extends IntentService { @@ -76,15 +78,16 @@ public class RssCheckerService extends IntentService {
try {
if (!feed.shouldAlarmOnNewItems()) {
Log.d(this, "Skip checker for " + feed.getName() + " as alarms are disabled");
log.d(this, "Skip checker for " + feed.getName() + " as alarms are disabled");
continue;
}
Log.d(this, "Try to parse " + feed.getName() + " (" + feed.getUrl() + ")");
log.d(this, "Try to parse " + feed.getName() + " (" + feed.getUrl() + ")");
RssParser parser = new RssParser(feed.getUrl());
parser.parse();
if (parser.getChannel() == null)
if (parser.getChannel() == null) {
continue;
}
// Find the last item that is newer than the last viewed date
for (Item item : parser.getChannel().getItems()) {
@ -92,13 +95,14 @@ public class RssCheckerService extends IntentService { @@ -92,13 +95,14 @@ public class RssCheckerService extends IntentService {
break;
} else {
unread++;
if (!hasUnread.contains(feed.getName()))
if (!hasUnread.contains(feed.getName())) {
hasUnread.add(feed.getName());
}
}
}
Log.d(this, feed.getName() + " has " + (hasUnread.contains(feed.getName()) ? "" : "no ")
+ "unread items");
log.d(this,
feed.getName() + " has " + (hasUnread.contains(feed.getName()) ? "" : "no ") + "unread items");
} catch (Exception e) {
// Ignore RSS feeds that could not be retrieved or parsed
@ -111,17 +115,17 @@ public class RssCheckerService extends IntentService { @@ -111,17 +115,17 @@ public class RssCheckerService extends IntentService {
}
// Provide a notification, since there are new RSS items
PendingIntent pi = PendingIntent.getActivity(this, 80000, new Intent(this, RssfeedsActivity_.class),
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pi = PendingIntent
.getActivity(this, 80000, new Intent(this, RssfeedsActivity_.class), PendingIntent.FLAG_UPDATE_CURRENT);
String title = getResources().getQuantityString(R.plurals.rss_service_new, unread, Integer.toString(unread));
String forString = Collections2.joinString(hasUnread, ", ");
Builder builder = new Notification.Builder(this).setSmallIcon(R.drawable.ic_stat_notification)
.setTicker(title).setContentTitle(title)
.setContentText(getString(R.string.rss_service_newfor, forString)).setNumber(unread)
.setLights(notificationSettings.getDesiredLedColour(), 600, 1000)
Builder builder = new Notification.Builder(this).setSmallIcon(R.drawable.ic_stat_notification).setTicker(title)
.setContentTitle(title).setContentText(getString(R.string.rss_service_newfor, forString))
.setNumber(unread).setLights(notificationSettings.getDesiredLedColour(), 600, 1000)
.setSound(notificationSettings.getSound()).setAutoCancel(true).setContentIntent(pi);
if (notificationSettings.shouldVibrate())
if (notificationSettings.shouldVibrate()) {
builder.setVibrate(notificationSettings.getDefaultVibratePattern());
}
notificationManager.notify(80001, builder.getNotification());
}

10
app/src/main/java/org/transdroid/core/service/ServerCheckerService.java

@ -54,6 +54,8 @@ import android.os.Build; @@ -54,6 +54,8 @@ import android.os.Build;
@EService
public class ServerCheckerService extends IntentService {
@Bean
protected Log log;
@Bean
protected ConnectivityHelper connectivityHelper;
@Bean
@ -73,7 +75,7 @@ public class ServerCheckerService extends IntentService { @@ -73,7 +75,7 @@ public class ServerCheckerService extends IntentService {
protected void onHandleIntent(Intent intent) {
if (!connectivityHelper.shouldPerformBackgroundActions() || !notificationSettings.isEnabledForTorrents()) {
Log.d(this,
log.d(this,
"Skip the server checker service, as background data is disabled, the service is disabled or we are not connected.");
return;
}
@ -92,13 +94,13 @@ public class ServerCheckerService extends IntentService { @@ -92,13 +94,13 @@ public class ServerCheckerService extends IntentService {
// Synchronously retrieve torrents listing
IDaemonAdapter adapter = server.createServerAdapter(connectivityHelper.getConnectedNetworkName(), this);
DaemonTaskResult result = RetrieveTask.create(adapter).execute();
DaemonTaskResult result = RetrieveTask.create(adapter).execute(log);
if (!(result instanceof RetrieveTaskSuccessResult)) {
// Cannot retrieve torrents at this time
continue;
}
List<Torrent> retrieved = ((RetrieveTaskSuccessResult) result).getTorrents();
Log.d(this, server.getName() + ": Retrieved torrent listing");
log.d(this, server.getName() + ": Retrieved torrent listing");
// Check for differences between the last and the current stats
JSONArray currentStats = new JSONArray();
@ -133,7 +135,7 @@ public class ServerCheckerService extends IntentService { @@ -133,7 +135,7 @@ public class ServerCheckerService extends IntentService {
applicationSettings.setServerLastStats(server, currentStats);
// Notify on new and now-done torrents for this server
Log.d(this, server.getName() + ": " + newTorrents.size() + " new torrents, " + doneTorrents.size()
log.d(this, server.getName() + ": " + newTorrents.size() + " new torrents, " + doneTorrents.size()
+ " newly finished torrents.");
Intent i = new Intent(this, TorrentsActivity_.class);
i.putExtra("org.transdroid.START_SERVER", server.getOrder());

178
app/src/main/java/org/transdroid/core/widget/ListWidgetConfigActivity.java

@ -16,9 +16,21 @@ @@ -16,9 +16,21 @@
*/
package org.transdroid.core.widget;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Background;
@ -33,6 +45,7 @@ import org.transdroid.core.app.settings.ServerSetting; @@ -33,6 +45,7 @@ import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.app.settings.SystemSettings;
import org.transdroid.core.gui.lists.SimpleListItemSpinnerAdapter;
import org.transdroid.core.gui.lists.SortByListItem;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.navigation.StatusType;
import org.transdroid.core.gui.navigation.StatusType.StatusTypeFilter;
import org.transdroid.core.service.ConnectivityHelper;
@ -47,21 +60,9 @@ import org.transdroid.daemon.task.RetrieveTask; @@ -47,21 +60,9 @@ import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.util.FileSizeConverter;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@EActivity(resName = "activity_widgetconfig")
@ -80,23 +81,73 @@ public class ListWidgetConfigActivity extends Activity { @@ -80,23 +81,73 @@ public class ListWidgetConfigActivity extends Activity {
protected ListView torrentsList;
@ViewById
protected View navigationView, serverstatusView;
private List<Torrent> previewTorrents = null;
// Settings and helpers
@Bean
protected Log log;
@Bean
protected ConnectivityHelper connectivityHelper;
@Bean
protected ApplicationSettings applicationSettings;
@Bean
protected SystemSettings systemSettings;
protected OnCheckedChangeListener reverseorderCheckedChanged = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
filterTorrents();
}
};
protected OnCheckedChangeListener showstatusCheckChanged = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
navigationView.setVisibility(showstatusCheckBox.isChecked() ? View.GONE : View.VISIBLE);
serverstatusView.setVisibility(showstatusCheckBox.isChecked() ? View.VISIBLE : View.GONE);
}
};
private List<Torrent> previewTorrents = null;
private int appWidgetId;
private OnClickListener doneClicked = new OnClickListener() {
@Override
public void onClick(View v) {
// All spinner have to be initialised already
if (serverSpinner.getSelectedItem() == null) {
return;
}
if (filterSpinner.getSelectedItem() == null) {
return;
}
if (sortSpinner.getSelectedItem() == null) {
return;
}
// Store these user preferences
int server = ((ServerSetting) serverSpinner.getSelectedItem()).getOrder();
StatusType statusType = ((StatusTypeFilter) filterSpinner.getSelectedItem()).getStatusType();
TorrentsSortBy sortBy = ((SortByListItem) sortSpinner.getSelectedItem()).getSortBy();
boolean reverseSort = reverseorderCheckBox.isChecked();
boolean showstatus = showstatusCheckBox.isChecked();
boolean useDarkTheme = darkthemeCheckBox.isChecked();
ListWidgetConfig config =
new ListWidgetConfig(server, statusType, sortBy, reverseSort, showstatus, useDarkTheme);
applicationSettings.setWidgetConfig(appWidgetId, config);
// Return the widget configuration result
AppWidgetManager manager = AppWidgetManager.getInstance(ListWidgetConfigActivity.this);
manager.updateAppWidget(appWidgetId,
ListWidgetProvider.buildRemoteViews(getApplicationContext(), appWidgetId, config));
manager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.torrents_list);
setResult(RESULT_OK, new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId));
finish();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent() == null || getIntent().getExtras() == null
|| !getIntent().hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
if (getIntent() == null || getIntent().getExtras() == null ||
!getIntent().hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
// Invalid configuration; return canceled result
setResult(RESULT_CANCELED,
new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID));
@ -104,8 +155,8 @@ public class ListWidgetConfigActivity extends Activity { @@ -104,8 +155,8 @@ public class ListWidgetConfigActivity extends Activity {
}
// Get the appwidget ID we are configuring
appWidgetId = getIntent().getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
appWidgetId =
getIntent().getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
// Set preliminary canceled result and continue with the initialisation
setResult(RESULT_CANCELED, new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId));
@ -119,10 +170,10 @@ public class ListWidgetConfigActivity extends Activity { @@ -119,10 +170,10 @@ public class ListWidgetConfigActivity extends Activity {
for (TorrentsSortBy order : TorrentsSortBy.values()) {
sortOrders.add(new SortByListItem(this, order));
}
serverSpinner.setAdapter(new SimpleListItemSpinnerAdapter<ServerSetting>(this, 0, applicationSettings
.getAllServerSettings()));
filterSpinner.setAdapter(new SimpleListItemSpinnerAdapter<StatusTypeFilter>(this, 0, StatusType
.getAllStatusTypes(this)));
serverSpinner.setAdapter(
new SimpleListItemSpinnerAdapter<ServerSetting>(this, 0, applicationSettings.getAllServerSettings()));
filterSpinner.setAdapter(
new SimpleListItemSpinnerAdapter<StatusTypeFilter>(this, 0, StatusType.getAllStatusTypes(this)));
sortSpinner.setAdapter(new SimpleListItemSpinnerAdapter<SortByListItem>(this, 0, sortOrders));
// TODO: Update to AndroidAnnotations 3.0 and use @CheckedChanged
reverseorderCheckBox.setOnCheckedChangeListener(reverseorderCheckedChanged);
@ -156,31 +207,17 @@ public class ListWidgetConfigActivity extends Activity { @@ -156,31 +207,17 @@ public class ListWidgetConfigActivity extends Activity {
filterTorrents();
}
protected OnCheckedChangeListener reverseorderCheckedChanged = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
filterTorrents();
}
};
protected OnCheckedChangeListener showstatusCheckChanged = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
navigationView.setVisibility(showstatusCheckBox.isChecked()? View.GONE: View.VISIBLE);
serverstatusView.setVisibility(showstatusCheckBox.isChecked()? View.VISIBLE: View.GONE);
}
};
@Background
protected void loadTorrents() {
if (serverSpinner.getSelectedItem() == null)
if (serverSpinner.getSelectedItem() == null) {
return;
}
// Create a connection object and retrieve the live torrents
IDaemonAdapter connection = ((ServerSetting) serverSpinner.getSelectedItem())
.createServerAdapter(connectivityHelper.getConnectedNetworkName(), this);
DaemonTaskResult result = RetrieveTask.create(connection).execute();
DaemonTaskResult result = RetrieveTask.create(connection).execute(log);
if (result instanceof RetrieveTaskSuccessResult) {
// Success; show the active torrents in the widget preview
onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(),
@ -201,22 +238,27 @@ public class ListWidgetConfigActivity extends Activity { @@ -201,22 +238,27 @@ public class ListWidgetConfigActivity extends Activity {
protected void filterTorrents() {
// All spinners have to be initialised already
if (serverSpinner.getSelectedItem() == null)
if (serverSpinner.getSelectedItem() == null) {
return;
if (filterSpinner.getSelectedItem() == null)
}
if (filterSpinner.getSelectedItem() == null) {
return;
if (sortSpinner.getSelectedItem() == null)
}
if (sortSpinner.getSelectedItem() == null) {
return;
if (previewTorrents == null)
}
if (previewTorrents == null) {
return;
}
// Get the already loaded torrents and filter and sort them
ArrayList<Torrent> filteredTorrents = new ArrayList<Torrent>(previewTorrents.size());
StatusTypeFilter statusTypeFilter = (StatusTypeFilter) filterSpinner.getSelectedItem();
boolean dormantAsInactive = systemSettings.treatDormantAsInactive();
for (Torrent torrent : previewTorrents) {
if (statusTypeFilter.matches(torrent, dormantAsInactive))
if (statusTypeFilter.matches(torrent, dormantAsInactive)) {
filteredTorrents.add(torrent);
}
}
if (filteredTorrents.size() == 0) {
showError(true);
@ -226,7 +268,7 @@ public class ListWidgetConfigActivity extends Activity { @@ -226,7 +268,7 @@ public class ListWidgetConfigActivity extends Activity {
Daemon serverType = filteredTorrents.get(0).getDaemon();
Collections
.sort(filteredTorrents, new TorrentsComparator(serverType, sortBy, reverseorderCheckBox.isChecked()));
// Update the server status count and speeds
int downcount = 0, upcount = 0, downspeed = 0, upspeed = 0;
for (Torrent torrent : previewTorrents) {
@ -257,38 +299,4 @@ public class ListWidgetConfigActivity extends Activity { @@ -257,38 +299,4 @@ public class ListWidgetConfigActivity extends Activity {
errorText.setText(emptyResults ? R.string.navigation_emptytorrents : R.string.error_httperror);
}
private OnClickListener doneClicked = new OnClickListener() {
@Override
public void onClick(View v) {
// All spinner have to be initialised already
if (serverSpinner.getSelectedItem() == null)
return;
if (filterSpinner.getSelectedItem() == null)
return;
if (sortSpinner.getSelectedItem() == null)
return;
// Store these user preferences
int server = ((ServerSetting) serverSpinner.getSelectedItem()).getOrder();
StatusType statusType = ((StatusTypeFilter) filterSpinner.getSelectedItem()).getStatusType();
TorrentsSortBy sortBy = ((SortByListItem) sortSpinner.getSelectedItem()).getSortBy();
boolean reverseSort = reverseorderCheckBox.isChecked();
boolean showstatus = showstatusCheckBox.isChecked();
boolean useDarkTheme = darkthemeCheckBox.isChecked();
ListWidgetConfig config = new ListWidgetConfig(server, statusType, sortBy, reverseSort, showstatus,
useDarkTheme);
applicationSettings.setWidgetConfig(appWidgetId, config);
// Return the widget configuration result
AppWidgetManager manager = AppWidgetManager.getInstance(ListWidgetConfigActivity.this);
manager.updateAppWidget(appWidgetId,
ListWidgetProvider.buildRemoteViews(getApplicationContext(), appWidgetId, config));
manager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.torrents_list);
setResult(RESULT_OK, new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId));
finish();
}
};
}

113
app/src/main/java/org/transdroid/core/widget/ListWidgetProvider.java

@ -16,15 +16,6 @@ @@ -16,15 +16,6 @@
*/
package org.transdroid.core.widget;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EReceiver;
import org.transdroid.R;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.gui.*;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.service.ControlService;
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
@ -33,8 +24,17 @@ import android.content.Context; @@ -33,8 +24,17 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.widget.RemoteViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EReceiver;
import org.transdroid.R;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.gui.*;
import org.transdroid.core.gui.log.*;
import org.transdroid.core.service.ControlService;
/**
* The provider of a list-style Transdroid widget, which controls the general loading and (touch) event handling. The
* list rows' remote views are loaded in the accompanying {@link ListWidgetViewsService}.
@ -52,49 +52,6 @@ public class ListWidgetProvider extends AppWidgetProvider { @@ -52,49 +52,6 @@ public class ListWidgetProvider extends AppWidgetProvider {
@Bean
protected ApplicationSettings applicationSettings;
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (intent == null)
return;
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
// Refresh a specific app widget
if (intent.hasExtra(EXTRA_REFRESH)) {
// Manually requested a refresh for the app widget of which the ID was supplied
RemoteViews views = buildRemoteViews(context, appWidgetId, applicationSettings.getWidgetConfig(appWidgetId));
if (views != null) {
AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views);
AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(appWidgetId, R.id.torrents_list);
}
return;
}
// No refresh: this is a control intent: copy the action and EXTRA_APPWIDGET_ID to start the control service
if (intent.getAction().startsWith("org.transdroid.control.")) {
Intent action = new Intent(intent.getAction());
action.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
context.startService(action);
}
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
appWidgetManager.updateAppWidget(appWidgetId,
buildRemoteViews(context, appWidgetId, applicationSettings.getWidgetConfig(appWidgetId)));
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.torrents_list);
}
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
applicationSettings.removeWidgetConfig(appWidgetId);
}
}
/**
* Loads and sets up the layout for some specific app widget given the user's widget settings. Note that the views
* for the list view rows are loaded separately in the {@link WidgetViewsFactory}.
@ -107,12 +64,14 @@ public class ListWidgetProvider extends AppWidgetProvider { @@ -107,12 +64,14 @@ public class ListWidgetProvider extends AppWidgetProvider {
public static RemoteViews buildRemoteViews(Context context, int appWidgetId, ListWidgetConfig config) {
// Does the server to show and its widget settings actually still exist?
if (context == null || config == null)
if (context == null || config == null) {
return null;
}
ApplicationSettings appSettings = ApplicationSettings_.getInstance_(context);
if (config.getServerId() < 0 || config.getServerId() > appSettings.getMaxOfAllServers()) {
Log.e(context, "Tried to set up widget " + appWidgetId + " but the bound server ID " + config.getServerId()
+ " no longer exists.");
Log_.getInstance_(context).e("ListWidgetProvider",
"Tried to set up widget " + appWidgetId + " but the bound server ID " + config.getServerId() +
" no longer exists.");
return null;
}
@ -137,7 +96,7 @@ public class ListWidgetProvider extends AppWidgetProvider { @@ -137,7 +96,7 @@ public class ListWidgetProvider extends AppWidgetProvider {
ServerSetting server = appSettings.getServerSetting(config.getServerId());
rv.setTextViewText(R.id.server_text, server.getName());
rv.setTextViewText(R.id.filter_text, config.getStatusType().getFilterItem(context).getName());
// Set up the START_SERVER intent for 'action bar' clicks to start Transdroid normally
Intent start = new Intent(context, TorrentsActivity_.class);
// start.setData(Uri.parse("intent://widget/" + appWidgetId + "/start/" + config.getServerId()));
@ -176,4 +135,46 @@ public class ListWidgetProvider extends AppWidgetProvider { @@ -176,4 +135,46 @@ public class ListWidgetProvider extends AppWidgetProvider {
}
@Override
public void onReceive(Context context, @NonNull Intent intent) {
super.onReceive(context, intent);
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
// Refresh a specific app widget
if (intent.hasExtra(EXTRA_REFRESH)) {
// Manually requested a refresh for the app widget of which the ID was supplied
RemoteViews views =
buildRemoteViews(context, appWidgetId, applicationSettings.getWidgetConfig(appWidgetId));
if (views != null) {
AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views);
AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(appWidgetId, R.id.torrents_list);
}
return;
}
// No refresh: this is a control intent: copy the action and EXTRA_APPWIDGET_ID to start the control service
if (intent.getAction().startsWith("org.transdroid.control.")) {
Intent action = new Intent(intent.getAction());
action.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
context.startService(action);
}
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
appWidgetManager.updateAppWidget(appWidgetId,
buildRemoteViews(context, appWidgetId, applicationSettings.getWidgetConfig(appWidgetId)));
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.torrents_list);
}
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
applicationSettings.removeWidgetConfig(appWidgetId);
}
}
}

100
app/src/main/java/org/transdroid/core/widget/ListWidgetViewsService.java

@ -16,16 +16,26 @@ @@ -16,16 +16,26 @@
*/
package org.transdroid.core.widget;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.annotation.TargetApi;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import org.androidannotations.annotations.EService;
import org.transdroid.R;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.ApplicationSettings_;
import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.app.settings.SystemSettings;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.lists.LocalTorrent;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.service.*;
import org.transdroid.core.gui.log.Log_;
import org.transdroid.core.service.ConnectivityHelper_;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Torrent;
@ -35,14 +45,9 @@ import org.transdroid.daemon.task.RetrieveTask; @@ -35,14 +45,9 @@ import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.util.FileSizeConverter;
import android.annotation.TargetApi;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A service for the list widget to update the remote views that a list widget shows, by getting the torrents from the
@ -65,13 +70,15 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @@ -65,13 +70,15 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private final Context context;
private final int appWidgetId;
private final Log log;
private List<Torrent> torrents = null;
private ListWidgetConfig config = null;
public WidgetViewsFactory(Context applicationContext, Intent intent) {
this.context = applicationContext;
this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
this.appWidgetId =
intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
this.log = Log_.getInstance_(applicationContext);
}
@Override
@ -86,27 +93,30 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @@ -86,27 +93,30 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
ApplicationSettings settings = ApplicationSettings_.getInstance_(context);
config = settings.getWidgetConfig(appWidgetId);
if (config == null || config.getServerId() < 0) {
Log.e(context, "Looking for widget data while the widget wasn't yet configured");
log.e(context, "Looking for widget data while the widget wasn't yet configured");
return;
}
ServerSetting server = settings.getServerSetting(config.getServerId());
if (server == null) {
// TODO: Show error text some how in the remote view, perhaps via the EmptyView's text?
Log.e(context, "The server for which this widget was created no longer exists");
if (torrents != null)
log.e(context, "The server for which this widget was created no longer exists");
if (torrents != null) {
torrents.clear();
}
return;
}
// Load the torrents; synchronously
IDaemonAdapter connection = server.createServerAdapter(ConnectivityHelper_.getInstance_(context)
.getConnectedNetworkName(), context);
DaemonTaskResult result = RetrieveTask.create(connection).execute();
IDaemonAdapter connection =
server.createServerAdapter(ConnectivityHelper_.getInstance_(context).getConnectedNetworkName(),
context);
DaemonTaskResult result = RetrieveTask.create(connection).execute(log);
if (!(result instanceof RetrieveTaskSuccessResult)) {
// TODO: Show error text somehow in the remote view, perhaps via the EmptyView's text?
Log.e(context, "The torrents could not be retrieved at this time; probably a connection issue");
if (torrents != null)
log.e(context, "The torrents could not be retrieved at this time; probably a connection issue");
if (torrents != null) {
torrents.clear();
}
return;
}
@ -115,8 +125,10 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @@ -115,8 +125,10 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
ArrayList<Torrent> filteredTorrents = new ArrayList<Torrent>();
List<Torrent> allTorrents = ((RetrieveTaskSuccessResult) result).getTorrents();
for (Torrent torrent : allTorrents) {
if (config.getStatusType().getFilterItem(context).matches(torrent, systemSettings.treatDormantAsInactive()))
if (config.getStatusType().getFilterItem(context)
.matches(torrent, systemSettings.treatDormantAsInactive())) {
filteredTorrents.add(torrent);
}
}
if (filteredTorrents.size() > 0) {
// Only sort when there are actually torrents left after filtering
@ -129,7 +141,7 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @@ -129,7 +141,7 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
// If the user asked to show the server status statistics, we need to update the widget remote views again
RemoteViews rv = ListWidgetProvider.buildRemoteViews(context, appWidgetId, config);
if (config.shouldShowStatusView()) {
// Update the server status count and speeds in the 'action bar'
int downcount = 0, upcount = 0, downspeed = 0, upspeed = 0;
for (Torrent torrent : torrents) {
@ -150,7 +162,7 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @@ -150,7 +162,7 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
rv.setTextViewText(R.id.upspeed_text, FileSizeConverter.getSize(upspeed) + "/s");
AppWidgetManager.getInstance(context.getApplicationContext()).updateAppWidget(appWidgetId, rv);
}
}
@ -170,21 +182,21 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @@ -170,21 +182,21 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
rv.setTextViewText(R.id.ratio_text, local.getProgressEtaRatioText(context.getResources()));
int statusColour;
switch (torrent.getStatusCode()) {
case Downloading:
statusColour = R.color.torrent_downloading;
break;
case Paused:
statusColour = R.color.torrent_paused;
break;
case Seeding:
statusColour = R.color.torrent_seeding;
break;
case Error:
statusColour = R.color.torrent_error;
break;
default: // Checking, Waiting, Queued, Unknown
statusColour = R.color.torrent_other;
break;
case Downloading:
statusColour = R.color.torrent_downloading;
break;
case Paused:
statusColour = R.color.torrent_paused;
break;
case Seeding:
statusColour = R.color.torrent_seeding;
break;
case Error:
statusColour = R.color.torrent_error;
break;
default: // Checking, Waiting, Queued, Unknown
statusColour = R.color.torrent_other;
break;
}
rv.setInt(R.id.status_view, "setBackgroundColor", context.getResources().getColor(statusColour));
Intent startIntent = new Intent();
@ -203,8 +215,9 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @@ -203,8 +215,9 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
@Override
public void onDestroy() {
if (torrents != null)
if (torrents != null) {
torrents.clear();
}
torrents = null;
}
@ -220,8 +233,9 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @@ -220,8 +233,9 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
@Override
public int getCount() {
if (torrents == null)
if (torrents == null) {
return 0;
}
return torrents.size();
}

16
app/src/main/java/org/transdroid/daemon/AlphanumComparator.java

@ -29,12 +29,12 @@ import java.util.Comparator; @@ -29,12 +29,12 @@ import java.util.Comparator;
public class AlphanumComparator implements Comparator<String> {
private final boolean isDigit(char ch) {
private boolean isDigit(char ch) {
return ch >= 48 && ch <= 57;
}
/** Length of string is passed in for improved efficiency (only need to calculate it once) **/
private final String getChunk(String s, int slength, int marker) {
private String getChunk(String s, int slength, int marker) {
StringBuilder chunk = new StringBuilder();
char c = s.charAt(marker);
chunk.append(c);
@ -59,12 +59,14 @@ public class AlphanumComparator implements Comparator<String> { @@ -59,12 +59,14 @@ public class AlphanumComparator implements Comparator<String> {
return chunk.toString();
}
public int compare(String o1, String o2) {
if (!(o1 instanceof String) || !(o2 instanceof String)) {
public int compare(String s1, String s2) {
if (s1 == null && s2 != null) {
return -1;
} else if (s1 != null && s2 == null) {
return 1;
} else if (s1 == null) {
return 0;
}
String s1 = (String) o1;
String s2 = (String) o2;
int thisMarker = 0;
int thatMarker = 0;
@ -79,7 +81,7 @@ public class AlphanumComparator implements Comparator<String> { @@ -79,7 +81,7 @@ public class AlphanumComparator implements Comparator<String> {
thatMarker += thatChunk.length();
// If both chunks contain numeric characters, sort them numerically
int result = 0;
int result;
if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) {
// Simple chunk comparison by length.
int thisChunkLength = thisChunk.length();

388
app/src/main/java/org/transdroid/daemon/Aria2c/Aria2Adapter.java

@ -17,15 +17,8 @@ @@ -17,15 +17,8 @@
*/
package org.transdroid.daemon.Aria2c;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import android.net.Uri;
import android.text.TextUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@ -36,6 +29,7 @@ import org.base64.android.Base64; @@ -36,6 +29,7 @@ import org.base64.android.Base64;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
@ -63,15 +57,20 @@ import org.transdroid.daemon.task.ResumeTask; @@ -63,15 +57,20 @@ import org.transdroid.daemon.task.ResumeTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import android.net.Uri;
import android.text.TextUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* The daemon adapter from the Aria2 torrent client. Documentation available at
* http://aria2.sourceforge.net/manual/en/html/aria2c.html
* The daemon adapter from the Aria2 torrent client. Documentation available at http://aria2.sourceforge.net/manual/en/html/aria2c.html
* @author erickok
*/
public class Aria2Adapter implements IDaemonAdapter {
@ -86,137 +85,143 @@ public class Aria2Adapter implements IDaemonAdapter { @@ -86,137 +85,143 @@ public class Aria2Adapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
JSONArray params = new JSONArray();
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
// NOTE Since there is no aria2.tellAll (or something) we have to use batch requests
JSONArray fields = new JSONArray().put("gid").put("status").put("totalLength").put("completedLength")
.put("uploadLength").put("downloadSpeed").put("uploadSpeed").put("numSeeders").put("dir")
.put("connections").put("errorCode").put("bittorrent").put("files");
JSONObject active = buildRequest("aria2.tellActive", new JSONArray().put(fields));
JSONObject waiting = buildRequest("aria2.tellWaiting", new JSONArray().put(0).put(9999).put(fields));
JSONObject stopped = buildRequest("aria2.tellStopped", new JSONArray().put(0).put(9999).put(fields));
params.put(active).put(waiting).put(stopped);
List<Torrent> torrents = new ArrayList<Torrent>();
JSONArray lists = makeRequestForArray(params.toString());
for (int i = 0; i < lists.length(); i++) {
torrents.addAll(parseJsonRetrieveTorrents(lists.getJSONObject(i).getJSONArray("result")));
}
return new RetrieveTaskSuccessResult((RetrieveTask) task, torrents, null);
case GetTorrentDetails:
case Retrieve:
// Request file listing of a torrent
params.put(task.getTargetTorrent().getUniqueID()); // gid
params.put(new JSONArray().put("bittorrent").put("errorCode"));
// Request all torrents from server
// NOTE Since there is no aria2.tellAll (or something) we have to use batch requests
JSONArray fields =
new JSONArray().put("gid").put("status").put("totalLength").put("completedLength")
.put("uploadLength").put("downloadSpeed").put("uploadSpeed").put("numSeeders")
.put("dir").put("connections").put("errorCode").put("bittorrent").put("files");
JSONObject active = buildRequest("aria2.tellActive", new JSONArray().put(fields));
JSONObject waiting =
buildRequest("aria2.tellWaiting", new JSONArray().put(0).put(9999).put(fields));
JSONObject stopped =
buildRequest("aria2.tellStopped", new JSONArray().put(0).put(9999).put(fields));
params.put(active).put(waiting).put(stopped);
JSONObject dinfo = makeRequest(buildRequest("aria2.tellStatus", params).toString());
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
parseJsonTorrentDetails(dinfo.getJSONObject("result")));
List<Torrent> torrents = new ArrayList<Torrent>();
JSONArray lists = makeRequestForArray(log, params.toString());
for (int i = 0; i < lists.length(); i++) {
torrents.addAll(parseJsonRetrieveTorrents(lists.getJSONObject(i).getJSONArray("result")));
}
return new RetrieveTaskSuccessResult((RetrieveTask) task, torrents, null);
case GetTorrentDetails:
// Request file listing of a torrent
params.put(task.getTargetTorrent().getUniqueID()); // gid
params.put(new JSONArray().put("bittorrent").put("errorCode"));
JSONObject dinfo = makeRequest(log, buildRequest("aria2.tellStatus", params).toString());
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
parseJsonTorrentDetails(dinfo.getJSONObject("result")));
case GetFileList:
// Request file listing of a torrent
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
JSONObject finfo = makeRequest(log, buildRequest("aria2.getFiles", params).toString());
return new GetFileListTaskSuccessResult((GetFileListTask) task,
parseJsonFileListing(finfo.getJSONArray("result"), task.getTargetTorrent()));
case AddByFile:
// Encode the .torrent file's data
String file = ((AddByFileTask) task).getFile();
InputStream in =
new Base64.InputStream(new FileInputStream(new File(URI.create(file))), Base64.ENCODE);
StringWriter writer = new StringWriter();
int c;
while ((c = in.read()) != -1) {
writer.write(c);
}
in.close();
// Request to add a torrent by local .torrent file
params.put(writer.toString());
makeRequest(log, buildRequest("aria2.addTorrent", params).toString());
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
params.put(new JSONArray().put(url));
makeRequest(log, buildRequest("aria2.addUri", params).toString());
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
params.put(new JSONArray().put(magnet));
makeRequest(log, buildRequest("aria2.addUri", params).toString());
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(log,
buildRequest(removeTask.includingData() ? "aria2.removeDownloadResult" : "aria2.remove",
params.put(removeTask.getTargetTorrent().getUniqueID())).toString());
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
PauseTask pauseTask = (PauseTask) task;
makeRequest(log, buildRequest("aria2.pause", params.put(pauseTask.getTargetTorrent().getUniqueID()))
.toString());
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Resume all torrents
makeRequest(log, buildRequest("aria2.pauseAll", null).toString());
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
ResumeTask resumeTask = (ResumeTask) task;
makeRequest(log,
buildRequest("aria2.unpause", params.put(resumeTask.getTargetTorrent().getUniqueID()))
.toString());
return new DaemonTaskSuccessResult(task);
case GetFileList:
case ResumeAll:
// Request file listing of a torrent
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
// Resume all torrents
makeRequest(log, buildRequest("aria2.unpauseAll", null).toString());
return new DaemonTaskSuccessResult(task);
JSONObject finfo = makeRequest(buildRequest("aria2.getFiles", params).toString());
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFileListing(
finfo.getJSONArray("result"), task.getTargetTorrent()));
case AddByFile:
// Encode the .torrent file's data
String file = ((AddByFileTask) task).getFile();
InputStream in = new Base64.InputStream(new FileInputStream(new File(URI.create(file))), Base64.ENCODE);
StringWriter writer = new StringWriter();
int c;
while ((c = in.read()) != -1) {
writer.write(c);
}
in.close();
case SetTransferRates:
// Request to add a torrent by local .torrent file
params.put(writer.toString());
makeRequest(buildRequest("aria2.addTorrent", params).toString());
return new DaemonTaskSuccessResult(task);
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
JSONObject options = new JSONObject();
options.put("max-overall-download-limit",
(ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate()));
options.put("max-overall-upload-limit",
(ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate()));
case AddByUrl:
makeRequest(log, buildRequest("aria2.changeGlobalOption", params.put(options)).toString());
return new DaemonTaskSuccessResult(task);
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
params.put(new JSONArray().put(url));
makeRequest(buildRequest("aria2.addUri", params).toString());
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
params.put(new JSONArray().put(magnet));
makeRequest(buildRequest("aria2.addUri", params).toString());
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(buildRequest(removeTask.includingData() ? "aria2.removeDownloadResult" : "aria2.remove",
params.put(removeTask.getTargetTorrent().getUniqueID())).toString());
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
PauseTask pauseTask = (PauseTask) task;
makeRequest(buildRequest("aria2.pause", params.put(pauseTask.getTargetTorrent().getUniqueID()))
.toString());
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Resume all torrents
makeRequest(buildRequest("aria2.pauseAll", null).toString());
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
ResumeTask resumeTask = (ResumeTask) task;
makeRequest(buildRequest("aria2.unpause", params.put(resumeTask.getTargetTorrent().getUniqueID()))
.toString());
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeRequest(buildRequest("aria2.unpauseAll", null).toString());
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
JSONObject options = new JSONObject();
options.put("max-overall-download-limit", (ratesTask.getDownloadRate() == null ? -1 : ratesTask
.getDownloadRate().intValue()));
options.put("max-overall-upload-limit", (ratesTask.getUploadRate() == null ? -1 : ratesTask
.getUploadRate().intValue()));
makeRequest(buildRequest("aria2.changeGlobalOption", params.put(options)).toString());
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
@ -252,27 +257,27 @@ public class Aria2Adapter implements IDaemonAdapter { @@ -252,27 +257,27 @@ public class Aria2Adapter implements IDaemonAdapter {
}
private synchronized JSONObject makeRequest(String data) throws DaemonException {
String raw = makeRawRequest(data);
private synchronized JSONObject makeRequest(Log log, String data) throws DaemonException {
String raw = makeRawRequest(log, data);
try {
return new JSONObject(raw);
} catch (JSONException e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.UnexpectedResponse, e.toString());
}
}
private synchronized JSONArray makeRequestForArray(String data) throws DaemonException {
String raw = makeRawRequest(data);
private synchronized JSONArray makeRequestForArray(Log log, String data) throws DaemonException {
String raw = makeRawRequest(log, data);
try {
return new JSONArray(raw);
} catch (JSONException e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.UnexpectedResponse, e.toString());
}
}
private synchronized String makeRawRequest(String data) throws DaemonException {
private synchronized String makeRawRequest(Log log, String data) throws DaemonException {
try {
@ -284,8 +289,9 @@ public class Aria2Adapter implements IDaemonAdapter { @@ -284,8 +289,9 @@ public class Aria2Adapter implements IDaemonAdapter {
}
// Set POST URL and data
String url = (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":"
+ settings.getPort() + (settings.getFolder() == null ? "" : settings.getFolder()) + "/jsonrpc";
String url =
(settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() +
(settings.getFolder() == null ? "" : settings.getFolder()) + "/jsonrpc";
HttpPost httppost = new HttpPost(url);
httppost.setEntity(new StringEntity(data));
httppost.setHeader("Content-Type", "application/json");
@ -295,22 +301,22 @@ public class Aria2Adapter implements IDaemonAdapter { @@ -295,22 +301,22 @@ public class Aria2Adapter implements IDaemonAdapter {
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (entity == null)
if (entity == null) {
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity in response object.");
}
// Read JSON response
InputStream instream = entity.getContent();
String result = HttpHelper.convertStreamToString(instream);
instream.close();
DLog.d(LOG_NAME,
"Success: "
+ (result.length() > 300 ? result.substring(0, 300) + "... (" + result.length() + " chars)"
: result));
log.d(LOG_NAME, "Success: " +
(result.length() > 300 ? result.substring(0, 300) + "... (" + result.length() + " chars)" :
result));
return result;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
@ -332,23 +338,26 @@ public class Aria2Adapter implements IDaemonAdapter { @@ -332,23 +338,26 @@ public class Aria2Adapter implements IDaemonAdapter {
int errorCode = tor.optInt("errorCode", 0);
String error = errorCode > 0 ? convertAriaError(errorCode) : null;
String name = null;
JSONObject bittorrent = null;
JSONObject bittorrent;
if (tor.has("bittorrent")) {
// Get name form the bittorrent info object
bittorrent = tor.getJSONObject("bittorrent");
if (bittorrent.has("info"))
if (bittorrent.has("info")) {
name = bittorrent.getJSONObject("info").getString("name");
}
} else if (tor.has("files")) {
// Get name from the first included file we can find
JSONArray files = tor.getJSONArray("files");
if (files.length() > 0) {
name = Uri.parse(files.getJSONObject(0).getString("path")).getLastPathSegment();
if (name == null)
if (name == null) {
name = files.getJSONObject(0).getString("path");
}
}
}
if (name == null)
if (name == null) {
name = tor.getString("gid"); // Fallback name
}
// @formatter:off
torrents.add(new Torrent(
j,
@ -388,11 +397,11 @@ public class Aria2Adapter implements IDaemonAdapter { @@ -388,11 +397,11 @@ public class Aria2Adapter implements IDaemonAdapter {
JSONObject file = response.getJSONObject(j);
// Add the parsed torrent to the list
// @formatter:off
String rel = file.getString("path");
if (rel.startsWith(torrent.getLocationDir())) {
rel = rel.substring(torrent.getLocationDir().length());
}
// @formatter:off
files.add(new TorrentFile(
Integer.toString(file.getInt("index")),
rel,
@ -415,8 +424,9 @@ public class Aria2Adapter implements IDaemonAdapter { @@ -415,8 +424,9 @@ public class Aria2Adapter implements IDaemonAdapter {
List<String> errors = new ArrayList<String>();
int error = response.optInt("errorCode", 0);
if (error > 0)
if (error > 0) {
errors.add(convertAriaError(error));
}
if (response.has("bittorrent")) {
JSONObject bittorrent = response.getJSONObject("bittorrent");
@ -454,42 +464,42 @@ public class Aria2Adapter implements IDaemonAdapter { @@ -454,42 +464,42 @@ public class Aria2Adapter implements IDaemonAdapter {
// Aria2 sends an exit code as error (http://aria2.sourceforge.net/manual/en/html/aria2c.html#id1)
String error = "Aria error #" + Integer.toString(errorCode);
switch (errorCode) {
case 3:
case 4:
return error + ": Resource was not found";
case 5:
return error + ": Aborted because download speed was too slow";
case 6:
return error + ": Network problem occurred";
case 8:
return error + ": Remote server did not support resume when resume was required to complete download";
case 9:
return error + ": There was not enough disk space available";
case 11:
case 12:
return error + ": Duplicate file or info hash download";
case 15:
case 16:
return error + ": Aria2 could not create new or open or truncate existing file";
case 17:
case 18:
case 19:
return error + ": File I/O error occurred";
case 20:
case 27:
return error + ": Aria2 could not parse Magnet URI or Metalink document";
case 21:
return error + ": FTP command failed";
case 22:
return error + ": HTTP response header was bad or unexpected";
case 23:
return error + ": Too many redirects occurred";
case 24:
return error + ": HTTP authorization failed";
case 26:
return error + ": \".torrent\" file is corrupted or missing information that aria2 needs";
default:
return error;
case 3:
case 4:
return error + ": Resource was not found";
case 5:
return error + ": Aborted because download speed was too slow";
case 6:
return error + ": Network problem occurred";
case 8:
return error + ": Remote server did not support resume when resume was required to complete download";
case 9:
return error + ": There was not enough disk space available";
case 11:
case 12:
return error + ": Duplicate file or info hash download";
case 15:
case 16:
return error + ": Aria2 could not create new or open or truncate existing file";
case 17:
case 18:
case 19:
return error + ": File I/O error occurred";
case 20:
case 27:
return error + ": Aria2 could not parse Magnet URI or Metalink document";
case 21:
return error + ": FTP command failed";
case 22:
return error + ": HTTP response header was bad or unexpected";
case 23:
return error + ": Too many redirects occurred";
case 24:
return error + ": HTTP authorization failed";
case 26:
return error + ": \".torrent\" file is corrupted or missing information that aria2 needs";
default:
return error;
}
}

653
app/src/main/java/org/transdroid/daemon/BitComet/BitCometAdapter.java

@ -17,16 +17,10 @@ @@ -17,16 +17,10 @@
*/
package org.transdroid.daemon.BitComet;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.android.internalcopy.http.multipart.BitCometFilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import com.android.internalcopy.http.multipart.Utf8StringPart;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@ -38,18 +32,19 @@ import org.apache.http.client.methods.HttpPost; @@ -38,18 +32,19 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
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.AddByMagnetUrlTask;
import org.transdroid.daemon.task.AddByUrlTask;
import org.transdroid.daemon.task.DaemonTask;
import org.transdroid.daemon.task.DaemonTaskFailureResult;
import org.transdroid.daemon.task.DaemonTaskResult;
@ -60,26 +55,28 @@ import org.transdroid.daemon.task.RemoveTask; @@ -60,26 +55,28 @@ import org.transdroid.daemon.task.RemoveTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.Part;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.BitCometFilePart;
import com.android.internalcopy.http.multipart.Utf8StringPart;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* The daemon adapter for the BitComet torrent client.
*
* @author SeNS (sensboston)
*
* 09/26/2012: added AJAX support for BitComet v.1.34 and up
* : added additional tasks support
* <p/>
* 09/26/2012: added AJAX support for BitComet v.1.34 and up : added additional tasks support
*/
public class BitCometAdapter implements IDaemonAdapter {
@ -87,136 +84,175 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -87,136 +84,175 @@ public class BitCometAdapter implements IDaemonAdapter {
private DaemonSettings settings;
private DefaultHttpClient httpclient;
public BitCometAdapter(DaemonSettings settings) {
this.settings = settings;
}
/**
* Returns the size of the torrent, as parsed form some string
* @param size The size in a string format, i.e. '691 MB'
* @return The size in bytes
*/
private static long convertSize(String size) {
try {
if (size.endsWith("GB")) {
return (long) (Float.parseFloat(size.substring(0, size.indexOf("GB"))) * 1024 * 1024 * 1024);
} else if (size.endsWith("MB")) {
return (long) (Float.parseFloat(size.substring(0, size.indexOf("MB"))) * 1024 * 1024);
} else if (size.endsWith("kB")) {
return (long) (Float.parseFloat(size.substring(0, size.indexOf("kB"))) * 1024);
} else if (size.endsWith("B")) {
return (long) (Float.parseFloat(size.substring(0, size.indexOf("B"))));
}
} catch (Exception e) {
}
return 0;
}
/**
* Returns the part done (or progress) of a torrent, as parsed from some string
* @param progress The part done in a string format, i.e. '15.96'
* @return The part done as [0..1] fraction, i.e. 0.1596
*/
public static float convertProgress(String progress) {
return Float.parseFloat(progress) / 1000.0f;
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
// first, check client for the new AJAX interface (BitComet v.1.34 and up)
try {
String xmlResult = makeRequest("/panel/task_list_xml");
if (xmlResult.startsWith("<?xml", 0)) {
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseXmlTorrents(xmlResult), null);
case Retrieve:
// Request all torrents from server
// first, check client for the new AJAX interface (BitComet v.1.34 and up)
try {
String xmlResult = makeRequest(log, "/panel/task_list_xml");
if (xmlResult.startsWith("<?xml", 0)) {
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseXmlTorrents(xmlResult),
null);
}
} catch (Exception e) {
// it's probably an old client, parse HTML instead
String htmlResult = makeRequest(log, "/panel/task_list");
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseHttpTorrents(log, htmlResult),
null);
}
} catch (Exception e) {
// it's probably an old client, parse HTML instead
String htmlResult = makeRequest("/panel/task_list");
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseHttpTorrents(htmlResult), null);
}
case GetFileList:
// Request files listing for a specific torrent
String fhash = ((GetFileListTask)task).getTargetTorrent().getUniqueID();
String fileListResult = makeRequest("/panel/task_detail", new BasicNameValuePair("id", fhash),
new BasicNameValuePair("show", "files"));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseHttpTorrentFiles(fileListResult,
fhash));
case AddByFile:
// Upload a local .torrent file
String ufile = ((AddByFileTask)task).getFile();
makeFileUploadRequest("/panel/task_add_bt_result", ufile);
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask)task).getUrl();
makeUploadUrlRequest("/panel/task_add_httpftp_result", url);
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a torrent by URL
String magnetUrl = ((AddByMagnetUrlTask)task).getUrl();
makeUploadUrlRequest("/panel/task_add_httpftp_result", magnetUrl);
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest("/panel/task_delete", new BasicNameValuePair("id", removeTask.getTargetTorrent().getUniqueID()),
new BasicNameValuePair("action", (removeTask.includingData()? "delete_all": "delete_task")));
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
makeRequest("/panel/task_action", new BasicNameValuePair("id", task.getTargetTorrent().getUniqueID()), new BasicNameValuePair("action", "stop"));
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeRequest("/panel/task_action", new BasicNameValuePair("id", task.getTargetTorrent().getUniqueID()), new BasicNameValuePair("action", "start"));
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Suspend (pause) all active torrents
makeRequest("/panel/tasklist_action", new BasicNameValuePair("id", "suspend_all"));
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume suspended torrents
makeRequest("/panel/tasklist_action", new BasicNameValuePair("id", "resume_all"));
return new DaemonTaskSuccessResult(task);
case StopAll:
// Stop all torrents
makeRequest("/panel/tasklist_action", new BasicNameValuePair("id", "stop_all"));
return new DaemonTaskSuccessResult(task);
case StartAll:
// Start all torrents for download and seeding
makeRequest("/panel/tasklist_action", new BasicNameValuePair("id", "start_all_download"));
makeRequest("/panel/tasklist_action", new BasicNameValuePair("id", "start_all_seeding"));
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
String dl = Integer.toString((ratesTask.getDownloadRate() == null? -1: ratesTask.getDownloadRate().intValue()));
String ul = Integer.toString((ratesTask.getUploadRate() == null? -1: ratesTask.getUploadRate().intValue()));
makeRequest("/panel/option_set", new BasicNameValuePair("key", "down_rate_max"), new BasicNameValuePair("value", dl));
makeRequest("/panel/option_set", new BasicNameValuePair("key", "up_rate_max"), new BasicNameValuePair("value", ul));
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, task.getMethod() + " is not supported by " + getType()));
case GetFileList:
// Request files listing for a specific torrent
String fhash = task.getTargetTorrent().getUniqueID();
String fileListResult = makeRequest(log, "/panel/task_detail", new BasicNameValuePair("id", fhash),
new BasicNameValuePair("show", "files"));
return new GetFileListTaskSuccessResult((GetFileListTask) task,
parseHttpTorrentFiles(fileListResult, fhash));
case AddByFile:
// Upload a local .torrent file
String ufile = ((AddByFileTask) task).getFile();
makeFileUploadRequest(log, "/panel/task_add_bt_result", ufile);
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeUploadUrlRequest(log, "/panel/task_add_httpftp_result", url);
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a torrent by URL
String magnetUrl = ((AddByMagnetUrlTask) task).getUrl();
makeUploadUrlRequest(log, "/panel/task_add_httpftp_result", magnetUrl);
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(log, "/panel/task_delete",
new BasicNameValuePair("id", removeTask.getTargetTorrent().getUniqueID()),
new BasicNameValuePair("action",
(removeTask.includingData() ? "delete_all" : "delete_task")));
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
makeRequest(log, "/panel/task_action",
new BasicNameValuePair("id", task.getTargetTorrent().getUniqueID()),
new BasicNameValuePair("action", "stop"));
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeRequest(log, "/panel/task_action",
new BasicNameValuePair("id", task.getTargetTorrent().getUniqueID()),
new BasicNameValuePair("action", "start"));
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Suspend (pause) all active torrents
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "suspend_all"));
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume suspended torrents
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "resume_all"));
return new DaemonTaskSuccessResult(task);
case StopAll:
// Stop all torrents
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "stop_all"));
return new DaemonTaskSuccessResult(task);
case StartAll:
// Start all torrents for download and seeding
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "start_all_download"));
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "start_all_seeding"));
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
String dl =
Integer.toString((ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate()));
String ul = Integer.toString((ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate()));
makeRequest(log, "/panel/option_set", new BasicNameValuePair("key", "down_rate_max"),
new BasicNameValuePair("value", dl));
makeRequest(log, "/panel/option_set", new BasicNameValuePair("key", "up_rate_max"),
new BasicNameValuePair("value", ul));
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
}
catch (DaemonException e) {
} catch (DaemonException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
}
}
/**
* Instantiates an HTTP client with proper credentials that can be used for all Buffalo NAS requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On conflicting or missing settings
*/
private void initialise(int connectionTimeout) throws DaemonException {
private void initialise() throws DaemonException {
httpclient = HttpHelper.createStandardHttpClient(settings, true);
}
/**
* Build the URL of the HTTP request from the user settings
* @return The URL to request
@ -224,14 +260,14 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -224,14 +260,14 @@ public class BitCometAdapter implements IDaemonAdapter {
private String buildWebUIUrl(String path) {
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() + path;
}
private String makeRequest(String url, NameValuePair... params) throws DaemonException {
private String makeRequest(Log log, String url, NameValuePair... params) throws DaemonException {
try {
// Initialize the HTTP client
if (httpclient == null) {
initialise(HttpHelper.DEFAULT_CONNECTION_TIMEOUT);
initialise();
}
// Add the parameters to the query string
@ -250,116 +286,119 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -250,116 +286,119 @@ public class BitCometAdapter implements IDaemonAdapter {
HttpResponse response = httpclient.execute(new HttpGet(buildWebUIUrl(url)));
HttpEntity entity = response.getEntity();
if (entity != null) {
// Read HTTP response
java.io.InputStream instream = entity.getContent();
String result = HttpHelper.convertStreamToString(instream);
instream.close();
// Return raw result
return result;
}
DLog.d(LOG_NAME, "Error: No entity in HTTP response");
log.d(LOG_NAME, "Error: No entity in HTTP response");
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response.");
} catch (UnsupportedEncodingException e) {
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private boolean makeFileUploadRequest(String path, String file) throws DaemonException {
private boolean makeFileUploadRequest(Log log, String path, String file) throws DaemonException {
try {
// Initialize the HTTP client
if (httpclient == null) {
initialise(HttpHelper.DEFAULT_CONNECTION_TIMEOUT);
initialise();
}
// Get default download file location first
HttpResponse response = httpclient.execute(new HttpGet(buildWebUIUrl("/panel/task_add_bt")));
HttpEntity entity = response.getEntity();
if (entity != null) {
// Read BitComet response
java.io.InputStream instream = entity.getContent();
String result = HttpHelper.convertStreamToString(instream);
instream.close();
int idx = result.indexOf("save_path' value='")+18;
int idx = result.indexOf("save_path' value='") + 18;
String defaultPath = result.substring(idx, result.indexOf("'>", idx));
// Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path));
File upload = new File(URI.create(file));
Part[] parts = { new BitCometFilePart("torrent_file", upload), new Utf8StringPart("save_path", defaultPath) };
Part[] parts =
{new BitCometFilePart("torrent_file", upload), new Utf8StringPart("save_path", defaultPath)};
httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
// Make the request
response = httpclient.execute(httppost);
entity = response.getEntity();
if (entity != null) {
// Check BitComet response
instream = entity.getContent();
result = HttpHelper.convertStreamToString(instream);
instream.close();
if (result.indexOf("failed!") > 0) throw new Exception("Adding torrent file failed");
if (result.indexOf("failed!") > 0) {
throw new Exception("Adding torrent file failed");
}
}
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
}
return false;
} catch (FileNotFoundException e) {
throw new DaemonException(ExceptionType.FileAccessError, e.toString());
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private boolean makeUploadUrlRequest(String path, String url) throws DaemonException {
private boolean makeUploadUrlRequest(Log log, String path, String url) throws DaemonException {
try {
// Initialize the HTTP client
if (httpclient == null) {
initialise(HttpHelper.DEFAULT_CONNECTION_TIMEOUT);
initialise();
}
// Get default download file location first
HttpResponse response = httpclient.execute(new HttpGet(buildWebUIUrl("/panel/task_add_httpftp")));
HttpEntity entity = response.getEntity();
if (entity != null) {
// Read BitComet response
java.io.InputStream instream = entity.getContent();
String result = HttpHelper.convertStreamToString(instream);
instream.close();
int idx = result.indexOf("save_path' value='")+18;
int idx = result.indexOf("save_path' value='") + 18;
String defaultPath = result.substring(idx, result.indexOf("'>", idx));
// Setup form fields and post request
HttpPost httppost = new HttpPost(buildWebUIUrl(path));
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("url", url));
params.add(new BasicNameValuePair("save_path", defaultPath));
params.add(new BasicNameValuePair("save_path", defaultPath));
params.add(new BasicNameValuePair("connection", "5"));
params.add(new BasicNameValuePair("ReferPage", ""));
params.add(new BasicNameValuePair("textSpeedLimit", "0"));
httppost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
// Make the request
response = httpclient.execute(httppost);
entity = response.getEntity();
if (entity != null) {
// Check BitComet response
@ -370,95 +409,105 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -370,95 +409,105 @@ public class BitCometAdapter implements IDaemonAdapter {
throw new Exception("Adding URL failed");
}
}
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
}
return false;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
/**
* Parse BitComet HTML page (http response)
* @param response
* @return
* Parse BitComet HTML page (http response)
* @param response The raw HTML response from the server
* @return The parsed list of torrents from the raw HTML content
* @throws DaemonException
*/
private ArrayList<Torrent> parseHttpTorrents(String response) throws DaemonException {
private ArrayList<Torrent> parseHttpTorrents(Log log, String response) throws DaemonException {
ArrayList<Torrent> torrents = new ArrayList<Torrent>();
try {
// Find, prepare and split substring with HTML tag TABLE
String[] parts = response.substring(response.indexOf("<TABLE"),response.indexOf("</TABLE>")).replaceAll("</td>", "").replaceAll("</tr>", "").replaceAll("\n", "").split("<tr>");
for (int i=2; i<parts.length; i++) {
// Find, prepare and split substring with HTML tag TABLE
String[] parts =
response.substring(response.indexOf("<TABLE"), response.indexOf("</TABLE>")).replaceAll("</td>", "")
.replaceAll("</tr>", "").replaceAll("\n", "").split("<tr>");
for (int i = 2; i < parts.length; i++) {
String[] subParts = parts[i].replaceAll("<td>", "<td").split("<td");
if (subParts.length == 10 && subParts[1].contains("BT") ) {
String name = subParts[2].substring(subParts[2].indexOf("/panel/task_detail"));
name = name.substring(name.indexOf(">")+1, name.indexOf("<"));
if (subParts.length == 10 && subParts[1].contains("BT")) {
String name = subParts[2].substring(subParts[2].indexOf("/panel/task_detail"));
name = name.substring(name.indexOf(">") + 1, name.indexOf("<"));
TorrentStatus status = convertStatus(subParts[3]);
String percenDoneStr = subParts[6];
String downloadRateStr = subParts[7];
String uploadRateStr = subParts[8];
long size = convertSize(subParts[5]);
float percentDone = Float.parseFloat(percenDoneStr.substring(0, percenDoneStr.indexOf("%")));
long sizeDone = (long) (size * percentDone / 100 );
int rateUp = 1000 * Integer.parseInt(uploadRateStr.substring(0, uploadRateStr.indexOf("kB/s")));
int rateDown = 1000 * Integer.parseInt(downloadRateStr.substring(0, downloadRateStr.indexOf("kB/s")));
long sizeDone = (long) (size * percentDone / 100);
int rateUp = 1000 * Integer.parseInt(uploadRateStr.substring(0, uploadRateStr.indexOf("kB/s")));
int rateDown =
1000 * Integer.parseInt(downloadRateStr.substring(0, downloadRateStr.indexOf("kB/s")));
// Unfortunately, there is no info for above values providing by BitComet now,
// Unfortunately, there is no info for above values providing by BitComet now,
// so we may only send additional request for that
int leechers = 0;
int seeders = 0;
int knownLeechers = 0;
int knownSeeders = 0;
int distributed_copies = 0;
long sizeUp = 0;
String comment = "";
Date dateAdded = new Date();
// Comment code below to speedup torrent listing
long sizeUp;
String comment;
Date dateAdded;
// Comment code below to speedup torrent listing
// P.S. feature request to extend torrents info is already sent to the BitComet developers
//*
try {
// Lets make summary request and parse details
String summary = makeRequest("/panel/task_detail", new BasicNameValuePair("id", ""+(i-2)), new BasicNameValuePair("show", "summary"));
String[] sumParts = summary.substring(summary.indexOf("<div align=\"left\">Value</div></th>")).split("<tr><td>");
comment = sumParts[7].substring(sumParts[7].indexOf("<td>")+4, sumParts[7].indexOf("</td></tr>"));
// Indexes for date and uploaded size
int idx = 9;
int sizeIdx = 12;
if (status == TorrentStatus.Downloading) {
seeders = Integer.parseInt(sumParts[9].substring(sumParts[9].indexOf("Seeds:")+6, sumParts[9].indexOf("(Max possible")));
leechers = Integer.parseInt(sumParts[9].substring(sumParts[9].indexOf("Peers:")+6, sumParts[9].lastIndexOf("(Max possible")));
knownSeeders = Integer.parseInt(sumParts[9].substring(sumParts[9].indexOf("(Max possible:")+14, sumParts[9].indexOf(")")));
knownLeechers = Integer.parseInt(sumParts[9].substring(sumParts[9].lastIndexOf("(Max possible:")+14, sumParts[9].lastIndexOf(")")));
idx = 13;
sizeIdx = 16;
}
DateFormat df = new SimpleDateFormat("yyyy-mm-dd kk:mm:ss");
dateAdded = df.parse(sumParts[idx].substring(sumParts[idx].indexOf("<td>")+4, sumParts[idx].indexOf("</td></tr>")));
//sizeDone = convertSize(sumParts[sizeIdx].substring(sumParts[sizeIdx].indexOf("<td>")+4, sumParts[sizeIdx].indexOf(" (")));
sizeUp = convertSize(sumParts[sizeIdx+1].substring(sumParts[sizeIdx+1].indexOf("<td>")+4, sumParts[sizeIdx+1].indexOf(" (")));
// Lets make summary request and parse details
String summary = makeRequest(log, "/panel/task_detail", new BasicNameValuePair("id", "" + (i - 2)),
new BasicNameValuePair("show", "summary"));
String[] sumParts = summary.substring(summary.indexOf("<div align=\"left\">Value</div></th>"))
.split("<tr><td>");
comment = sumParts[7].substring(sumParts[7].indexOf("<td>") + 4, sumParts[7].indexOf("</td></tr>"));
// Indexes for date and uploaded size
int idx = 9;
int sizeIdx = 12;
if (status == TorrentStatus.Downloading) {
seeders = Integer.parseInt(sumParts[9]
.substring(sumParts[9].indexOf("Seeds:") + 6, sumParts[9].indexOf("(Max possible")));
leechers = Integer.parseInt(sumParts[9].substring(sumParts[9].indexOf("Peers:") + 6,
sumParts[9].lastIndexOf("(Max possible")));
knownSeeders = Integer.parseInt(sumParts[9]
.substring(sumParts[9].indexOf("(Max possible:") + 14, sumParts[9].indexOf(")")));
knownLeechers = Integer.parseInt(sumParts[9]
.substring(sumParts[9].lastIndexOf("(Max possible:") + 14,
sumParts[9].lastIndexOf(")")));
idx = 13;
sizeIdx = 16;
}
catch (Exception e) {}
DateFormat df = new SimpleDateFormat("yyyy-mm-dd kk:mm:ss");
dateAdded = df.parse(sumParts[idx]
.substring(sumParts[idx].indexOf("<td>") + 4, sumParts[idx].indexOf("</td></tr>")));
//sizeDone = convertSize(sumParts[sizeIdx].substring(sumParts[sizeIdx].indexOf("<td>")+4, sumParts[sizeIdx].indexOf(" (")));
sizeUp = convertSize(sumParts[sizeIdx + 1]
.substring(sumParts[sizeIdx + 1].indexOf("<td>") + 4, sumParts[sizeIdx + 1].indexOf(" (")));
//*
// Add the parsed torrent to the list
// @formatter:off
torrents.add(new Torrent(
(long)i-2,
null,
@ -482,25 +531,24 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -482,25 +531,24 @@ public class BitCometAdapter implements IDaemonAdapter {
null,
null,
settings.getType()));
// @formatter:on
}
}
}
catch (Exception e) {
} catch (Exception e) {
throw new DaemonException(ExceptionType.UnexpectedResponse, "Invalid BitComet HTTP response.");
}
return torrents;
}
/**
* Parse BitComet AJAX response
* that code was copy-pasted and slightly modified from \Ktorrent\StatsParser.java
* @param response
* @return
* Parse BitComet AJAX response that code was copy-pasted and slightly modified from \Ktorrent\StatsParser.java
* @param response The raw XML data as string that was returned by the server
* @return The parsed list of torrents from the XML
* @throws DaemonException
*/
private ArrayList<Torrent> parseXmlTorrents(String response) throws DaemonException {
private ArrayList<Torrent> parseXmlTorrents(String response) throws DaemonException {
ArrayList<Torrent> torrents = new ArrayList<Torrent>();
try {
@ -511,8 +559,7 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -511,8 +559,7 @@ public class BitCometAdapter implements IDaemonAdapter {
// Temp variables to load into torrent objects
int id = 0;
String name = "";
@SuppressWarnings("unused")
String hash = "";
@SuppressWarnings("unused") String hash = "";
TorrentStatus status = TorrentStatus.Unknown;
long sizeDone = 0;
long sizeUp = 0;
@ -526,45 +573,47 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -526,45 +573,47 @@ public class BitCometAdapter implements IDaemonAdapter {
float progress = 0;
String label = "";
Date dateAdded = new Date();
// Start pulling
int next = xpp.nextTag();
String tagName = xpp.getName();
while (next != XmlPullParser.END_DOCUMENT) {
if (next == XmlPullParser.END_TAG && tagName.equals("task")) {
// End of a 'transfer' item, add gathered torrent data
sizeDone = (long) (totalSize * progress);
// @formatter:off
torrents.add(new Torrent(
id,
id,
null, // hash, // we suppose to use simple integer IDs
name,
status,
null,
rateDown,
rateUp,
seeders,
seedersTotal,
leechers,
leechersTotal,
name,
status,
null,
rateDown,
rateUp,
seeders,
seedersTotal,
leechers,
leechersTotal,
(int) ((status == TorrentStatus.Downloading && rateDown != 0)? (totalSize - sizeDone) / rateDown: -1), // eta (in seconds) = (total_size_in_btes - bytes_already_downloaded) / bytes_per_second
sizeDone,
sizeUp,
totalSize,
progress,
sizeDone,
sizeUp,
totalSize,
progress,
0f,
label,
dateAdded,
null,
null, // Not supported in the web interface
settings.getType()));
// @formatter:on
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 && tagName.equals("task")){
} else if (next == XmlPullParser.START_TAG && tagName.equals("task")) {
// Start of a new 'transfer' item; reset gathered torrent data
name = "";
//hash = "";
@ -581,9 +630,9 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -581,9 +630,9 @@ public class BitCometAdapter implements IDaemonAdapter {
progress = 0;
label = "";
dateAdded = new Date();
} else if (next == XmlPullParser.START_TAG){
} else if (next == XmlPullParser.START_TAG) {
// Probably encountered a torrent property, i.e. '<type>BT</type>'
next = xpp.next();
if (next == XmlPullParser.TEXT) {
@ -620,13 +669,13 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -620,13 +669,13 @@ public class BitCometAdapter implements IDaemonAdapter {
}
}
}
next = xpp.next();
if (next == XmlPullParser.START_TAG || next == XmlPullParser.END_TAG) {
tagName = xpp.getName();
}
}
} catch (XmlPullParserException e) {
throw new DaemonException(ExceptionType.ParsingFailed, e.toString());
} catch (Exception e) {
@ -635,33 +684,35 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -635,33 +684,35 @@ public class BitCometAdapter implements IDaemonAdapter {
return torrents;
}
/**
* Parse BitComet HTML page (HTTP response)
* @param response
* @return
* Parse BitComet HTML page (HTTP response)
* @param response The raw HTML response from the server
* @return The parsed list of files in the torrent from the raw HTML
* @throws DaemonException
*/
private ArrayList<TorrentFile> parseHttpTorrentFiles(String response, String hash) throws DaemonException {
// Parse response
ArrayList<TorrentFile> torrentfiles = new ArrayList<TorrentFile>();
try {
String[] files = response.substring(response.indexOf("Operation Method</div></th>")+27, response.lastIndexOf("</TABLE>")).replaceAll("</td>", "").replaceAll("</tr>", "").split("<tr>");
try {
String[] files = response.substring(response.indexOf("Operation Method</div></th>") + 27,
response.lastIndexOf("</TABLE>")).replaceAll("</td>", "").replaceAll("</tr>", "").split("<tr>");
for (int i = 1; i < files.length; i++) {
String[] fileDetails = files[i].replace(">","").split("<td");
long size = convertSize(fileDetails[4].substring(fileDetails[4].indexOf("&nbsp&nbsp ")+11));
String[] fileDetails = files[i].replace(">", "").split("<td");
long size = convertSize(fileDetails[4].substring(fileDetails[4].indexOf("&nbsp&nbsp ") + 11));
long sizeDone = 0;
if (!fileDetails[2].contains("--")) {
double percentDone = Double.parseDouble(fileDetails[2].substring(0, fileDetails[2].indexOf("%")));
sizeDone = (long) ( size / 100.0 * percentDone);
sizeDone = (long) (size / 100.0 * percentDone);
}
// @formatter:off
torrentfiles.add(new TorrentFile(
hash,
fileDetails[3],
@ -670,40 +721,19 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -670,40 +721,19 @@ public class BitCometAdapter implements IDaemonAdapter {
size,
sizeDone,
convertPriority(fileDetails[1])));
// @formatter:on
}
}
catch (Exception e) {
} catch (Exception e) {
throw new DaemonException(ExceptionType.UnexpectedResponse, "Invalid BitComet HTTP response.");
}
// Return the list
return torrentfiles;
}
/**
* Returns the size of the torrent, as parsed form some string
* @param size The size in a string format, i.e. '691 MB'
* @return The size in bytes
*/
private static long convertSize(String size) {
try {
if (size.endsWith("GB")) {
return (long)(Float.parseFloat(size.substring(0, size.indexOf("GB"))) * 1024 * 1024 * 1024);
} else if (size.endsWith("MB")) {
return (long)(Float.parseFloat(size.substring(0, size.indexOf("MB"))) * 1024 * 1024);
} else if (size.endsWith("kB")) {
return (long)(Float.parseFloat(size.substring(0, size.indexOf("kB"))) * 1024);
} else if (size.endsWith("B")) {
return (long)(Float.parseFloat(size.substring(0, size.indexOf("B"))));
}
}
catch (Exception e) { }
return 0;
}
/**
* Parse BitComet torrent files priority
**/
*/
private Priority convertPriority(String priority) {
if (priority.equals("Very High") || priority.equals("High")) {
return Priority.High;
@ -712,10 +742,10 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -712,10 +742,10 @@ public class BitCometAdapter implements IDaemonAdapter {
}
return Priority.Off;
}
/**
* Parse BitComet torrent status
**/
* Parse BitComet torrent status
*/
private TorrentStatus convertStatus(String state) {
// Status is given as a descriptive string and an indication if the torrent was stopped/paused
if (state.equals("stopped")) {
@ -725,16 +755,7 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -725,16 +755,7 @@ public class BitCometAdapter implements IDaemonAdapter {
}
return TorrentStatus.Unknown;
}
/**
* Returns the part done (or progress) of a torrent, as parsed from some string
* @param progress The part done in a string format, i.e. '15.96'
* @return The part done as [0..1] fraction, i.e. 0.1596
*/
public static float convertProgress(String progress) {
return Float.parseFloat(progress) / 1000.0f;
}
@Override
public Daemon getType() {
return settings.getType();
@ -744,5 +765,5 @@ public class BitCometAdapter implements IDaemonAdapter { @@ -744,5 +765,5 @@ public class BitCometAdapter implements IDaemonAdapter {
public DaemonSettings getSettings() {
return this.settings;
}
}

111
app/src/main/java/org/transdroid/daemon/Bitflu/BitfluAdapter.java

@ -17,26 +17,22 @@ @@ -17,26 +17,22 @@
*/
package org.transdroid.daemon.Bitflu;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
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.AddByMagnetUrlTask;
import org.transdroid.daemon.task.AddByUrlTask;
import org.transdroid.daemon.task.DaemonTask;
@ -51,7 +47,11 @@ import org.transdroid.daemon.task.RemoveTask; @@ -51,7 +47,11 @@ import org.transdroid.daemon.task.RemoveTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.util.HttpHelper;
import org.transdroid.daemon.util.DLog;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
/**
* An adapter that allows for easy access to uTorrent torrent data. Communication is handled via authenticated JSON-RPC
@ -84,60 +84,61 @@ public class BitfluAdapter implements IDaemonAdapter { @@ -84,60 +84,61 @@ public class BitfluAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
JSONObject result = makeBitfluRequest(RPC_TORRENT_LIST);
return new RetrieveTaskSuccessResult((RetrieveTask) task,
parseJsonRetrieveTorrents(result.getJSONArray(JSON_ROOT)), null);
case GetStats:
return new GetStatsTaskSuccessResult((GetStatsTask) task, false, -1);
case Pause:
makeBitfluRequest(RPC_PAUSE_TORRENT + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case Resume:
makeBitfluRequest(RPC_RESUME_TORRENT + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
String removeUriBase = RPC_CANCEL_TORRENT;
if (removeTask.includingData()) {
removeUriBase = RPC_REMOVE_TORRENT;
}
DLog.d(LOG_NAME, "*** CALLING " + removeUriBase);
makeBitfluRequest(removeUriBase + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case GetFileList:
JSONObject jfiles = makeBitfluRequest(RPC_TORRENT_FILES + task.getTargetTorrent().getUniqueID());
return new GetFileListTaskSuccessResult((GetFileListTask) task,
parseJsonShowFilesTorrent(jfiles.getJSONArray(JSON_ROOT)));
case AddByUrl:
String url = URLEncoder.encode(((AddByUrlTask) task).getUrl(), "UTF-8");
makeBitfluRequest(RPC_START_DOWNLOAD + url);
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
String magnet = URLEncoder.encode(((AddByMagnetUrlTask) task).getUrl(), "UTF-8");
makeBitfluRequest(RPC_START_DOWNLOAD + magnet);
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
case Retrieve:
// Request all torrents from server
JSONObject result = makeBitfluRequest(log, RPC_TORRENT_LIST);
return new RetrieveTaskSuccessResult((RetrieveTask) task,
parseJsonRetrieveTorrents(result.getJSONArray(JSON_ROOT)), null);
case GetStats:
return new GetStatsTaskSuccessResult((GetStatsTask) task, false, -1);
case Pause:
makeBitfluRequest(log, RPC_PAUSE_TORRENT + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case Resume:
makeBitfluRequest(log, RPC_RESUME_TORRENT + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
String removeUriBase = RPC_CANCEL_TORRENT;
if (removeTask.includingData()) {
removeUriBase = RPC_REMOVE_TORRENT;
}
makeBitfluRequest(log, removeUriBase + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case GetFileList:
JSONObject jfiles =
makeBitfluRequest(log, RPC_TORRENT_FILES + task.getTargetTorrent().getUniqueID());
return new GetFileListTaskSuccessResult((GetFileListTask) task,
parseJsonShowFilesTorrent(jfiles.getJSONArray(JSON_ROOT)));
case AddByUrl:
String url = URLEncoder.encode(((AddByUrlTask) task).getUrl(), "UTF-8");
makeBitfluRequest(log, RPC_START_DOWNLOAD + url);
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
String magnet = URLEncoder.encode(((AddByMagnetUrlTask) task).getUrl(), "UTF-8");
makeBitfluRequest(log, RPC_START_DOWNLOAD + magnet);
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
} catch (DaemonException e) {
return new DaemonTaskFailureResult(task, e);
} catch (UnsupportedEncodingException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, e.toString()));
return new DaemonTaskFailureResult(task,
new DaemonException(ExceptionType.MethodUnsupported, e.toString()));
}
}
private JSONObject makeBitfluRequest(String addToUrl) throws DaemonException {
private JSONObject makeBitfluRequest(Log log, String addToUrl) throws DaemonException {
try {
@ -174,10 +175,10 @@ public class BitfluAdapter implements IDaemonAdapter { @@ -174,10 +175,10 @@ public class BitfluAdapter implements IDaemonAdapter {
} catch (DaemonException e) {
throw e;
} catch (JSONException e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ParsingFailed, e.toString());
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
@ -267,7 +268,6 @@ public class BitfluAdapter implements IDaemonAdapter { @@ -267,7 +268,6 @@ public class BitfluAdapter implements IDaemonAdapter {
/**
* Instantiates an HTTP client with proper credentials that can be used for all HTTP requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On conflicting or missing settings
*/
private void initialise() throws DaemonException {
@ -281,10 +281,11 @@ public class BitfluAdapter implements IDaemonAdapter { @@ -281,10 +281,11 @@ public class BitfluAdapter implements IDaemonAdapter {
*/
private String buildWebUIUrl() {
String webuiroot = "";
if (settings.getFolder() != null)
if (settings.getFolder() != null) {
webuiroot = settings.getFolder();
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort()
+ webuiroot + "/";
}
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() +
webuiroot + "/";
}
@Override

139
app/src/main/java/org/transdroid/daemon/BuffaloNas/BuffaloNasAdapter.java

@ -17,11 +17,9 @@ @@ -17,11 +17,9 @@
*/
package org.transdroid.daemon.BuffaloNas;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@ -34,15 +32,16 @@ import org.apache.http.message.BasicNameValuePair; @@ -34,15 +32,16 @@ import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
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;
@ -55,12 +54,13 @@ import org.transdroid.daemon.task.RemoveTask; @@ -55,12 +54,13 @@ import org.transdroid.daemon.task.RemoveTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
/**
* The daemon adapter for the Buffalo NAS' integrated torrent client.
@ -78,86 +78,88 @@ public class BuffaloNasAdapter implements IDaemonAdapter { @@ -78,86 +78,88 @@ public class BuffaloNasAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
case Retrieve:
// Request all torrents from server
JSONObject result = new JSONObject(makeRequest("/api/torrents-get"));
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null);
// Request all torrents from server
JSONObject result = new JSONObject(makeRequest(log, "/api/torrents-get"));
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null);
case GetFileList:
case GetFileList:
// Request files listing for a specific torrent
String fhash = ((GetFileListTask) task).getTargetTorrent().getUniqueID();
JSONObject files = new JSONObject(makeRequest("/api/torrent-get-files", new BasicNameValuePair("hash",
fhash)));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files, fhash));
// Request files listing for a specific torrent
String fhash = task.getTargetTorrent().getUniqueID();
JSONObject files = new JSONObject(
makeRequest(log, "/api/torrent-get-files", new BasicNameValuePair("hash", fhash)));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files, fhash));
case AddByFile:
case AddByFile:
// Upload a local .torrent file
String ufile = ((AddByFileTask) task).getFile();
makeUploadRequest("/api/torrent-add?start=yes", ufile);
return new DaemonTaskSuccessResult(task);
// Upload a local .torrent file
String ufile = ((AddByFileTask) task).getFile();
makeUploadRequest(log, "/api/torrent-add?start=yes", ufile);
return new DaemonTaskSuccessResult(task);
case AddByUrl:
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
// @formatter:off
makeRequest("/api/torrent-add",
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
// @formatter:off
makeRequest(log, "/api/torrent-add",
new BasicNameValuePair("url", url),
new BasicNameValuePair("start", "yes"));
// @formatter:on
return new DaemonTaskSuccessResult(task);
return new DaemonTaskSuccessResult(task);
case Remove:
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
// @formatter:off
makeRequest("/api/torrent-remove",
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
// @formatter:off
makeRequest(log, "/api/torrent-remove",
new BasicNameValuePair("hash", removeTask.getTargetTorrent().getUniqueID()),
new BasicNameValuePair("delete-torrent", "yes"),
new BasicNameValuePair("delete-data", (removeTask.includingData() ? "yes" : "no")));
// @formatter:on
return new DaemonTaskSuccessResult(task);
return new DaemonTaskSuccessResult(task);
case Pause:
case Pause:
// Pause a torrent
makeRequest("/api/torrent-stop", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
// Pause a torrent
makeRequest(log, "/api/torrent-stop",
new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case Resume:
case Resume:
// Resume a torrent
makeRequest("/api/torrent-start", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
// Resume a torrent
makeRequest(log, "/api/torrent-start",
new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
String dl = Integer.toString((ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate()
.intValue() * 1024));
String ul = Integer.toString((ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate()
.intValue() * 1024));
// @formatter:off
makeRequest("/api/app-settings-set",
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
String dl = Integer.toString(
(ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate() * 1024));
String ul = Integer.toString(
(ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate() * 1024));
// @formatter:off
makeRequest(log, "/api/app-settings-set",
new BasicNameValuePair("auto_bandwidth_management", "0"),
new BasicNameValuePair("max_dl_rate", dl),
new BasicNameValuePair("max_ul_rate", ul),
new BasicNameValuePair("max_ul_rate_seed", ul));
// @formatter:on
return new DaemonTaskSuccessResult(task);
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
@ -166,13 +168,13 @@ public class BuffaloNasAdapter implements IDaemonAdapter { @@ -166,13 +168,13 @@ public class BuffaloNasAdapter implements IDaemonAdapter {
}
}
private String makeRequest(String url, NameValuePair... params) throws DaemonException {
private String makeRequest(Log log, String url, NameValuePair... params) throws DaemonException {
try {
// Initialise the HTTP client
if (httpclient == null) {
initialise(HttpHelper.DEFAULT_CONNECTION_TIMEOUT);
initialise();
}
// Add the parameters to the query string
@ -201,31 +203,31 @@ public class BuffaloNasAdapter implements IDaemonAdapter { @@ -201,31 +203,31 @@ public class BuffaloNasAdapter implements IDaemonAdapter {
return result;
}
DLog.d(LOG_NAME, "Error: No entity in HTTP response");
log.d(LOG_NAME, "Error: No entity in HTTP response");
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response.");
} catch (UnsupportedEncodingException e) {
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private boolean makeUploadRequest(String path, String file) throws DaemonException {
private boolean makeUploadRequest(Log log, String path, String file) throws DaemonException {
try {
// Initialise the HTTP client
if (httpclient == null) {
initialise(HttpHelper.DEFAULT_CONNECTION_TIMEOUT);
initialise();
}
// Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path));
File upload = new File(URI.create(file));
Part[] parts = { new FilePart("fileEl", upload) };
Part[] parts = {new FilePart("fileEl", upload)};
httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
// Make the request
@ -235,7 +237,7 @@ public class BuffaloNasAdapter implements IDaemonAdapter { @@ -235,7 +237,7 @@ public class BuffaloNasAdapter implements IDaemonAdapter {
} catch (FileNotFoundException e) {
throw new DaemonException(ExceptionType.FileAccessError, e.toString());
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
@ -243,10 +245,9 @@ public class BuffaloNasAdapter implements IDaemonAdapter { @@ -243,10 +245,9 @@ public class BuffaloNasAdapter implements IDaemonAdapter {
/**
* Instantiates an HTTP client with proper credentials that can be used for all Buffalo NAS requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On conflicting or missing settings
*/
private void initialise(int connectionTimeout) throws DaemonException {
private void initialise() throws DaemonException {
httpclient = HttpHelper.createStandardHttpClient(settings, true);

199
app/src/main/java/org/transdroid/daemon/DLinkRouterBT/DLinkRouterBTAdapter.java

@ -17,9 +17,9 @@ @@ -17,9 +17,9 @@
*/
package org.transdroid.daemon.DLinkRouterBT;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@ -28,15 +28,16 @@ import org.apache.http.impl.client.DefaultHttpClient; @@ -28,15 +28,16 @@ import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
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;
@ -50,17 +51,15 @@ import org.transdroid.daemon.task.RemoveTask; @@ -50,17 +51,15 @@ import org.transdroid.daemon.task.RemoveTask;
import org.transdroid.daemon.task.ResumeTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
/**
* The daemon adapter for the DLink Router Bittorrent client.
*
* @author AvengerMoJo <avengermojo at gmail.com>
*
*/
public class DLinkRouterBTAdapter implements IDaemonAdapter {
@ -115,54 +114,54 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -115,54 +114,54 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
case Retrieve:
// Request all torrents from server
JSONObject result = makeRequest(API_GET);
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonRetrieveTorrents(result),null);
// Request all torrents from server
JSONObject result = makeRequest(log, API_GET);
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonRetrieveTorrents(result), null);
case GetFileList:
case GetFileList:
// Request all details for a specific torrent
JSONObject result2 = makeRequest(API_GET_FILES + task.getTargetTorrent().getUniqueID());
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFileList(result2, task
.getTargetTorrent().getUniqueID()));
// Request all details for a specific torrent
JSONObject result2 = makeRequest(log, API_GET_FILES + task.getTargetTorrent().getUniqueID());
return new GetFileListTaskSuccessResult((GetFileListTask) task,
parseJsonFileList(result2, task.getTargetTorrent().getUniqueID()));
case AddByFile:
case AddByFile:
// Add a torrent to the server by sending the contents of a local .torrent file
String file = ((AddByFileTask) task).getFile();
// Add a torrent to the server by sending the contents of a local .torrent file
String file = ((AddByFileTask) task).getFile();
// put .torrent file's data into the request
makeRequest(API_ADD_BY_FILE, new File(URI.create(file)));
return new DaemonTaskSuccessResult(task);
// put .torrent file's data into the request
makeRequest(log, API_ADD_BY_FILE, new File(URI.create(file)));
return new DaemonTaskSuccessResult(task);
case AddByUrl:
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeRequest(API_ADD + url);
return new DaemonTaskSuccessResult(task);
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeRequest(log, API_ADD + url);
return new DaemonTaskSuccessResult(task);
case Remove:
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(API_REMOVE + removeTask.getTargetTorrent().getUniqueID()
+ (removeTask.includingData() ? API_DEL_DATA + "yes" : ""), false);
return new DaemonTaskSuccessResult(task);
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(log, API_REMOVE + removeTask.getTargetTorrent().getUniqueID() +
(removeTask.includingData() ? API_DEL_DATA + "yes" : ""), false);
return new DaemonTaskSuccessResult(task);
// case Stop:
case Pause:
case Pause:
// Pause a torrent
PauseTask pauseTask = (PauseTask) task;
makeRequest(API_STOP + pauseTask.getTargetTorrent().getUniqueID(), false);
return new DaemonTaskSuccessResult(task);
// Pause a torrent
PauseTask pauseTask = (PauseTask) task;
makeRequest(log, API_STOP + pauseTask.getTargetTorrent().getUniqueID(), false);
return new DaemonTaskSuccessResult(task);
// case PauseAll:
@ -172,12 +171,12 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -172,12 +171,12 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
// return new DaemonTaskSuccessResult(task);
// case Start:
case Resume:
case Resume:
// Resume a torrent
ResumeTask resumeTask = (ResumeTask) task;
makeRequest(API_START + resumeTask.getTargetTorrent().getUniqueID(), false);
return new DaemonTaskSuccessResult(task);
// Resume a torrent
ResumeTask resumeTask = (ResumeTask) task;
makeRequest(log, API_START + resumeTask.getTargetTorrent().getUniqueID(), false);
return new DaemonTaskSuccessResult(task);
// case ResumeAll:
@ -206,10 +205,9 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -206,10 +205,9 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
// makeRequest( RPC_METHOD_SESSIONSET );
// return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, task
.getMethod()
+ " is not supported by " + getType()));
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
@ -218,31 +216,31 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -218,31 +216,31 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
}
}
private JSONObject makeRequest(String requestUrl, File upload) throws DaemonException {
return makeRequest(requestUrl, false, upload);
private JSONObject makeRequest(Log log, String requestUrl, File upload) throws DaemonException {
return makeRequest(log, requestUrl, false, upload);
}
private JSONObject makeRequest(String requestUrl) throws DaemonException {
return makeRequest(requestUrl, true, null);
private JSONObject makeRequest(Log log, String requestUrl) throws DaemonException {
return makeRequest(log, requestUrl, true, null);
}
private JSONObject makeRequest(String requestUrl, boolean hasRespond) throws DaemonException {
return makeRequest(requestUrl, hasRespond, null);
private JSONObject makeRequest(Log log, String requestUrl, boolean hasRespond) throws DaemonException {
return makeRequest(log, requestUrl, hasRespond, null);
}
private JSONObject makeRequest(String requestUrl, boolean hasRespond, File upload) throws DaemonException {
private JSONObject makeRequest(Log log, String requestUrl, boolean hasRespond, File upload) throws DaemonException {
try {
// Initialise the HTTP client
if (httpclient == null) {
initialise(HttpHelper.DEFAULT_CONNECTION_TIMEOUT);
initialise();
}
// Setup request using POST stream with URL and data
HttpPost httppost = new HttpPost(buildWebUIUrl() + requestUrl);
if (upload != null) {
Part[] parts = { new FilePart(BT_ADD_BY_FILE, upload) };
Part[] parts = {new FilePart(BT_ADD_BY_FILE, upload)};
httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
}
@ -264,8 +262,9 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -264,8 +262,9 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
response = httpclient.execute(httppost);
}
if (!hasRespond)
if (!hasRespond) {
return null;
}
HttpEntity entity = response.getEntity();
if (entity != null) {
@ -276,24 +275,24 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -276,24 +275,24 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
JSONObject json = new JSONObject(result);
instream.close();
DLog.d(LOG_NAME, "Success: "
+ (result.length() > 300 ? result.substring(0, 300) + "... (" + result.length() + " chars)"
: result));
log.d(LOG_NAME, "Success: " +
(result.length() > 300 ? result.substring(0, 300) + "... (" + result.length() + " chars)" :
result));
// Return the JSON object
return json;
}
DLog.d(LOG_NAME, "Error: No entity in HTTP response");
log.d(LOG_NAME, "Error: No entity in HTTP response");
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response.");
} catch (DaemonException e) {
throw e;
} catch (JSONException e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.UnexpectedResponse, e.toString());
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
@ -301,39 +300,40 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -301,39 +300,40 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
/**
* Instantiates an HTTP client with proper credentials that can be used for all Transmission requests.
*
* @param connectionTimeout
* The connection timeout in milliseconds
* @throws DaemonException
* On conflicting or missing settings
* @throws DaemonException On conflicting or missing settings
*/
private void initialise(int connectionTimeout) throws DaemonException {
private void initialise() throws DaemonException {
httpclient = HttpHelper.createStandardHttpClient(settings, true);
}
/**
* Build the URL of the Transmission web UI from the user settings.
*
* @return The URL of the RPC API
*/
private String buildWebUIUrl() {
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort()
+ PATH_TO_API;
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() +
PATH_TO_API;
}
private TorrentStatus convertStatus(String state) {
if ("allocating".equals(state))
if ("allocating".equals(state)) {
return TorrentStatus.Checking;
if ("seeding".equals(state))
}
if ("seeding".equals(state)) {
return TorrentStatus.Seeding;
if ("finished".equals(state))
}
if ("finished".equals(state)) {
return TorrentStatus.Downloading;
if ("connecting_to_tracker".equals(state))
}
if ("connecting_to_tracker".equals(state)) {
return TorrentStatus.Checking;
if ("queued_for_checking".equals(state))
}
if ("queued_for_checking".equals(state)) {
return TorrentStatus.Queued;
if ("downloading".equals(state))
}
if ("downloading".equals(state)) {
return TorrentStatus.Downloading;
}
return TorrentStatus.Unknown;
}
@ -345,15 +345,18 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -345,15 +345,18 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
for (int i = 0; i < rarray.length(); i++) {
JSONObject tor = rarray.getJSONObject(i);
// Add the parsed torrent to the list
TorrentStatus status = TorrentStatus.Unknown;
if (tor.getInt(BT_STOPPED) == 1)
TorrentStatus status;
if (tor.getInt(BT_STOPPED) == 1) {
status = TorrentStatus.Paused;
else
} else {
status = convertStatus(tor.getString(BT_STATE));
int eta = (int) (tor.getLong(BT_SIZE) / (tor.getInt(BT_DOWNLOAD_RATE) + 1));
if (0 > eta)
}
int eta = (int) ((tor.getLong(BT_SIZE) - tor.getLong(BT_DONE)) / (tor.getInt(BT_DOWNLOAD_RATE) + 1));
if (0 > eta) {
eta = -1;
}
// @formatter:off
Torrent new_t = new Torrent(
i,
tor.getString(BT_HASH),
@ -366,17 +369,18 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -366,17 +369,18 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
tor.getInt(BT_PEERS_TOTAL),
tor.getInt(BT_SEEDS_CONNECTED),
tor.getInt(BT_SEEDS_TOTAL),
(int) ((tor.getLong(BT_SIZE) - tor.getLong(BT_DONE)) / (tor.getInt(BT_DOWNLOAD_RATE) + 1)),
eta,
tor.getLong(BT_DONE),
tor.getLong(BT_PAYLOAD_UPLOAD),
tor.getLong(BT_SIZE),
(float) (tor.getLong(BT_DONE) / (float) tor.getLong(BT_SIZE)),
tor.getLong(BT_DONE) / (float) tor.getLong(BT_SIZE),
Float.parseFloat(tor.getString(BT_COPYS)),
null,
null,
null,
null,
settings.getType());
// @formatter:on
torrents.add(new_t);
}
@ -395,15 +399,16 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -395,15 +399,16 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
JSONArray files = jobj.getJSONArray(hash); // "Hash id"
for (int i = 0; i < files.length(); i++) {
JSONObject file = files.getJSONObject(i);
// @formatter:off
torrentfiles.add(new TorrentFile(
i + "",
// TODO: How is an individual file identified? Index in the array?
String.valueOf(i),
file.getString(BT_FILE_NAME),
file.getString(BT_FILE_NAME),
null, // Not supported?
file.getLong(BT_FILE_SIZE),
file.getLong(BT_FILE_DONE),
convertTransmissionPriority(file.getInt(BT_FILE_PRIORITY))));
// @formatter:on
}
}
@ -414,12 +419,12 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter { @@ -414,12 +419,12 @@ public class DLinkRouterBTAdapter implements IDaemonAdapter {
private Priority convertTransmissionPriority(int priority) {
switch (priority) {
case 1:
return Priority.High;
case -1:
return Priority.Low;
default:
return Priority.Normal;
case 1:
return Priority.High;
case -1:
return Priority.Low;
default:
return Priority.Normal;
}
}

4
app/src/main/java/org/transdroid/daemon/Daemon.java

@ -306,7 +306,7 @@ public enum Daemon { @@ -306,7 +306,7 @@ public enum Daemon {
}
public static boolean supportsSetTransferRates(Daemon type) {
return type == Deluge || type == Transmission || type == uTorrent || type == BitTorrent || type == Deluge || type == rTorrent || type == Vuze || type == BuffaloNas || type == BitComet || type == Aria2 || type == Dummy;
return type == Deluge || type == Transmission || type == uTorrent || type == BitTorrent || type == rTorrent || type == Vuze || type == BuffaloNas || type == BitComet || type == Aria2 || type == Dummy;
}
public static boolean supportsAddByFile(Daemon type) {
@ -351,7 +351,7 @@ public enum Daemon { @@ -351,7 +351,7 @@ public enum Daemon {
}
public static boolean supportsForceRecheck(Daemon type) {
return type == uTorrent || type == BitTorrent || type == Deluge || type == rTorrent || type == Dummy;
return type == uTorrent || type == BitTorrent || type == Deluge || type == rTorrent || type == Transmission || type == Dummy;
}
public static boolean supportsExtraPassword(Daemon type) {

636
app/src/main/java/org/transdroid/daemon/Deluge/DelugeAdapter.java

@ -17,19 +17,12 @@ @@ -17,19 +17,12 @@
*/
package org.transdroid.daemon.Deluge;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
@ -38,6 +31,7 @@ import org.apache.http.impl.client.DefaultHttpClient; @@ -38,6 +31,7 @@ import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
@ -70,18 +64,21 @@ import org.transdroid.daemon.task.SetFilePriorityTask; @@ -70,18 +64,21 @@ import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetLabelTask;
import org.transdroid.daemon.task.SetTrackersTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* The daemon adapter from the Deluge torrent client.
*
* @author erickok
*
*/
public class DelugeAdapter implements IDaemonAdapter {
@ -89,7 +86,7 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -89,7 +86,7 @@ public class DelugeAdapter implements IDaemonAdapter {
private static final String PATH_TO_RPC = "/json";
private static final String PATH_TO_UPLOAD = "/upload";
private static final String RPC_ID = "id";
private static final String RPC_METHOD = "method";
private static final String RPC_PARAMS = "params";
@ -141,34 +138,30 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -141,34 +138,30 @@ public class DelugeAdapter implements IDaemonAdapter {
private static final String RPC_LABEL = "label";
private static final String RPC_TRACKERS = "trackers";
private static final String RPC_TRACKER_STATUS = "tracker_status";
private static final String[] RPC_FIELDS_ARRAY =
new String[]{RPC_NAME, RPC_STATUS, RPC_SAVEPATH, RPC_RATEDOWNLOAD, RPC_RATEUPLOAD, RPC_NUMPEERS,
RPC_NUMSEEDS, RPC_TOTALPEERS, RPC_TOTALSEEDS, RPC_ETA, RPC_DOWNLOADEDEVER, RPC_UPLOADEDEVER,
RPC_TOTALSIZE, RPC_PARTDONE, RPC_LABEL, RPC_MESSAGE, RPC_TIMEADDED, RPC_TRACKER_STATUS};
private static final String RPC_DETAILS = "files";
private static final String RPC_INDEX = "index";
private static final String RPC_PATH = "path";
private static final String RPC_SIZE = "size";
private static final String RPC_FILEPROGRESS = "file_progress";
private static final String RPC_FILEPRIORITIES = "file_priorities";
private static final String[] RPC_FIELDS_ARRAY = new String[] {
RPC_NAME, RPC_STATUS, RPC_SAVEPATH, RPC_RATEDOWNLOAD, RPC_RATEUPLOAD,
RPC_NUMPEERS, RPC_NUMSEEDS, RPC_TOTALPEERS,
RPC_TOTALSEEDS, RPC_ETA, RPC_DOWNLOADEDEVER, RPC_UPLOADEDEVER,
RPC_TOTALSIZE, RPC_PARTDONE, RPC_LABEL, RPC_MESSAGE, RPC_TIMEADDED, RPC_TRACKER_STATUS };
private DaemonSettings settings;
private DefaultHttpClient httpclient;
private Cookie sessionCookie;
private int version = -1;
private int version = -1;
public DelugeAdapter(DaemonSettings settings) {
this.settings = settings;
}
public JSONArray AddTorrentByFile(String file) throws JSONException, ClientProtocolException, IOException, DaemonException {
public JSONArray addTorrentByFile(String file, Log log) throws JSONException, IOException, DaemonException {
String url = buildWebUIUrl() + PATH_TO_UPLOAD;
DLog.d(LOG_NAME, "Uploading a file to the Deluge daemon: " + url);
log.d(LOG_NAME, "Uploading a file to the Deluge daemon: " + url);
// Initialise the HTTP client
if (httpclient == null) {
@ -178,7 +171,7 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -178,7 +171,7 @@ public class DelugeAdapter implements IDaemonAdapter {
// Setup client using POST
HttpPost httppost = new HttpPost(url);
File upload = new File(URI.create(file));
Part[] parts = { new FilePart(RPC_FILE, upload) };
Part[] parts = {new FilePart(RPC_FILE, upload)};
httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
// Make request
@ -199,17 +192,17 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -199,17 +192,17 @@ public class DelugeAdapter implements IDaemonAdapter {
fileu.put("options", new JSONArray());
files.put(fileu);
params.put(files);
return params;
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
ensureVersion();
ensureVersion(log);
JSONArray params = new JSONArray();
// Array of the fields needed for files listing calls
@ -217,202 +210,209 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -217,202 +210,209 @@ public class DelugeAdapter implements IDaemonAdapter {
ffields.put(RPC_DETAILS);
ffields.put(RPC_FILEPROGRESS);
ffields.put(RPC_FILEPRIORITIES);
switch (task.getMethod()) {
case Retrieve:
case Retrieve:
// Request all torrents from server
JSONArray fields = new JSONArray();
for (String field : RPC_FIELDS_ARRAY) {
fields.put(field);
}
params.put(fields); // keys
params.put(new JSONArray()); // filter_dict
// params.put(-1); // cache_id
JSONObject result = makeRequest(buildRequest(RPC_METHOD_GET, params));
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonRetrieveTorrents(result.getJSONObject(RPC_RESULT)), parseJsonRetrieveLabels(result.getJSONObject(RPC_RESULT)));
case GetTorrentDetails:
// Array of the fields needed for files listing calls
JSONArray dfields = new JSONArray();
dfields.put(RPC_TRACKERS);
dfields.put(RPC_TRACKER_STATUS);
// Request file listing of a torrent
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
params.put(dfields); // keys
JSONObject dinfo = makeRequest(buildRequest(RPC_METHOD_STATUS, params));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, parseJsonTorrentDetails(dinfo.getJSONObject(RPC_RESULT)));
case GetFileList:
// Request file listing of a torrent
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
params.put(ffields); // keys
JSONObject finfo = makeRequest(buildRequest(RPC_METHOD_STATUS, params));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFileListing(finfo.getJSONObject(RPC_RESULT), task.getTargetTorrent()));
case AddByFile:
// Request to add a torrent by local .torrent file
String file = ((AddByFileTask)task).getFile();
makeRequest(buildRequest(RPC_METHOD_ADD_FILE, AddTorrentByFile(file)));
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask)task).getUrl();
params.put(url);
params.put(new JSONArray());
makeRequest(buildRequest(RPC_METHOD_ADD, params));
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask)task).getUrl();
// Deluge doesn't support (fully) application/x-www-form-urlencoded magnet links
magnet = URLDecoder.decode(magnet, "UTF-8");
params.put(magnet);
params.put(new JSONArray());
makeRequest(buildRequest(RPC_METHOD_ADD_MAGNET, params));
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
params.put(removeTask.getTargetTorrent().getUniqueID());
params.put(removeTask.includingData());
makeRequest(buildRequest(RPC_METHOD_REMOVE, params));
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
PauseTask pauseTask = (PauseTask) task;
makeRequest(buildRequest(RPC_METHOD_PAUSE,
((new JSONArray()).put((new JSONArray()).put(pauseTask.getTargetTorrent().getUniqueID())))));
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Resume all torrents
makeRequest(buildRequest(RPC_METHOD_PAUSE_ALL, null));
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
ResumeTask resumeTask = (ResumeTask) task;
makeRequest(buildRequest(RPC_METHOD_RESUME,
((new JSONArray()).put((new JSONArray()).put(resumeTask.getTargetTorrent().getUniqueID())))));
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeRequest(buildRequest(RPC_METHOD_RESUME_ALL, null));
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
// Set the priorities of files in a specific torrent
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
// We first need a listing of all the files (because we can only set the priorities all at once)
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
params.put(ffields); // keys
JSONObject pinfo = makeRequest(buildRequest(RPC_METHOD_STATUS, params));
ArrayList<TorrentFile> pfiles = parseJsonFileListing(pinfo.getJSONObject(RPC_RESULT), prioTask.getTargetTorrent());
// Now prepare the new list of priorities
params = new JSONArray();
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
JSONArray pfields = new JSONArray();
// Override the priorities in the just retrieved list of all files
for (TorrentFile pfile : pfiles) {
Priority newPriority = pfile.getPriority();
for (TorrentFile forFile : prioTask.getForFiles()) {
if (forFile.getKey().equals(pfile.getKey())) {
// This is a file that we want to assign a new priority to
newPriority = prioTask.getNewPriority();
break;
// Request all torrents from server
JSONArray fields = new JSONArray();
for (String field : RPC_FIELDS_ARRAY) {
fields.put(field);
}
params.put(fields); // keys
params.put(new JSONArray()); // filter_dict
// params.put(-1); // cache_id
JSONObject result = makeRequest(buildRequest(RPC_METHOD_GET, params), log);
return new RetrieveTaskSuccessResult((RetrieveTask) task,
parseJsonRetrieveTorrents(result.getJSONObject(RPC_RESULT)),
parseJsonRetrieveLabels(result.getJSONObject(RPC_RESULT)));
case GetTorrentDetails:
// Array of the fields needed for files listing calls
JSONArray dfields = new JSONArray();
dfields.put(RPC_TRACKERS);
dfields.put(RPC_TRACKER_STATUS);
// Request file listing of a torrent
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
params.put(dfields); // keys
JSONObject dinfo = makeRequest(buildRequest(RPC_METHOD_STATUS, params), log);
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
parseJsonTorrentDetails(dinfo.getJSONObject(RPC_RESULT)));
case GetFileList:
// Request file listing of a torrent
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
params.put(ffields); // keys
JSONObject finfo = makeRequest(buildRequest(RPC_METHOD_STATUS, params), log);
return new GetFileListTaskSuccessResult((GetFileListTask) task,
parseJsonFileListing(finfo.getJSONObject(RPC_RESULT), task.getTargetTorrent()));
case AddByFile:
// Request to add a torrent by local .torrent file
String file = ((AddByFileTask) task).getFile();
makeRequest(buildRequest(RPC_METHOD_ADD_FILE, addTorrentByFile(file, log)), log);
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
params.put(url);
params.put(new JSONArray());
makeRequest(buildRequest(RPC_METHOD_ADD, params), log);
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
// Deluge doesn't support (fully) application/x-www-form-urlencoded magnet links
magnet = URLDecoder.decode(magnet, "UTF-8");
params.put(magnet);
params.put(new JSONArray());
makeRequest(buildRequest(RPC_METHOD_ADD_MAGNET, params), log);
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
params.put(removeTask.getTargetTorrent().getUniqueID());
params.put(removeTask.includingData());
makeRequest(buildRequest(RPC_METHOD_REMOVE, params), log);
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
PauseTask pauseTask = (PauseTask) task;
makeRequest(buildRequest(RPC_METHOD_PAUSE, ((new JSONArray())
.put((new JSONArray()).put(pauseTask.getTargetTorrent().getUniqueID())))), log);
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Resume all torrents
makeRequest(buildRequest(RPC_METHOD_PAUSE_ALL, null), log);
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
ResumeTask resumeTask = (ResumeTask) task;
makeRequest(buildRequest(RPC_METHOD_RESUME, ((new JSONArray())
.put((new JSONArray()).put(resumeTask.getTargetTorrent().getUniqueID())))), log);
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeRequest(buildRequest(RPC_METHOD_RESUME_ALL, null), log);
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
// Set the priorities of files in a specific torrent
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
// We first need a listing of all the files (because we can only set the priorities all at once)
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
params.put(ffields); // keys
JSONObject pinfo = makeRequest(buildRequest(RPC_METHOD_STATUS, params), log);
ArrayList<TorrentFile> pfiles =
parseJsonFileListing(pinfo.getJSONObject(RPC_RESULT), prioTask.getTargetTorrent());
// Now prepare the new list of priorities
params = new JSONArray();
params.put(task.getTargetTorrent().getUniqueID()); // torrent_id
JSONArray pfields = new JSONArray();
// Override the priorities in the just retrieved list of all files
for (TorrentFile pfile : pfiles) {
Priority newPriority = pfile.getPriority();
for (TorrentFile forFile : prioTask.getForFiles()) {
if (forFile.getKey().equals(pfile.getKey())) {
// This is a file that we want to assign a new priority to
newPriority = prioTask.getNewPriority();
break;
}
}
pfields.put(convertPriority(newPriority));
}
pfields.put(convertPriority(newPriority));
}
params.put(pfields); // keys
// Make a single call to set the priorities on all files at once
makeRequest(buildRequest(RPC_METHOD_SETFILE, params));
return new DaemonTaskSuccessResult(task);
case SetDownloadLocation:
// Set the download location of some torrent
SetDownloadLocationTask sdlTask = (SetDownloadLocationTask) task;
// This works, but does not move the torrent
//makeRequest(buildRequest(RPC_METHOD_SETOPTIONS, buildSetTorrentOptions(
// sdlTask.getTargetTorrent().getUniqueID(), RPC_DOWNLOADLOCATION, sdlTask.getNewLocation())));
params.put(new JSONArray().put(task.getTargetTorrent().getUniqueID()));
params.put(sdlTask.getNewLocation());
makeRequest(buildRequest(RPC_METHOD_MOVESTORAGE, params));
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
JSONObject map = new JSONObject();
map.put(RPC_MAXUPLOAD, (ratesTask.getUploadRate() == null? -1: ratesTask.getUploadRate().intValue()));
map.put(RPC_MAXDOWNLOAD, (ratesTask.getDownloadRate() == null? -1: ratesTask.getDownloadRate().intValue()));
makeRequest(buildRequest(RPC_METHOD_SETCONFIG, (new JSONArray()).put(map)));
return new DaemonTaskSuccessResult(task);
case SetLabel:
// Request to set the label
SetLabelTask labelTask = (SetLabelTask) task;
params.put(task.getTargetTorrent().getUniqueID());
params.put(labelTask.getNewLabel() == null ? "" : labelTask.getNewLabel());
makeRequest(buildRequest(RPC_METHOD_SETLABEL, params));
return new DaemonTaskSuccessResult(task);
case SetTrackers:
// Set the trackers of some torrent
SetTrackersTask trackersTask = (SetTrackersTask) task;
JSONArray trackers = new JSONArray();
// Build an JSON arrays of objcts that each have a tier (order) number and an url
for (int i = 0; i < trackersTask.getNewTrackers().size(); i++) {
JSONObject trackerObj = new JSONObject();
trackerObj.put("tier", i);
trackerObj.put("url", trackersTask.getNewTrackers().get(i));
trackers.put(trackerObj);
}
params.put(new JSONArray().put(task.getTargetTorrent().getUniqueID()));
params.put(trackers);
makeRequest(buildRequest(RPC_METHOD_SETTRACKERS, params));
return new DaemonTaskSuccessResult(task);
case ForceRecheck:
// Pause a torrent
makeRequest(buildRequest(RPC_METHOD_FORCERECHECK,
((new JSONArray()).put((new JSONArray()).put(task.getTargetTorrent().getUniqueID())))));
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, task.getMethod() + " is not supported by " + getType()));
params.put(pfields); // keys
// Make a single call to set the priorities on all files at once
makeRequest(buildRequest(RPC_METHOD_SETFILE, params), log);
return new DaemonTaskSuccessResult(task);
case SetDownloadLocation:
// Set the download location of some torrent
SetDownloadLocationTask sdlTask = (SetDownloadLocationTask) task;
// This works, but does not move the torrent
//makeRequest(buildRequest(RPC_METHOD_SETOPTIONS, buildSetTorrentOptions(
// sdlTask.getTargetTorrent().getUniqueID(), RPC_DOWNLOADLOCATION, sdlTask.getNewLocation())));
params.put(new JSONArray().put(task.getTargetTorrent().getUniqueID()));
params.put(sdlTask.getNewLocation());
makeRequest(buildRequest(RPC_METHOD_MOVESTORAGE, params), log);
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
JSONObject map = new JSONObject();
map.put(RPC_MAXUPLOAD, (ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate()));
map.put(RPC_MAXDOWNLOAD, (ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate()));
makeRequest(buildRequest(RPC_METHOD_SETCONFIG, (new JSONArray()).put(map)), log);
return new DaemonTaskSuccessResult(task);
case SetLabel:
// Request to set the label
SetLabelTask labelTask = (SetLabelTask) task;
params.put(task.getTargetTorrent().getUniqueID());
params.put(labelTask.getNewLabel() == null ? "" : labelTask.getNewLabel());
makeRequest(buildRequest(RPC_METHOD_SETLABEL, params), log);
return new DaemonTaskSuccessResult(task);
case SetTrackers:
// Set the trackers of some torrent
SetTrackersTask trackersTask = (SetTrackersTask) task;
JSONArray trackers = new JSONArray();
// Build an JSON arrays of objcts that each have a tier (order) number and an url
for (int i = 0; i < trackersTask.getNewTrackers().size(); i++) {
JSONObject trackerObj = new JSONObject();
trackerObj.put("tier", i);
trackerObj.put("url", trackersTask.getNewTrackers().get(i));
trackers.put(trackerObj);
}
params.put(new JSONArray().put(task.getTargetTorrent().getUniqueID()));
params.put(trackers);
makeRequest(buildRequest(RPC_METHOD_SETTRACKERS, params), log);
return new DaemonTaskSuccessResult(task);
case ForceRecheck:
// Pause a torrent
makeRequest(buildRequest(RPC_METHOD_FORCERECHECK,
((new JSONArray()).put((new JSONArray()).put(task.getTargetTorrent().getUniqueID())))),
log);
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
@ -435,9 +435,10 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -435,9 +435,10 @@ public class DelugeAdapter implements IDaemonAdapter {
return params;
}*/
private void ensureVersion() throws DaemonException {
if (version > 0)
private void ensureVersion(Log log) throws DaemonException {
if (version > 0) {
return;
}
// We still need to retrieve the version number from the server
// Do this by getting the web interface main html page and trying to parse the version number
// Format is something like '<title>Deluge: Web UI 1.3.6</title>'
@ -464,9 +465,10 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -464,9 +465,10 @@ public class DelugeAdapter implements IDaemonAdapter {
String numbers = "";
for (char c : parts[2].toCharArray()) {
if (Character.isDigit(c))
// Still a number; add it to the numbers string
// Still a number; add it to the numbers string
{
numbers += Character.toString(c);
else {
} else {
// No longer reading numbers; stop reading
break;
}
@ -478,10 +480,10 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -478,10 +480,10 @@ public class DelugeAdapter implements IDaemonAdapter {
}
}
} catch (NumberFormatException e) {
DLog.d(LOG_NAME, "Error parsing the Deluge version code as number: " + e.toString());
log.d(LOG_NAME, "Error parsing the Deluge version code as number: " + e.toString());
// Continue though, ignoring the version number
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
// Unable to establish version number; assume an old version by setting it to version 1
@ -496,21 +498,21 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -496,21 +498,21 @@ public class DelugeAdapter implements IDaemonAdapter {
request.put(RPC_PARAMS, (params == null) ? new JSONArray() : params);
request.put(RPC_ID, 2);
return request;
}
private synchronized JSONObject makeRequest(JSONObject data) throws DaemonException {
private synchronized JSONObject makeRequest(JSONObject data, Log log) throws DaemonException {
try {
// Initialise the HTTP client
if (httpclient == null) {
initialise();
}
// Login first?
if (sessionCookie == null) {
// Build login object
String extraPass = settings.getExtraPassword();
if (extraPass == null) {
@ -539,11 +541,13 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -539,11 +541,13 @@ public class DelugeAdapter implements IDaemonAdapter {
}
}
}
// Still no session cookie?
if (sessionCookie == null) {
// Set error message and cancel the action that was requested
throw new DaemonException(ExceptionType.AuthenticationFailure, "Password error? Server time difference? No (valid) cookie in response and JSON was: " + HttpHelper.convertStreamToString(instream));
throw new DaemonException(ExceptionType.AuthenticationFailure,
"Password error? Server time difference? No (valid) cookie in response and JSON was: " +
HttpHelper.convertStreamToString(instream));
}
}
@ -566,7 +570,7 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -566,7 +570,7 @@ public class DelugeAdapter implements IDaemonAdapter {
if (!cookiePresent) {
httpclient.getCookieStore().addCookie(sessionCookie);
}
// Execute
HttpResponse response = httpclient.execute(httppost);
@ -579,8 +583,10 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -579,8 +583,10 @@ public class DelugeAdapter implements IDaemonAdapter {
JSONObject json = new JSONObject(result);
instream.close();
DLog.d(LOG_NAME, "Success: " + (result.length() > 300? result.substring(0, 300) + "... (" + result.length() + " chars)": result));
log.d(LOG_NAME, "Success: " +
(result.length() > 300 ? result.substring(0, 300) + "... (" + result.length() + " chars)" :
result));
// Return JSON object
return json;
@ -590,34 +596,35 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -590,34 +596,35 @@ public class DelugeAdapter implements IDaemonAdapter {
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity in response object.");
} catch (JSONException e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.UnexpectedResponse, e.toString());
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
/**
* Instantiates an HTTP client with proper credentials that can be used for all Transmission requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On missing settings
*/
private void initialise() throws DaemonException {
httpclient = HttpHelper.createStandardHttpClient(settings, settings.getUsername() != null && settings.getUsername() != "");
httpclient.addRequestInterceptor(HttpHelper.gzipRequestInterceptor);
httpclient.addResponseInterceptor(HttpHelper.gzipResponseInterceptor);
httpclient = HttpHelper.createStandardHttpClient(settings,
settings.getUsername() != null && !settings.getUsername().equals(""));
httpclient.addRequestInterceptor(HttpHelper.gzipRequestInterceptor);
httpclient.addResponseInterceptor(HttpHelper.gzipResponseInterceptor);
}
/**
/**
* Build the URL of the Transmission web UI from the user settings.
* @return The URL of the RPC API
*/
private String buildWebUIUrl() {
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() + (settings.getFolder() == null? "": settings.getFolder());
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() +
(settings.getFolder() == null ? "" : settings.getFolder());
}
private ArrayList<Torrent> parseJsonRetrieveTorrents(JSONObject response) throws JSONException, DaemonException {
@ -625,43 +632,47 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -625,43 +632,47 @@ public class DelugeAdapter implements IDaemonAdapter {
// Parse response
ArrayList<Torrent> torrents = new ArrayList<Torrent>();
if (response.isNull(RPC_TORRENTS)) {
throw new DaemonException(ExceptionType.NotConnected, "Web interface probably not connected to a daemon yet, because 'torrents' is null: " + response.toString());
throw new DaemonException(ExceptionType.NotConnected,
"Web interface probably not connected to a daemon yet, because 'torrents' is null: " +
response.toString());
}
JSONObject objects = response.getJSONObject(RPC_TORRENTS);
JSONArray names = objects.names();
if (names != null) {
for (int j = 0; j < names.length(); j++) {
JSONObject tor = objects.getJSONObject(names.getString(j));
// Add the parsed torrent to the list
TorrentStatus status = convertDelugeState(tor.getString(RPC_STATUS));
String error = tor.getString(RPC_MESSAGE);
if (tor.getString(RPC_TRACKER_STATUS).indexOf("Error") > 0) {
error += (error.length() > 0? "\n": "") + tor.getString(RPC_TRACKER_STATUS);
error += (error.length() > 0 ? "\n" : "") + tor.getString(RPC_TRACKER_STATUS);
//status = TorrentStatus.Error; // Don't report this as blocking error
}
torrents.add(new Torrent(j,
names.getString(j),
tor.getString(RPC_NAME),
// @formatter:off
torrents.add(new Torrent(j,
names.getString(j),
tor.getString(RPC_NAME),
status,
tor.getString(RPC_SAVEPATH) + settings.getOS().getPathSeperator(),
tor.getInt(RPC_RATEDOWNLOAD),
tor.getInt(RPC_RATEUPLOAD),
tor.getInt(RPC_NUMSEEDS),
tor.getInt(RPC_TOTALSEEDS),
tor.getInt(RPC_NUMPEERS),
tor.getInt(RPC_TOTALPEERS),
tor.getInt(RPC_RATEDOWNLOAD),
tor.getInt(RPC_RATEUPLOAD),
tor.getInt(RPC_NUMSEEDS),
tor.getInt(RPC_TOTALSEEDS),
tor.getInt(RPC_NUMPEERS),
tor.getInt(RPC_TOTALPEERS),
tor.getInt(RPC_ETA),
tor.getLong(RPC_DOWNLOADEDEVER),
tor.getLong(RPC_UPLOADEDEVER),
tor.getLong(RPC_TOTALSIZE),
tor.getLong(RPC_DOWNLOADEDEVER),
tor.getLong(RPC_UPLOADEDEVER),
tor.getLong(RPC_TOTALSIZE),
((float) tor.getDouble(RPC_PARTDONE)) / 100f, // Percentage to [0..1]
0f, // Not available
tor.has(RPC_LABEL)? tor.getString(RPC_LABEL): null,
tor.has(RPC_TIMEADDED)? new Date((long) (tor.getDouble(RPC_TIMEADDED) * 1000L)): null,
null, // Not available
tor.getString(RPC_MESSAGE),
error,
settings.getType()));
// @formatter:on
}
}
@ -673,23 +684,26 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -673,23 +684,26 @@ public class DelugeAdapter implements IDaemonAdapter {
private ArrayList<Label> parseJsonRetrieveLabels(JSONObject response) throws JSONException {
// Get the labels, of they exist (which is dependent on the plugin)
if (!response.has("filters"))
if (!response.has("filters")) {
return null;
}
JSONObject filters = response.getJSONObject("filters");
if (!filters.has("label"))
if (!filters.has("label")) {
return null;
}
JSONArray labels = filters.getJSONArray("label");
// Parse response
ArrayList<Label> allLabels = new ArrayList<Label>();
for (int i = 0; i < labels.length(); i++) {
JSONArray labelAndCount = labels.getJSONArray(i);
if (labelAndCount.getString(0).equals("All"))
if (labelAndCount.getString(0).equals("All")) {
continue; // Ignore the 'All' filter, which is not an actual label
}
allLabels.add(new Label(labelAndCount.getString(0), labelAndCount.getInt(1)));
}
return allLabels;
}
private ArrayList<TorrentFile> parseJsonFileListing(JSONObject response, Torrent torrent) throws JSONException {
@ -701,9 +715,10 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -701,9 +715,10 @@ public class DelugeAdapter implements IDaemonAdapter {
JSONArray priorities = response.getJSONArray(RPC_FILEPRIORITIES);
if (objects != null) {
for (int j = 0; j < objects.length(); j++) {
JSONObject file = objects.getJSONObject(j);
// Add the parsed torrent to the list
// @formatter:off
files.add(new TorrentFile(
"" + file.getInt(RPC_INDEX),
file.getString(RPC_PATH),
@ -712,6 +727,7 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -712,6 +727,7 @@ public class DelugeAdapter implements IDaemonAdapter {
file.getLong(RPC_SIZE),
(long) (progress.getDouble(j) * file.getLong(RPC_SIZE)),
convertDelugePriority(priorities.getInt(j))));
// @formatter:on
}
}
@ -724,25 +740,25 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -724,25 +740,25 @@ public class DelugeAdapter implements IDaemonAdapter {
if (version >= 10303) {
// Priority codes changes from Deluge 1.3.3 onwards
switch (priority) {
case 0:
return Priority.Off;
case 1:
return Priority.Low;
case 7:
return Priority.High;
default:
return Priority.Normal;
case 0:
return Priority.Off;
case 1:
return Priority.Low;
case 7:
return Priority.High;
default:
return Priority.Normal;
}
} else {
switch (priority) {
case 0:
return Priority.Off;
case 2:
return Priority.Normal;
case 5:
return Priority.High;
default:
return Priority.Low;
case 0:
return Priority.Off;
case 2:
return Priority.Normal;
case 5:
return Priority.High;
default:
return Priority.Low;
}
}
}
@ -751,25 +767,25 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -751,25 +767,25 @@ public class DelugeAdapter implements IDaemonAdapter {
if (version >= 10303) {
// Priority codes changes from Deluge 1.3.3 onwards
switch (priority) {
case Off:
return 0;
case Low:
return 1;
case High:
return 7;
default:
return 5;
case Off:
return 0;
case Low:
return 1;
case High:
return 7;
default:
return 5;
}
} else {
switch (priority) {
case Off:
return 0;
case Normal:
return 2;
case High:
return 5;
default:
return 1;
case Off:
return 0;
case Normal:
return 2;
case High:
return 5;
default:
return 1;
}
}
}
@ -817,5 +833,5 @@ public class DelugeAdapter implements IDaemonAdapter { @@ -817,5 +833,5 @@ public class DelugeAdapter implements IDaemonAdapter {
public DaemonSettings getSettings() {
return this.settings;
}
}

10
app/src/main/java/org/transdroid/daemon/DummyAdapter.java

@ -25,6 +25,7 @@ import java.util.Date; @@ -25,6 +25,7 @@ import java.util.Date;
import java.util.List;
import java.util.Random;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.task.AddByFileTask;
import org.transdroid.daemon.task.AddByMagnetUrlTask;
@ -47,7 +48,6 @@ import org.transdroid.daemon.task.SetDownloadLocationTask; @@ -47,7 +48,6 @@ import org.transdroid.daemon.task.SetDownloadLocationTask;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetLabelTask;
import org.transdroid.daemon.task.SetTrackersTask;
import org.transdroid.daemon.util.DLog;
import android.net.Uri;
@ -124,7 +124,7 @@ public class DummyAdapter implements IDaemonAdapter { @@ -124,7 +124,7 @@ public class DummyAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
@ -164,7 +164,7 @@ public class DummyAdapter implements IDaemonAdapter { @@ -164,7 +164,7 @@ public class DummyAdapter implements IDaemonAdapter {
case AddByFile:
String file = ((AddByFileTask) task).getFile();
DLog.d(LOG_NAME, "Adding torrent " + file);
log.d(LOG_NAME, "Adding torrent " + file);
File upload = new File(URI.create(file));
dummyTorrents.add(new Torrent(0, "torrent_file", upload.getName(), TorrentStatus.Queued, "/downloads/"
+ file, 0, 0, 0, 0, 0, 0, -1, 0, 0, 1024 * 1024 * 1000, 0, 1F, "isos", new Date(), null, null,
@ -174,7 +174,7 @@ public class DummyAdapter implements IDaemonAdapter { @@ -174,7 +174,7 @@ public class DummyAdapter implements IDaemonAdapter {
case AddByUrl:
String url = ((AddByUrlTask) task).getUrl();
DLog.d(LOG_NAME, "Adding torrent " + url);
log.d(LOG_NAME, "Adding torrent " + url);
if (url == null || url.equals(""))
throw new DaemonException(DaemonException.ExceptionType.ParsingFailed, "No url specified");
Uri uri = Uri.parse(url);
@ -186,7 +186,7 @@ public class DummyAdapter implements IDaemonAdapter { @@ -186,7 +186,7 @@ public class DummyAdapter implements IDaemonAdapter {
case AddByMagnetUrl:
String magnet = ((AddByMagnetUrlTask) task).getUrl();
DLog.d(LOG_NAME, "Adding torrent " + magnet);
log.d(LOG_NAME, "Adding torrent " + magnet);
Uri magnetUri = Uri.parse(magnet);
dummyTorrents.add(new Torrent(0, "torrent_magnet", magnetUri.getLastPathSegment(),
TorrentStatus.Queued, "/downloads/" + magnetUri.getLastPathSegment(), 0, 0, 0, 0, 0, 0, -1, 0,

3
app/src/main/java/org/transdroid/daemon/IDaemonAdapter.java

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
*/
package org.transdroid.daemon;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.task.DaemonTask;
import org.transdroid.daemon.task.DaemonTaskResult;
@ -29,7 +30,7 @@ import org.transdroid.daemon.task.DaemonTaskResult; @@ -29,7 +30,7 @@ import org.transdroid.daemon.task.DaemonTaskResult;
*/
public interface IDaemonAdapter {
public DaemonTaskResult executeTask(DaemonTask task);
public DaemonTaskResult executeTask(Log log, DaemonTask task);
public Daemon getType();

392
app/src/main/java/org/transdroid/daemon/Ktorrent/KtorrentAdapter.java

@ -17,15 +17,9 @@ @@ -17,15 +17,9 @@
*/
package org.transdroid.daemon.Ktorrent;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
@ -37,14 +31,15 @@ import org.apache.http.client.methods.HttpPost; @@ -37,14 +31,15 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Priority;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentFile;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.task.AddByFileTask;
import org.transdroid.daemon.task.AddByMagnetUrlTask;
import org.transdroid.daemon.task.AddByUrlTask;
@ -57,19 +52,23 @@ import org.transdroid.daemon.task.GetFileListTaskSuccessResult; @@ -57,19 +52,23 @@ import org.transdroid.daemon.task.GetFileListTaskSuccessResult;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
/**
* An adapter that allows for easy access to Ktorrent's web interface. Communication
* is handled via HTTP GET requests and XML responses.
*
* An adapter that allows for easy access to Ktorrent's web interface. Communication is handled via HTTP GET requests
* and XML responses.
* @author erickok
*
*/
public class KtorrentAdapter implements IDaemonAdapter {
@ -86,10 +85,9 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -86,10 +85,9 @@ public class KtorrentAdapter implements IDaemonAdapter {
private static final String RPC_URL_FILES = "/data/torrent/files.xml?torrent=";
//private static final String RPC_COOKIE_NAME = "KT_SESSID";
private static final String RPC_SUCCESS = "<result>OK</result>";
static private int retries = 0;
private DaemonSettings settings;
private DefaultHttpClient httpclient;
static private int retries = 0;
/**
* Initialises an adapter that provides operations to the Ktorrent web interface
@ -98,167 +96,189 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -98,167 +96,189 @@ public class KtorrentAdapter implements IDaemonAdapter {
this.settings = settings;
}
/**
* Calculate the SHA1 hash of a password/challenge string to use with the login requests.
* @param passkey A concatenation of the challenge string and plain text password
* @return A hex-formatted SHA1-hashed string of the challenge and password strings
*/
public static String sha1Pass(String passkey) {
try {
MessageDigest m = MessageDigest.getInstance("SHA1");
byte[] data = passkey.getBytes();
m.update(data, 0, data.length);
BigInteger i = new BigInteger(1, m.digest());
return String.format("%1$040X", i).toLowerCase();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
return new RetrieveTaskSuccessResult((RetrieveTask) task, makeStatsRequest(),null);
case GetFileList:
// Request file listing for a torrent
return new GetFileListTaskSuccessResult((GetFileListTask) task, makeFileListRequest(task.getTargetTorrent()));
case AddByFile:
// Add a torrent to the server by sending the contents of a local .torrent file
String file = ((AddByFileTask)task).getFile();
makeFileUploadRequest(file);
return null;
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask)task).getUrl();
makeActionRequest("load_torrent=" + url);
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask)task).getUrl();
makeActionRequest("load_torrent=" + magnet);
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
// Note that removing with data is not supported
makeActionRequest("remove=" + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
makeActionRequest("stop=" + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Pause all torrents
makeActionRequest("stopall=true");
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeActionRequest("start=" + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeActionRequest("startall=true");
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
// Set the priorities of the files of some torrent
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
String act = "file_np=" + task.getTargetTorrent().getUniqueID() + "-";
switch (prioTask.getNewPriority()) {
case Off:
act = "file_stop=" + task.getTargetTorrent().getUniqueID() + "-";
break;
case Low:
case Normal:
act = "file_lp=" + task.getTargetTorrent().getUniqueID() + "-";
break;
case High:
act = "file_hp=" + task.getTargetTorrent().getUniqueID() + "-";
break;
}
// It seems KTorrent's web UI does not allow for setting all priorities in one request :(
for (TorrentFile forFile : prioTask.getForFiles()) {
makeActionRequest(act + forFile.getKey());
}
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
// TODO: Implement this?
return null;
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, task.getMethod() + " is not supported by " + getType()));
case Retrieve:
// Request all torrents from server
return new RetrieveTaskSuccessResult((RetrieveTask) task, makeStatsRequest(log), null);
case GetFileList:
// Request file listing for a torrent
return new GetFileListTaskSuccessResult((GetFileListTask) task,
makeFileListRequest(log, task.getTargetTorrent()));
case AddByFile:
// Add a torrent to the server by sending the contents of a local .torrent file
String file = ((AddByFileTask) task).getFile();
makeFileUploadRequest(log, file);
return null;
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeActionRequest(log, "load_torrent=" + url);
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
makeActionRequest(log, "load_torrent=" + magnet);
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
// Note that removing with data is not supported
makeActionRequest(log, "remove=" + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
makeActionRequest(log, "stop=" + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Pause all torrents
makeActionRequest(log, "stopall=true");
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeActionRequest(log, "start=" + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeActionRequest(log, "startall=true");
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
// Set the priorities of the files of some torrent
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
String act = "file_np=" + task.getTargetTorrent().getUniqueID() + "-";
switch (prioTask.getNewPriority()) {
case Off:
act = "file_stop=" + task.getTargetTorrent().getUniqueID() + "-";
break;
case Low:
case Normal:
act = "file_lp=" + task.getTargetTorrent().getUniqueID() + "-";
break;
case High:
act = "file_hp=" + task.getTargetTorrent().getUniqueID() + "-";
break;
}
// It seems KTorrent's web UI does not allow for setting all priorities in one request :(
for (TorrentFile forFile : prioTask.getForFiles()) {
makeActionRequest(log, act + forFile.getKey());
}
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
// TODO: Implement this?
return null;
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
} catch (LoggedOutException e) {
// Invalidate our session
httpclient = null;
if (retries < 2) {
retries++;
// Retry
DLog.d(LOG_NAME, "We were logged out without knowing: retry");
return executeTask(task);
log.d(LOG_NAME, "We were logged out without knowing: retry");
return executeTask(log, task);
} else {
// Never retry more than twice; in this case just return a task failure
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ConnectionError, "Retried " + retries + " already, so we stopped now"));
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ConnectionError,
"Retried " + retries + " already, so we stopped now"));
}
} catch (DaemonException e) {
// Invalidate our session
httpclient = null;
// Return the task failure
return new DaemonTaskFailureResult(task, e);
}
}
private List<Torrent> makeStatsRequest() throws DaemonException, LoggedOutException {
private List<Torrent> makeStatsRequest(Log log) throws DaemonException, LoggedOutException {
try {
// Initialise the HTTP client
initialise();
makeLoginRequest();
makeLoginRequest(log);
// Make request
HttpGet httpget = new HttpGet(buildWebUIUrl() + RPC_URL_STATS);
HttpResponse response = httpclient.execute(httpget);
// Read XML response
InputStream instream = response.getEntity().getContent();
List<Torrent> torrents = StatsParser.parse(new InputStreamReader(instream), settings.getDownloadDir(), settings.getOS().getPathSeperator());
List<Torrent> torrents = StatsParser.parse(new InputStreamReader(instream), settings.getDownloadDir(),
settings.getOS().getPathSeperator());
instream.close();
return torrents;
return torrents;
} catch (LoggedOutException e) {
throw e;
} catch (DaemonException e) {
DLog.d(LOG_NAME, "Parsing error: " + e.toString());
log.d(LOG_NAME, "Parsing error: " + e.toString());
throw e;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private List<TorrentFile> makeFileListRequest(Torrent torrent) throws DaemonException, LoggedOutException {
private List<TorrentFile> makeFileListRequest(Log log, Torrent torrent) throws DaemonException, LoggedOutException {
try {
// Initialise the HTTP client
initialise();
makeLoginRequest();
makeLoginRequest(log);
// Make request
HttpGet httpget = new HttpGet(buildWebUIUrl() + RPC_URL_FILES + torrent.getUniqueID());
HttpResponse response = httpclient.execute(httpget);
@ -267,26 +287,28 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -267,26 +287,28 @@ public class KtorrentAdapter implements IDaemonAdapter {
InputStream instream = response.getEntity().getContent();
List<TorrentFile> files = FileListParser.parse(new InputStreamReader(instream), torrent.getLocationDir());
instream.close();
// If the files list is empty, it means that this is a single-file torrent
// We can mimic this single file form the torrent statistics itself
files.add(new TorrentFile("" + 0, torrent.getName(), torrent.getName(), torrent.getLocationDir() + torrent.getName(), torrent.getTotalSize(), torrent.getDownloadedEver(), Priority.Normal));
return files;
files.add(new TorrentFile("" + 0, torrent.getName(), torrent.getName(),
torrent.getLocationDir() + torrent.getName(), torrent.getTotalSize(), torrent.getDownloadedEver(),
Priority.Normal));
return files;
} catch (LoggedOutException e) {
throw e;
} catch (DaemonException e) {
DLog.d(LOG_NAME, "Parsing error: " + e.toString());
log.d(LOG_NAME, "Parsing error: " + e.toString());
throw e;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private void makeLoginRequest() throws DaemonException {
private void makeLoginRequest(Log log) throws DaemonException {
try {
@ -294,10 +316,10 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -294,10 +316,10 @@ public class KtorrentAdapter implements IDaemonAdapter {
HttpGet httpget = new HttpGet(buildWebUIUrl() + RPC_URL_CHALLENGE);
HttpResponse response = httpclient.execute(httpget);
InputStream instream = response.getEntity().getContent();
String challengeString = HttpHelper.convertStreamToString(instream).replaceAll("\\<.*?>","").trim();
String challengeString = HttpHelper.convertStreamToString(instream).replaceAll("<.*?>", "").trim();
instream.close();
// Challenge string should be something like TncpX3TB8uZ0h8eqztZ6
if (challengeString == null || challengeString.length() != 20) {
if (challengeString.length() != 20) {
throw new DaemonException(ExceptionType.UnexpectedResponse, "No (valid) challenge string received");
}
@ -305,54 +327,38 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -305,54 +327,38 @@ public class KtorrentAdapter implements IDaemonAdapter {
HttpPost httppost2 = new HttpPost(buildWebUIUrl() + RPC_URL_LOGIN);
List<NameValuePair> params = new ArrayList<NameValuePair>(3);
params.add(new BasicNameValuePair(RPC_URL_LOGIN_USER, settings.getUsername()));
params.add(new BasicNameValuePair(RPC_URL_LOGIN_PASS, "")); // Password is send (as SHA1 hex) in the challenge field
params.add(new BasicNameValuePair(RPC_URL_LOGIN_CHAL, sha1Pass(challengeString + settings.getPassword()))); // Make a SHA1 encrypted hex-formated string of the challenge code and password
params.add(new BasicNameValuePair(RPC_URL_LOGIN_PASS,
"")); // Password is send (as SHA1 hex) in the challenge field
params.add(new BasicNameValuePair(RPC_URL_LOGIN_CHAL, sha1Pass(challengeString +
settings.getPassword()))); // Make a SHA1 encrypted hex-formated string of the challenge code and password
httppost2.setEntity(new UrlEncodedFormEntity(params));
// This sets the authentication cookie
httpclient.execute(httppost2);
/*InputStream instream2 = response2.getEntity().getContent();
String result2 = HttpHelper.ConvertStreamToString(instream2);
instream2.close();*/
// Successfully logged in; we may retry later if needed
retries = 0;
} catch (DaemonException e) {
DLog.d(LOG_NAME, "Login error: " + e.toString());
log.d(LOG_NAME, "Login error: " + e.toString());
throw e;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error during login: " + e.toString());
log.d(LOG_NAME, "Error during login: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
/**
* Calculate the SHA1 hash of a password/challenge string to use with the login requests.
* @param passkey A concatenation of the challenge string and plain text password
* @return A hex-formatted SHA1-hashed string of the challenge and password strings
*/
public static String sha1Pass(String passkey) {
try {
MessageDigest m = MessageDigest.getInstance("SHA1");
byte[] data = passkey.getBytes();
m.update(data,0,data.length);
BigInteger i = new BigInteger(1,m.digest());
return String.format("%1$040X", i).toLowerCase();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
private boolean makeActionRequest(String action) throws DaemonException, LoggedOutException {
private boolean makeActionRequest(Log log, String action) throws DaemonException, LoggedOutException {
try {
// Initialise the HTTP client
initialise();
makeLoginRequest();
makeLoginRequest(log);
// Make request
HttpGet httpget = new HttpGet(buildWebUIUrl() + RPC_URL_ACTION + action);
HttpResponse response = httpclient.execute(httpget);
@ -374,34 +380,39 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -374,34 +380,39 @@ public class KtorrentAdapter implements IDaemonAdapter {
} catch (LoggedOutException e) {
throw e;
} catch (DaemonException e) {
DLog.d(LOG_NAME, action + " request error: " + e.toString());
log.d(LOG_NAME, action + " request error: " + e.toString());
throw e;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private boolean makeFileUploadRequest(String target) throws DaemonException, LoggedOutException {
private boolean makeFileUploadRequest(Log log, String target) throws DaemonException, LoggedOutException {
try {
// Initialise the HTTP client
initialise();
makeLoginRequest();
makeLoginRequest(log);
// Make request
HttpPost httppost = new HttpPost(buildWebUIUrl() + RPC_URL_UPLOAD);
HttpPost httppost = new HttpPost(buildWebUIUrl() + RPC_URL_UPLOAD);
File upload = new File(URI.create(target));
Part[] parts = { new FilePart("load_torrent", upload) };
Part[] parts = {new FilePart("load_torrent", upload)};
httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
// Make sure we are not automatically redirected
RedirectHandler handler = new RedirectHandler() {
@Override
public boolean isRedirectRequested(HttpResponse response, HttpContext context) { return false; }
public boolean isRedirectRequested(HttpResponse response, HttpContext context) {
return false;
}
@Override
public URI getLocationURI(HttpResponse response, HttpContext context) throws ProtocolException { return null; }
public URI getLocationURI(HttpResponse response, HttpContext context) throws ProtocolException {
return null;
}
};
httpclient.setRedirectHandler(handler);
HttpResponse response = httpclient.execute(httppost);
@ -423,15 +434,15 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -423,15 +434,15 @@ public class KtorrentAdapter implements IDaemonAdapter {
} catch (LoggedOutException e) {
throw e;
} catch (DaemonException e) {
DLog.d(LOG_NAME, "File upload error: " + e.toString());
log.d(LOG_NAME, "File upload error: " + e.toString());
throw e;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
/**
* Indicates if we were already successfully authenticated
* @return True if the proper authentication cookie was already loaded
@ -448,7 +459,6 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -448,7 +459,6 @@ public class KtorrentAdapter implements IDaemonAdapter {
/**
* Instantiates an HTTP client that can be used for all Ktorrent requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException Thrown on settings error
*/
private void initialise() throws DaemonException {
@ -458,7 +468,7 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -458,7 +468,7 @@ public class KtorrentAdapter implements IDaemonAdapter {
}
httpclient = HttpHelper.createStandardHttpClient(settings, false);
}
/**
* Build the base URL for a Ktorrent web site request from the user settings.
* @return The base URL of for a request, i.e. http://localhost:8080
@ -476,5 +486,5 @@ public class KtorrentAdapter implements IDaemonAdapter { @@ -476,5 +486,5 @@ public class KtorrentAdapter implements IDaemonAdapter {
public DaemonSettings getSettings() {
return this.settings;
}
}

95
app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java

@ -23,6 +23,7 @@ import java.io.UnsupportedEncodingException; @@ -23,6 +23,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.http.HttpEntity;
@ -36,6 +37,7 @@ import org.apache.http.protocol.HTTP; @@ -36,6 +37,7 @@ import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonSettings;
@ -62,7 +64,6 @@ import org.transdroid.daemon.task.RetrieveTask; @@ -62,7 +64,6 @@ import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
@ -84,13 +85,13 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -84,13 +85,13 @@ public class QbittorrentAdapter implements IDaemonAdapter {
this.settings = settings;
}
private synchronized void ensureVersion() throws DaemonException {
private synchronized void ensureVersion(Log log) throws DaemonException {
if (version > 0)
return;
// We still need to retrieve the version number from the server
// Do this by getting the web interface about page and trying to parse the version number
// Format is something like 'qBittorrent v2.9.7 (Web UI)'
String about = makeRequest("/about.html");
String about = makeRequest(log, "/about.html");
String aboutStartText = "qBittorrent v";
String aboutEndText = " (Web UI)";
int aboutStart = about.indexOf(aboutStartText);
@ -129,83 +130,83 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -129,83 +130,83 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
ensureVersion();
ensureVersion(log);
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
JSONArray result = new JSONArray(makeRequest(version >= 30000 ? "/json/torrents" : "/json/events"));
JSONArray result = new JSONArray(makeRequest(log, version >= 30000 ? "/json/torrents" : "/json/events"));
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null);
case GetTorrentDetails:
// Request tracker and error details for a specific teacher
String mhash = ((GetTorrentDetailsTask) task).getTargetTorrent().getUniqueID();
JSONArray messages = new JSONArray(makeRequest("/json/propertiesTrackers/" + mhash));
String mhash = task.getTargetTorrent().getUniqueID();
JSONArray messages = new JSONArray(makeRequest(log, "/json/propertiesTrackers/" + mhash));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
parseJsonTorrentDetails(messages));
case GetFileList:
// Request files listing for a specific torrent
String fhash = ((GetFileListTask) task).getTargetTorrent().getUniqueID();
JSONArray files = new JSONArray(makeRequest("/json/propertiesFiles/" + fhash));
String fhash = task.getTargetTorrent().getUniqueID();
JSONArray files = new JSONArray(makeRequest(log, "/json/propertiesFiles/" + fhash));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files));
case AddByFile:
// Upload a local .torrent file
String ufile = ((AddByFileTask) task).getFile();
makeUploadRequest("/command/upload", ufile);
makeUploadRequest("/command/upload", ufile, log);
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeRequest("/command/download", new BasicNameValuePair("urls", url));
makeRequest(log, "/command/download", new BasicNameValuePair("urls", url));
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
makeRequest("/command/download", new BasicNameValuePair("urls", magnet));
makeRequest(log, "/command/download", new BasicNameValuePair("urls", magnet));
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest((removeTask.includingData() ? "/command/deletePerm" : "/command/delete"),
makeRequest(log, (removeTask.includingData() ? "/command/deletePerm" : "/command/delete"),
new BasicNameValuePair("hashes", removeTask.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
makeRequest("/command/pause", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
makeRequest(log, "/command/pause", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Resume all torrents
makeRequest("/command/pauseall");
makeRequest(log, "/command/pauseall");
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeRequest("/command/resume", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
makeRequest(log, "/command/resume", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeRequest("/command/resumeall");
makeRequest(log, "/command/resumeall");
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
@ -222,7 +223,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -222,7 +223,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
// We have to make a separate request per file, it seems
for (TorrentFile file : setPrio.getForFiles()) {
makeRequest("/command/setFilePrio", new BasicNameValuePair("hash", task.getTargetTorrent()
makeRequest(log, "/command/setFilePrio", new BasicNameValuePair("hash", task.getTargetTorrent()
.getUniqueID()), new BasicNameValuePair("id", file.getKey()), new BasicNameValuePair(
"priority", newPrio));
}
@ -233,14 +234,14 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -233,14 +234,14 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// TODO: This doesn't seem to work yet
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
int dl = (ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate().intValue());
int ul = (ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate().intValue());
int dl = (ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate());
int ul = (ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate());
// First get the preferences
JSONObject prefs = new JSONObject(makeRequest("/json/preferences"));
JSONObject prefs = new JSONObject(makeRequest(log, "/json/preferences"));
prefs.put("dl_limit", dl);
prefs.put("up_limit", ul);
makeRequest("/command/setPreferences",
makeRequest(log, "/command/setPreferences",
new BasicNameValuePair("json", URLEncoder.encode(prefs.toString(), HTTP.UTF_8)));
return new DaemonTaskSuccessResult(task);
@ -257,18 +258,16 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -257,18 +258,16 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
}
private String makeRequest(String path, NameValuePair... params) throws DaemonException {
private String makeRequest(Log log, String path, NameValuePair... params) throws DaemonException {
try {
// Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path));
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
for (NameValuePair param : params) {
nvps.add(param);
}
Collections.addAll(nvps, params);
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
return makeWebRequest(path, httppost);
return makeWebRequest(httppost, log);
} catch (UnsupportedEncodingException e) {
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
@ -276,7 +275,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -276,7 +275,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
private String makeUploadRequest(String path, String file) throws DaemonException {
private String makeUploadRequest(String path, String file, Log log) throws DaemonException {
try {
@ -285,7 +284,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -285,7 +284,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
File upload = new File(URI.create(file));
Part[] parts = { new FilePart("torrentfile", upload) };
httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
return makeWebRequest(path, httppost);
return makeWebRequest(httppost, log);
} catch (FileNotFoundException e) {
throw new DaemonException(ExceptionType.FileAccessError, e.toString());
@ -293,7 +292,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -293,7 +292,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
private String makeWebRequest(String path, HttpPost httppost) throws DaemonException {
private String makeWebRequest(HttpPost httppost, Log log) throws DaemonException {
try {
@ -320,11 +319,11 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -320,11 +319,11 @@ public class QbittorrentAdapter implements IDaemonAdapter {
return result;
}
DLog.d(LOG_NAME, "Error: No entity in HTTP response");
log.d(LOG_NAME, "Error: No entity in HTTP response");
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response.");
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
@ -332,7 +331,6 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -332,7 +331,6 @@ public class QbittorrentAdapter implements IDaemonAdapter {
/**
* Instantiates an HTTP client with proper credentials that can be used for all qBittorrent requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On conflicting or missing settings
*/
private void initialise() throws DaemonException {
@ -385,10 +383,31 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -385,10 +383,31 @@ public class QbittorrentAdapter implements IDaemonAdapter {
eta = (long) (size - (size * progress)) / dlspeed;
// Date added is only available in /json/propertiesGeneral on a per-torrent basis, unfortunately
// Add the parsed torrent to the list
torrents.add(new Torrent((long) i, tor.getString("hash"), tor.getString("name"), parseStatus(tor
.getString("state")), null, dlspeed, parseSpeed(tor.getString("upspeed")), seeders[0], seeders[1],
leechers[0], leechers[1], (int) eta, (long) (size * progress), (long) (size * ratio), size,
(float) progress, 0f, null, null, null, null, settings.getType()));
// @formatter:off
torrents.add(new Torrent(
(long) i,
tor.getString("hash"),
tor.getString("name"),
parseStatus(tor.getString("state")),
null,
dlspeed,
parseSpeed(tor.getString("upspeed")),
seeders[0],
seeders[1],
leechers[0],
leechers[1],
(int) eta,
(long) (size * progress),
(long) (size * ratio),
size,
(float) progress,
0f,
null,
null,
null,
null,
settings.getType()));
// @formatter:on
}
// Return the list

372
app/src/main/java/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java

@ -17,22 +17,7 @@ @@ -17,22 +17,7 @@
*/
package org.transdroid.daemon.Rtorrent;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
@ -61,9 +46,24 @@ import org.transdroid.daemon.task.RetrieveTaskSuccessResult; @@ -61,9 +46,24 @@ import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetLabelTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import de.timroes.axmlrpc.XMLRPCClient;
import de.timroes.axmlrpc.XMLRPCClient.UnauthorizdException;
import de.timroes.axmlrpc.XMLRPCException;
@ -88,14 +88,14 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -88,14 +88,14 @@ public class RtorrentAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
case Retrieve:
// @formatter:off
Object result = makeRtorrentCall("d.multicall",
// @formatter:off
Object result = makeRtorrentCall(log,"d.multicall",
new String[] { "main",
"d.get_hash=",
"d.get_name=",
@ -122,23 +122,24 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -122,23 +122,24 @@ public class RtorrentAdapter implements IDaemonAdapter {
"d.get_peers_complete=",
"d.get_peers_accounted=" });
// @formatter:on
return new RetrieveTaskSuccessResult((RetrieveTask) task, onTorrentsRetrieved(result), lastKnownLabels);
return new RetrieveTaskSuccessResult((RetrieveTask) task, onTorrentsRetrieved(result),
lastKnownLabels);
case GetTorrentDetails:
case GetTorrentDetails:
// @formatter:off
Object dresult = makeRtorrentCall("t.multicall", new String[] {
// @formatter:off
Object dresult = makeRtorrentCall(log,"t.multicall", new String[] {
task.getTargetTorrent().getUniqueID(),
"",
"t.get_url=" });
// @formatter:on
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
onTorrentDetailsRetrieved(dresult));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
onTorrentDetailsRetrieved(log, dresult));
case GetFileList:
case GetFileList:
// @formatter:off
Object fresult = makeRtorrentCall("f.multicall", new String[] {
// @formatter:off
Object fresult = makeRtorrentCall(log,"f.multicall", new String[] {
task.getTargetTorrent().getUniqueID(),
"",
"f.get_path=",
@ -149,137 +150,139 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -149,137 +150,139 @@ public class RtorrentAdapter implements IDaemonAdapter {
"f.get_priority=",
"f.get_frozen_path=" });
// @formatter:on
return new GetFileListTaskSuccessResult((GetFileListTask) task, onTorrentFilesRetrieved(fresult,
task.getTargetTorrent()));
case AddByFile:
// Request to add a torrent by local .torrent file
File file = new File(URI.create(((AddByFileTask) task).getFile()));
FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[(int) file.length()];
int read = 0;
while ((read = in.read(buffer, 0, buffer.length)) > 0) {
baos.write(buffer, 0, read);
}
byte[] bytes = baos.toByteArray();
int size = (int) file.length() * 2;
final int XMLRPC_EXTRA_PADDING = 1280;
makeRtorrentCall("set_xmlrpc_size_limit", new Object[] { size + XMLRPC_EXTRA_PADDING });
makeRtorrentCall("load_raw_start", new Object[] { bytes });
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeRtorrentCall("load_start", new String[] { url });
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
makeRtorrentCall("load_start", new String[] { magnet });
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
if (removeTask.includingData()) {
makeRtorrentCall("d.set_custom5", new String[] { task.getTargetTorrent().getUniqueID(), "1" });
}
makeRtorrentCall("d.erase", new String[] { task.getTargetTorrent().getUniqueID() });
return new DaemonTaskSuccessResult(task);
return new GetFileListTaskSuccessResult((GetFileListTask) task,
onTorrentFilesRetrieved(fresult, task.getTargetTorrent()));
case AddByFile:
// Request to add a torrent by local .torrent file
File file = new File(URI.create(((AddByFileTask) task).getFile()));
FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[(int) file.length()];
int read;
while ((read = in.read(buffer, 0, buffer.length)) > 0) {
baos.write(buffer, 0, read);
}
byte[] bytes = baos.toByteArray();
int size = (int) file.length() * 2;
final int XMLRPC_EXTRA_PADDING = 1280;
makeRtorrentCall(log, "set_xmlrpc_size_limit", new Object[]{size + XMLRPC_EXTRA_PADDING});
makeRtorrentCall(log, "load_raw_start", new Object[]{bytes});
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeRtorrentCall(log, "load_start", new String[]{url});
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
makeRtorrentCall(log, "load_start", new String[]{magnet});
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
if (removeTask.includingData()) {
makeRtorrentCall(log, "d.set_custom5",
new String[]{task.getTargetTorrent().getUniqueID(), "1"});
}
makeRtorrentCall(log, "d.erase", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task);
case Pause:
case Pause:
// Pause a torrent
makeRtorrentCall("d.pause", new String[] { task.getTargetTorrent().getUniqueID() });
return new DaemonTaskSuccessResult(task);
// Pause a torrent
makeRtorrentCall(log, "d.pause", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task);
case PauseAll:
case PauseAll:
// Resume all torrents
makeRtorrentCall("d.multicall", new String[] { "main", "d.pause=" });
return new DaemonTaskSuccessResult(task);
// Resume all torrents
makeRtorrentCall(log, "d.multicall", new String[]{"main", "d.pause="});
return new DaemonTaskSuccessResult(task);
case Resume:
case Resume:
// Resume a torrent
makeRtorrentCall("d.resume", new String[] { task.getTargetTorrent().getUniqueID() });
return new DaemonTaskSuccessResult(task);
// Resume a torrent
makeRtorrentCall(log, "d.resume", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task);
case ResumeAll:
case ResumeAll:
// Resume all torrents
makeRtorrentCall("d.multicall", new String[] { "main", "d.resume=" });
return new DaemonTaskSuccessResult(task);
// Resume all torrents
makeRtorrentCall(log, "d.multicall", new String[]{"main", "d.resume="});
return new DaemonTaskSuccessResult(task);
case Stop:
case Stop:
// Stop a torrent
makeRtorrentCall("d.stop", new String[] { task.getTargetTorrent().getUniqueID() });
return new DaemonTaskSuccessResult(task);
// Stop a torrent
makeRtorrentCall(log, "d.stop", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task);
case StopAll:
case StopAll:
// Stop all torrents
makeRtorrentCall("d.multicall", new String[] { "main", "d.stop=" });
return new DaemonTaskSuccessResult(task);
// Stop all torrents
makeRtorrentCall(log, "d.multicall", new String[]{"main", "d.stop="});
return new DaemonTaskSuccessResult(task);
case Start:
case Start:
// Start a torrent
makeRtorrentCall("d.start", new String[] { task.getTargetTorrent().getUniqueID() });
return new DaemonTaskSuccessResult(task);
// Start a torrent
makeRtorrentCall(log, "d.start", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task);
case StartAll:
case StartAll:
// Start all torrents
makeRtorrentCall("d.multicall", new String[] { "main", "d.start=" });
return new DaemonTaskSuccessResult(task);
// Start all torrents
makeRtorrentCall(log, "d.multicall", new String[]{"main", "d.start="});
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
case SetFilePriorities:
// For each of the chosen files belonging to some torrent, set the priority
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
String newPriority = "" + convertPriority(prioTask.getNewPriority());
// One at a time; rTorrent doesn't seem to support a multicall on a selective number of files
for (TorrentFile forFile : prioTask.getForFiles()) {
makeRtorrentCall("f.set_priority", new String[] {
task.getTargetTorrent().getUniqueID() + ":f" + forFile.getKey(), newPriority });
}
return new DaemonTaskSuccessResult(task);
// For each of the chosen files belonging to some torrent, set the priority
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
String newPriority = "" + convertPriority(prioTask.getNewPriority());
// One at a time; rTorrent doesn't seem to support a multicall on a selective number of files
for (TorrentFile forFile : prioTask.getForFiles()) {
makeRtorrentCall(log, "f.set_priority",
new String[]{task.getTargetTorrent().getUniqueID() + ":f" + forFile.getKey(),
newPriority});
}
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
makeRtorrentCall("set_download_rate", new String[] { (ratesTask.getDownloadRate() == null ? "0"
: ratesTask.getDownloadRate().toString() + "k") });
makeRtorrentCall("set_upload_rate", new String[] { (ratesTask.getUploadRate() == null ? "0" : ratesTask
.getUploadRate().toString() + "k") });
return new DaemonTaskSuccessResult(task);
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
makeRtorrentCall(log, "set_download_rate", new String[]{(ratesTask.getDownloadRate() == null ? "0" :
ratesTask.getDownloadRate().toString() + "k")});
makeRtorrentCall(log, "set_upload_rate", new String[]{
(ratesTask.getUploadRate() == null ? "0" : ratesTask.getUploadRate().toString() + "k")});
return new DaemonTaskSuccessResult(task);
case SetLabel:
case SetLabel:
SetLabelTask labelTask = (SetLabelTask) task;
makeRtorrentCall("d.set_custom1",
new String[] { task.getTargetTorrent().getUniqueID(), labelTask.getNewLabel() });
return new DaemonTaskSuccessResult(task);
SetLabelTask labelTask = (SetLabelTask) task;
makeRtorrentCall(log, "d.set_custom1",
new String[]{task.getTargetTorrent().getUniqueID(), labelTask.getNewLabel()});
return new DaemonTaskSuccessResult(task);
case ForceRecheck:
case ForceRecheck:
// Force re-check of data of a torrent
makeRtorrentCall("d.check_hash", new String[] { task.getTargetTorrent().getUniqueID() });
return new DaemonTaskSuccessResult(task);
// Force re-check of data of a torrent
makeRtorrentCall(log, "d.check_hash", new String[]{task.getTargetTorrent().getUniqueID()});
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
} catch (DaemonException e) {
return new DaemonTaskFailureResult(task, e);
@ -290,8 +293,8 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -290,8 +293,8 @@ public class RtorrentAdapter implements IDaemonAdapter {
}
}
private Object makeRtorrentCall(String serverMethod, Object[] arguments) throws DaemonException,
MalformedURLException {
private Object makeRtorrentCall(Log log, String serverMethod, Object[] arguments)
throws DaemonException, MalformedURLException {
// Initialise the HTTP client
if (rpcclient == null) {
@ -299,22 +302,25 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -299,22 +302,25 @@ public class RtorrentAdapter implements IDaemonAdapter {
}
String params = "";
for (Object arg : arguments)
for (Object arg : arguments) {
params += " " + arg.toString();
}
try {
DLog.d(LOG_NAME,
"Calling " + serverMethod + " with params ["
+ (params.length() > 100 ? params.substring(0, 100) + "..." : params) + " ]");
log.d(LOG_NAME, "Calling " + serverMethod + " with params [" +
(params.length() > 100 ? params.substring(0, 100) + "..." : params) + " ]");
return rpcclient.call(serverMethod, arguments);
} catch (XMLRPCException e) {
DLog.d(LOG_NAME, e.toString());
if (e.getCause() instanceof UnauthorizdException)
log.d(LOG_NAME, e.toString());
if (e.getCause() instanceof UnauthorizdException) {
throw new DaemonException(ExceptionType.AuthenticationFailure, e.toString());
if (e.getCause() instanceof DaemonException)
}
if (e.getCause() instanceof DaemonException) {
throw (DaemonException) e.getCause();
throw new DaemonException(ExceptionType.ConnectionError, "Error making call to " + serverMethod
+ " with params [" + (params.length() > 100 ? params.substring(0, 100) + "..." : params) + " ]: "
+ e.toString());
}
throw new DaemonException(ExceptionType.ConnectionError,
"Error making call to " + serverMethod + " with params [" +
(params.length() > 100 ? params.substring(0, 100) + "..." : params) + " ]: " +
e.toString());
}
}
@ -336,12 +342,9 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -336,12 +342,9 @@ public class RtorrentAdapter implements IDaemonAdapter {
* @return The URL of the RPC API
*/
private String buildWebUIUrl() {
return (settings.getSsl() ? "https://" : "http://")
+ settings.getAddress()
+ ":"
+ settings.getPort()
+ (settings.getFolder() == null || settings.getFolder().equals("") ? DEFAULT_RPC_URL : settings
.getFolder());
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() +
(settings.getFolder() == null || settings.getFolder().equals("") ? DEFAULT_RPC_URL :
settings.getFolder());
}
private List<Torrent> onTorrentsRetrieved(Object response) throws DaemonException {
@ -365,7 +368,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -365,7 +368,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
error = error.equals("") ? null : error;
// Determine the time added
Date added = null;
Date added;
Long addtime = null;
try {
addtime = Long.valueOf(((String) info[19]).trim());
@ -373,14 +376,16 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -373,14 +376,16 @@ public class RtorrentAdapter implements IDaemonAdapter {
// Not a number (timestamp); ignore and fall back to using creationtime
}
if (addtime != null)
// Successfully received the addtime from rTorrent (which is a String like '1337089336\n')
// Successfully received the addtime from rTorrent (which is a String like '1337089336\n')
{
added = new Date(addtime * 1000L);
else {
} else {
// rTorrent didn't have the addtime (missing plugin?): base it on creationtime instead
if (info[11] instanceof Long)
if (info[11] instanceof Long) {
added = new Date((Long) info[11] * 1000L);
else
} else {
added = new Date((Integer) info[11] * 1000L);
}
}
// Determine the seeding time
@ -392,8 +397,10 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -392,8 +397,10 @@ public class RtorrentAdapter implements IDaemonAdapter {
// Not a number (timestamp); ignore and fall back to using creationtime
}
if (seedingtime != null)
// Successfully received the seedingtime from rTorrent (which is a String like '1337089336\n')
// Successfully received the seedingtime from rTorrent (which is a String like '1337089336\n')
{
finished = new Date(seedingtime * 1000L);
}
// Determine the label
String label = null;
@ -455,11 +462,11 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -455,11 +462,11 @@ public class RtorrentAdapter implements IDaemonAdapter {
basePath.substring(0, basePath.indexOf((String)info[17])), // locationDir
rateDownload, // rateDownload
(Integer)info[4], // rateUpload
((Integer)info[22]).intValue(), // seedersConnected
((Integer)info[5]).intValue() + ((Integer)info[6]).intValue(), // seedersKnown
((Integer)info[23]).intValue(), // leechersConnected
((Integer)info[5]).intValue() + ((Integer)info[6]).intValue(), // leechersKnown
(rateDownload > 0? (int) ((Integer)info[12] / rateDownload): -1), // eta (bytes left / rate download, if rate > 0)
(Integer)info[22], // seedersConnected
(Integer)info[5] + (Integer)info[6], // seedersKnown
(Integer)info[23], // leechersConnected
(Integer)info[5] + (Integer)info[6], // leechersKnown
(rateDownload > 0? (Integer)info[12] / rateDownload: -1), // eta (bytes left / rate download, if rate > 0)
(Integer)info[8], // downloadedEver
(Integer)info[9], // uploadedEver
(Integer)info[10], // totalSize
@ -476,8 +483,9 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -476,8 +483,9 @@ public class RtorrentAdapter implements IDaemonAdapter {
}
lastKnownLabels = new ArrayList<Label>();
for (Entry<String, Integer> pair : labels.entrySet()) {
if (pair.getKey() != null)
if (pair.getKey() != null) {
lastKnownLabels.add(new Label(pair.getKey(), pair.getValue()));
}
}
return torrents;
@ -550,24 +558,24 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -550,24 +558,24 @@ public class RtorrentAdapter implements IDaemonAdapter {
private Priority convertRtorrentPriority(int code) {
// Note that Rtorrent has no low priority value
switch (code) {
case 0:
return Priority.Off;
case 2:
return Priority.High;
default:
return Priority.Normal;
case 0:
return Priority.Off;
case 2:
return Priority.High;
default:
return Priority.Normal;
}
}
private int convertPriority(Priority priority) {
// Note that Rtorrent has no low priority value
switch (priority) {
case Off:
return 0;
case High:
return 2;
default:
return 1;
case Off:
return 0;
case High:
return 2;
default:
return 1;
}
}
@ -587,7 +595,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -587,7 +595,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
}
}
private TorrentDetails onTorrentDetailsRetrieved(Object response) throws DaemonException {
private TorrentDetails onTorrentDetailsRetrieved(Log log, Object response) throws DaemonException {
if (response == null || !(response instanceof Object[])) {
@ -601,12 +609,12 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -601,12 +609,12 @@ public class RtorrentAdapter implements IDaemonAdapter {
List<String> trackers = new ArrayList<String>();
Object[] responseList = (Object[]) response;
try {
for (int i = 0; i < responseList.length; i++) {
Object[] info = (Object[]) responseList[i];
for (Object aResponseList : responseList) {
Object[] info = (Object[]) aResponseList;
trackers.add((String) info[0]);
}
} catch (Exception e) {
DLog.e(LOG_NAME, e.toString());
log.e(LOG_NAME, e.toString());
}
return new TorrentDetails(trackers, null);

271
app/src/main/java/org/transdroid/daemon/Synology/SynologyAdapter.java

@ -17,13 +17,6 @@ @@ -17,13 +17,6 @@
*/
package org.transdroid.daemon.Synology;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
@ -31,6 +24,7 @@ import org.apache.http.impl.client.DefaultHttpClient; @@ -31,6 +24,7 @@ import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
@ -55,12 +49,17 @@ import org.transdroid.daemon.task.RetrieveTask; @@ -55,12 +49,17 @@ import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.Collections2;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* The daemon adapter from the Synology Download Station torrent client.
*
*/
public class SynologyAdapter implements IDaemonAdapter {
@ -76,47 +75,48 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -76,47 +75,48 @@ public class SynologyAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
String tid;
try {
switch (task.getMethod()) {
case Retrieve:
return new RetrieveTaskSuccessResult((RetrieveTask) task, tasksList(), null);
return new RetrieveTaskSuccessResult((RetrieveTask) task, tasksList(log), null);
case GetStats:
return null;
case GetTorrentDetails:
tid = task.getTargetTorrent().getUniqueID();
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, torrentDetails(tid));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
torrentDetails(log, tid));
case GetFileList:
tid = task.getTargetTorrent().getUniqueID();
return new GetFileListTaskSuccessResult((GetFileListTask) task, fileList(tid));
return new GetFileListTaskSuccessResult((GetFileListTask) task, fileList(log, tid));
case AddByFile:
return null;
case AddByUrl:
String url = ((AddByUrlTask)task).getUrl();
createTask(url);
String url = ((AddByUrlTask) task).getUrl();
createTask(log, url);
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
String magnet = ((AddByMagnetUrlTask)task).getUrl();
createTask(magnet);
String magnet = ((AddByMagnetUrlTask) task).getUrl();
createTask(log, magnet);
return new DaemonTaskSuccessResult(task);
case Remove:
tid = task.getTargetTorrent().getUniqueID();
removeTask(tid);
removeTask(log, tid);
return new DaemonTaskSuccessResult(task);
case Pause:
tid = task.getTargetTorrent().getUniqueID();
pauseTask(tid);
pauseTask(log, tid);
return new DaemonTaskSuccessResult(task);
case PauseAll:
pauseAllTasks();
pauseAllTasks(log);
return new DaemonTaskSuccessResult(task);
case Resume:
tid = task.getTargetTorrent().getUniqueID();
resumeTask(tid);
resumeTask(log, tid);
return new DaemonTaskSuccessResult(task);
case ResumeAll:
resumeAllTasks();
resumeAllTasks(log);
return new DaemonTaskSuccessResult(task);
case SetDownloadLocation:
return null;
@ -124,9 +124,9 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -124,9 +124,9 @@ public class SynologyAdapter implements IDaemonAdapter {
return null;
case SetTransferRates:
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
int uploadRate = ratesTask.getUploadRate() == null ? 0 : ratesTask.getUploadRate().intValue();
int downloadRate = ratesTask.getDownloadRate() == null ? 0 : ratesTask.getDownloadRate().intValue();
setTransferRates(uploadRate, downloadRate);
int uploadRate = ratesTask.getUploadRate() == null ? 0 : ratesTask.getUploadRate();
int downloadRate = ratesTask.getDownloadRate() == null ? 0 : ratesTask.getDownloadRate();
setTransferRates(log, uploadRate, downloadRate);
return new DaemonTaskSuccessResult(task);
case SetAlternativeMode:
default:
@ -149,84 +149,87 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -149,84 +149,87 @@ public class SynologyAdapter implements IDaemonAdapter {
// Synology API
private String login() throws DaemonException {
DLog.d(LOG_NAME, "login()");
private String login(Log log) throws DaemonException {
log.d(LOG_NAME, "login()");
try {
return new SynoRequest(
"auth.cgi",
"SYNO.API.Auth",
"2"
).get("&method=login&account=" + settings.getUsername() + "&passwd=" + settings.getPassword() + "&session=DownloadStation&format=sid"
).getData().getString("sid");
return new SynoRequest("auth.cgi", "SYNO.API.Auth", "2")
.get("&method=login&account=" + settings.getUsername() + "&passwd=" + settings.getPassword() +
"&session=DownloadStation&format=sid").getData(log).getString("sid");
} catch (JSONException e) {
throw new DaemonException(ExceptionType.ParsingFailed, e.toString());
}
}
private void setTransferRates(int uploadRate, int downloadRate) throws DaemonException {
authGet("SYNO.DownloadStation.Info", "1", "DownloadStation/info.cgi",
"&method=setserverconfig&bt_max_upload=" + uploadRate + "&bt_max_download=" + downloadRate).ensureSuccess();
private void setTransferRates(Log log, int uploadRate, int downloadRate) throws DaemonException {
authGet(log, "SYNO.DownloadStation.Info", "1", "DownloadStation/info.cgi",
"&method=setserverconfig&bt_max_upload=" + uploadRate + "&bt_max_download=" + downloadRate)
.ensureSuccess(log);
}
private void createTask(String uri) throws DaemonException {
private void createTask(Log log, String uri) throws DaemonException {
try {
authGet("SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi", "&method=create&uri=" + URLEncoder.encode(uri, "UTF-8")).ensureSuccess();
authGet(log, "SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi",
"&method=create&uri=" + URLEncoder.encode(uri, "UTF-8")).ensureSuccess(log);
} catch (UnsupportedEncodingException e) {
// Never happens
throw new DaemonException(ExceptionType.UnexpectedResponse, e.toString());
}
}
private void removeTask(String tid) throws DaemonException {
private void removeTask(Log log, String tid) throws DaemonException {
List<String> tids = new ArrayList<String>();
tids.add(tid);
removeTasks(tids);
removeTasks(log, tids);
}
private void pauseTask(String tid) throws DaemonException {
private void pauseTask(Log log, String tid) throws DaemonException {
List<String> tids = new ArrayList<String>();
tids.add(tid);
pauseTasks(tids);
pauseTasks(log, tids);
}
private void resumeTask(String tid) throws DaemonException {
private void resumeTask(Log log, String tid) throws DaemonException {
List<String> tids = new ArrayList<String>();
tids.add(tid);
resumeTasks(tids);
resumeTasks(log, tids);
}
private void pauseAllTasks() throws DaemonException {
private void pauseAllTasks(Log log) throws DaemonException {
List<String> tids = new ArrayList<String>();
for (Torrent torrent: tasksList()) {
for (Torrent torrent : tasksList(log)) {
tids.add(torrent.getUniqueID());
}
pauseTasks(tids);
pauseTasks(log, tids);
}
private void resumeAllTasks() throws DaemonException {
private void resumeAllTasks(Log log) throws DaemonException {
List<String> tids = new ArrayList<String>();
for (Torrent torrent: tasksList()) {
for (Torrent torrent : tasksList(log)) {
tids.add(torrent.getUniqueID());
}
resumeTasks(tids);
resumeTasks(log, tids);
}
private void removeTasks(List<String> tids) throws DaemonException {
authGet("SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi", "&method=delete&id=" + Collections2.joinString(tids, ",") + "").ensureSuccess();
private void removeTasks(Log log, List<String> tids) throws DaemonException {
authGet(log, "SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi",
"&method=delete&id=" + Collections2.joinString(tids, ",") + "").ensureSuccess(log);
}
private void pauseTasks(List<String> tids) throws DaemonException {
authGet("SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi", "&method=pause&id=" + Collections2.joinString(tids, ",")).ensureSuccess();
private void pauseTasks(Log log, List<String> tids) throws DaemonException {
authGet(log, "SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi",
"&method=pause&id=" + Collections2.joinString(tids, ",")).ensureSuccess(log);
}
private void resumeTasks(List<String> tids) throws DaemonException {
authGet("SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi", "&method=resume&id=" + Collections2.joinString(tids, ",")).ensureSuccess();
private void resumeTasks(Log log, List<String> tids) throws DaemonException {
authGet(log, "SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi",
"&method=resume&id=" + Collections2.joinString(tids, ",")).ensureSuccess(log);
}
private List<Torrent> tasksList() throws DaemonException {
private List<Torrent> tasksList(Log log) throws DaemonException {
try {
JSONArray jsonTasks = authGet("SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi", "&method=list&additional=detail,transfer,tracker").getData().getJSONArray("tasks");
DLog.d(LOG_NAME, "Tasks = " + jsonTasks.toString());
JSONArray jsonTasks = authGet(log, "SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi",
"&method=list&additional=detail,transfer,tracker").getData(log).getJSONArray("tasks");
log.d(LOG_NAME, "Tasks = " + jsonTasks.toString());
List<Torrent> result = new ArrayList<Torrent>();
for (int i = 0; i < jsonTasks.length(); i++) {
result.add(parseTorrent(i, jsonTasks.getJSONObject(i)));
@ -237,16 +240,21 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -237,16 +240,21 @@ public class SynologyAdapter implements IDaemonAdapter {
}
}
private List<TorrentFile> fileList(String torrentId) throws DaemonException {
private List<TorrentFile> fileList(Log log, String torrentId) throws DaemonException {
try {
List<TorrentFile> result = new ArrayList<TorrentFile>();
JSONObject jsonTask = authGet("SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi", "&method=getinfo&id=" + torrentId + "&additional=detail,transfer,tracker,file").getData().getJSONArray("tasks").getJSONObject(0);
DLog.d(LOG_NAME, "File list = " + jsonTask.toString());
JSONObject jsonTask = authGet(log, "SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi",
"&method=getinfo&id=" + torrentId + "&additional=detail,transfer,tracker,file").getData(log)
.getJSONArray("tasks").getJSONObject(0);
log.d(LOG_NAME, "File list = " + jsonTask.toString());
JSONObject additional = jsonTask.getJSONObject("additional");
if (!additional.has("file")) return result;
if (!additional.has("file")) {
return result;
}
JSONArray files = additional.getJSONArray("file");
for (int i = 0; i < files.length(); i++) {
JSONObject task = files.getJSONObject(i);
// @formatter:off
result.add(new TorrentFile(
task.getString("filename"),
task.getString("filename"),
@ -255,6 +263,7 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -255,6 +263,7 @@ public class SynologyAdapter implements IDaemonAdapter {
task.getLong("size"),
task.getLong("size_downloaded"),
priority(task.getString("priority"))
// @formatter:on
));
}
return result;
@ -263,11 +272,13 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -263,11 +272,13 @@ public class SynologyAdapter implements IDaemonAdapter {
}
}
private TorrentDetails torrentDetails(String torrentId) throws DaemonException {
private TorrentDetails torrentDetails(Log log, String torrentId) throws DaemonException {
List<String> trackers = new ArrayList<String>();
List<String> errors = new ArrayList<String>();
try {
JSONObject jsonTorrent = authGet("SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi", "&method=getinfo&id=" + torrentId + "&additional=tracker").getData().getJSONArray("tasks").getJSONObject(0);
JSONObject jsonTorrent = authGet(log, "SYNO.DownloadStation.Task", "1", "DownloadStation/task.cgi",
"&method=getinfo&id=" + torrentId + "&additional=tracker").getData(log).getJSONArray("tasks")
.getJSONObject(0);
JSONObject additional = jsonTorrent.getJSONObject("additional");
if (additional.has("tracker")) {
JSONArray tracker = additional.getJSONArray("tracker");
@ -293,7 +304,7 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -293,7 +304,7 @@ public class SynologyAdapter implements IDaemonAdapter {
long downloaded = transfer.getLong("size_downloaded");
int speed = transfer.getInt("speed_download");
long size = jsonTorrent.getLong("size");
Float eta = new Float(size - downloaded) / speed;
Float eta = Float.valueOf(size - downloaded) / speed;
int totalSeeders = 0;
int totalLeechers = 0;
if (additional.has("tracker")) {
@ -306,6 +317,7 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -306,6 +317,7 @@ public class SynologyAdapter implements IDaemonAdapter {
}
}
}
// @formatter:off
return new Torrent(
id,
jsonTorrent.getString("id"),
@ -322,79 +334,71 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -322,79 +334,71 @@ public class SynologyAdapter implements IDaemonAdapter {
downloaded,
transfer.getLong("size_uploaded"),
size,
(size == 0) ? 0 : (new Float(downloaded) / size),
(size == 0) ? 0 : (Float.valueOf(downloaded) / size),
0,
jsonTorrent.getString("title"),
new Date(detail.getLong("create_time") * 1000),
null,
"",
settings.getType()
// @formatter:on
);
}
private TorrentStatus torrentStatus(String status) {
if ("downloading".equals(status)) return TorrentStatus.Downloading;
if ("seeding".equals(status)) return TorrentStatus.Seeding;
if ("finished".equals(status)) return TorrentStatus.Paused;
if ("finishing".equals(status)) return TorrentStatus.Paused;
if ("waiting".equals(status)) return TorrentStatus.Waiting;
if ("paused".equals(status)) return TorrentStatus.Paused;
if ("error".equals(status)) return TorrentStatus.Error;
if ("downloading".equals(status)) {
return TorrentStatus.Downloading;
}
if ("seeding".equals(status)) {
return TorrentStatus.Seeding;
}
if ("finished".equals(status)) {
return TorrentStatus.Paused;
}
if ("finishing".equals(status)) {
return TorrentStatus.Paused;
}
if ("waiting".equals(status)) {
return TorrentStatus.Waiting;
}
if ("paused".equals(status)) {
return TorrentStatus.Paused;
}
if ("error".equals(status)) {
return TorrentStatus.Error;
}
return TorrentStatus.Unknown;
}
private Priority priority(String priority) {
if ("low".equals(priority)) return Priority.Low;
if ("normal".equals(priority)) return Priority.Normal;
if ("high".equals(priority)) return Priority.High;
if ("low".equals(priority)) {
return Priority.Low;
}
if ("normal".equals(priority)) {
return Priority.Normal;
}
if ("high".equals(priority)) {
return Priority.High;
}
return Priority.Off;
}
/**
* Authenticated GET. If no session open, a login authGet will be done before-hand.
*/
private SynoResponse authGet(String api, String version, String path, String params) throws DaemonException {
private SynoResponse authGet(Log log, String api, String version, String path, String params)
throws DaemonException {
if (sid == null) {
sid = login();
sid = login(log);
}
return new SynoRequest(path, api, version).get(params + "&_sid=" + sid);
}
private DefaultHttpClient getHttpClient() throws DaemonException {
if (httpClient == null)
if (httpClient == null) {
httpClient = HttpHelper.createStandardHttpClient(settings, true);
return httpClient;
}
private class SynoRequest {
private final String path;
private final String api;
private final String version;
public SynoRequest(String path, String api, String version) {
this.path = path;
this.api = api;
this.version = version;
}
public SynoResponse get(String params) throws DaemonException {
try {
return new SynoResponse(getHttpClient().execute(new HttpGet(buildURL(params))));
} catch (IOException e) {
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private String buildURL(String params) {
return (settings.getSsl() ? "https://" : "http://")
+ settings.getAddress()
+ ":" + settings.getPort()
+ "/webapi/" + path
+ "?api=" + api
+ "&version=" + version
+ params;
}
return httpClient;
}
private static class SynoResponse {
@ -405,13 +409,13 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -405,13 +409,13 @@ public class SynologyAdapter implements IDaemonAdapter {
this.response = response;
}
public JSONObject getData() throws DaemonException {
JSONObject json = getJson();
public JSONObject getData(Log log) throws DaemonException {
JSONObject json = getJson(log);
try {
if (json.getBoolean("success")) {
return json.getJSONObject("data");
} else {
DLog.e(LOG_NAME, "not a success: " + json.toString());
log.e(LOG_NAME, "not a success: " + json.toString());
throw new DaemonException(ExceptionType.AuthenticationFailure, json.getString("error"));
}
} catch (JSONException e) {
@ -419,11 +423,11 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -419,11 +423,11 @@ public class SynologyAdapter implements IDaemonAdapter {
}
}
public JSONObject getJson() throws DaemonException {
public JSONObject getJson(Log log) throws DaemonException {
try {
HttpEntity entity = response.getEntity();
if (entity == null) {
DLog.e(LOG_NAME, "Error: No entity in HTTP response");
log.e(LOG_NAME, "Error: No entity in HTTP response");
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response.");
}
// Read JSON response
@ -436,16 +440,17 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -436,16 +440,17 @@ public class SynologyAdapter implements IDaemonAdapter {
} catch (JSONException e) {
throw new DaemonException(ExceptionType.UnexpectedResponse, "Bad JSON");
} catch (IOException e) {
DLog.e(LOG_NAME, "getJson error: " + e.toString());
log.e(LOG_NAME, "getJson error: " + e.toString());
throw new DaemonException(ExceptionType.AuthenticationFailure, e.toString());
}
}
public void ensureSuccess() throws DaemonException {
JSONObject json = getJson();
public void ensureSuccess(Log log) throws DaemonException {
JSONObject json = getJson(log);
try {
if (!json.getBoolean("success"))
if (!json.getBoolean("success")) {
throw new DaemonException(ExceptionType.UnexpectedResponse, json.getString("error"));
}
} catch (JSONException e) {
throw new DaemonException(ExceptionType.ParsingFailed, e.toString());
}
@ -453,4 +458,30 @@ public class SynologyAdapter implements IDaemonAdapter { @@ -453,4 +458,30 @@ public class SynologyAdapter implements IDaemonAdapter {
}
private class SynoRequest {
private final String path;
private final String api;
private final String version;
public SynoRequest(String path, String api, String version) {
this.path = path;
this.api = api;
this.version = version;
}
public SynoResponse get(String params) throws DaemonException {
try {
return new SynoResponse(getHttpClient().execute(new HttpGet(buildURL(params))));
} catch (IOException e) {
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private String buildURL(String params) {
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() +
"/webapi/" + path + "?api=" + api + "&version=" + version + params;
}
}
}

181
app/src/main/java/org/transdroid/daemon/TaskQueue.java

@ -1,181 +0,0 @@ @@ -1,181 +0,0 @@
/*
* This file is part of Transdroid <http://www.transdroid.org>
*
* Transdroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Transdroid is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.transdroid.daemon;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import org.transdroid.daemon.task.DaemonTask;
import org.transdroid.daemon.task.DaemonTaskFailureResult;
import org.transdroid.daemon.task.DaemonTaskResult;
import org.transdroid.daemon.task.DaemonTaskSuccessResult;
import org.transdroid.daemon.util.DLog;
public class TaskQueue implements Runnable {
private static final String LOG_NAME = "Queue";
private List<DaemonTask> queue;
private Thread worker;
private IDaemonCallback callback;
private boolean paused;;
public TaskQueue(IDaemonCallback callback) {
queue = Collections.synchronizedList(new LinkedList<DaemonTask>());
paused = true;
this.callback = callback;
worker = new Thread(this);
worker.start();
}
/**
* Enqueue a single new task to later perform.
* @param task The task to add to the queue
*/
public synchronized void enqueue(DaemonTask task) {
queue.add(task);
notifyAll();
}
/**
* Queues an old set of tasks again in the queue. This for example can be
* used to restore on old queue after an Activity was destroyed and
* is restored again.
* @param tasks A list of daemon tasks to queue
*/
public synchronized void requeue(List<DaemonTask> tasks) {
queue.addAll(tasks);
notifyAll();
}
/**
* Removes all remaining tasks from the queue. Existing operations are still
* continued and their results posted back.
*/
public synchronized void clear() {
queue.clear();
notifyAll();
}
/**
* Removes all remaining tasks from the queue that are of some specific type.
* Other remaining tasks will still be executed and running operations are
* still continued and their results posted back.
* @param class1
*/
public synchronized void clear(DaemonMethod ofType) {
Iterator<DaemonTask> task = queue.iterator();
while (task.hasNext()) {
if (task.next().getMethod() == ofType) {
task.remove();
}
}
notifyAll();
}
/**
* Returns a copy of the queue with all remaining tasks. This can be used
* to save them on an Activity destroy and restore them later using
* requeue().
* @return A list containing all remaining tasks
*/
public Queue<DaemonTask> getRemainingTasks() {
return new LinkedList<DaemonTask>(queue);
}
/**
* Request the task perfoming thread to stop all activity
*/
public synchronized void requestStop() {
paused = true;
}
/**
* Request
*/
public synchronized void start() {
paused = false;
notify();
}
@Override
public void run() {
while (true) {
if (this.paused) {
// We are going to pause
DLog.d(LOG_NAME, "Task queue pausing");
}
synchronized (this) {
while (this.paused || queue.isEmpty()) {
try {
// We are going to run again if wait() succeeded (on notify())
wait();
DLog.d(LOG_NAME, "Task queue resuming");
} catch (Exception e) {
}
}
}
processTask();
if (queue.isEmpty()) {
callback.onQueueEmpty();
// We are going to pause
DLog.d(LOG_NAME, "Task queue pausing (queue empty)");
}
}
}
private void processTask() {
// Get the task to execute
DaemonTask task = queue.remove(0);
if (task == null) {
return;
}
if (callback.isAttached())
callback.onQueuedTaskStarted(task);
// Ask the daemon adapter to perform the task (which does it synchronously)
DLog.d(LOG_NAME, "Starting task: " + task.toString());
DaemonTaskResult result = task.execute();
if (callback.isAttached())
callback.onQueuedTaskFinished(task);
// Return the result (to the UI thread)
DLog.d(LOG_NAME, "Task result: " + (result == null? "null": result.toString()));
if (result != null && !this.paused && callback.isAttached()) {
if (result.wasSuccessful()) {
callback.onTaskSuccess((DaemonTaskSuccessResult) result);
} else {
callback.onTaskFailure((DaemonTaskFailureResult) result);
}
}
}
}

39
app/src/main/java/org/transdroid/daemon/Tfb4rt/Tfb4rtAdapter.java

@ -30,6 +30,7 @@ import org.apache.http.HttpResponse; @@ -30,6 +30,7 @@ import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonSettings;
@ -45,7 +46,6 @@ import org.transdroid.daemon.task.DaemonTaskSuccessResult; @@ -45,7 +46,6 @@ import org.transdroid.daemon.task.DaemonTaskSuccessResult;
import org.transdroid.daemon.task.RemoveTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
@ -80,59 +80,59 @@ public class Tfb4rtAdapter implements IDaemonAdapter { @@ -80,59 +80,59 @@ public class Tfb4rtAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
return new RetrieveTaskSuccessResult((RetrieveTask) task, makeStatsRequest(), null);
return new RetrieveTaskSuccessResult((RetrieveTask) task, makeStatsRequest(log), null);
case AddByFile:
// Add a torrent to the server by sending the contents of a local .torrent file
String file = ((AddByFileTask) task).getFile();
makeFileUploadRequest("fileUpload", file);
makeFileUploadRequest(log, "fileUpload", file);
return null;
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeActionRequest("urlUpload", url);
makeActionRequest(log, "urlUpload", url);
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeActionRequest((removeTask.includingData() ? "deleteWithData" : "delete"), task.getTargetTorrent()
makeActionRequest(log, (removeTask.includingData() ? "deleteWithData" : "delete"), task.getTargetTorrent()
.getUniqueID());
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
makeActionRequest("stop", task.getTargetTorrent().getUniqueID());
makeActionRequest(log, "stop", task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Pause all torrents
makeActionRequest("bulkStop", null);
makeActionRequest(log, "bulkStop", null);
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeActionRequest("start", task.getTargetTorrent().getUniqueID());
makeActionRequest(log, "start", task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeActionRequest("bulkStart", null);
makeActionRequest(log, "bulkStart", null);
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
@ -150,7 +150,7 @@ public class Tfb4rtAdapter implements IDaemonAdapter { @@ -150,7 +150,7 @@ public class Tfb4rtAdapter implements IDaemonAdapter {
}
}
private List<Torrent> makeStatsRequest() throws DaemonException {
private List<Torrent> makeStatsRequest(Log log) throws DaemonException {
try {
@ -170,16 +170,16 @@ public class Tfb4rtAdapter implements IDaemonAdapter { @@ -170,16 +170,16 @@ public class Tfb4rtAdapter implements IDaemonAdapter {
return torrents;
} catch (DaemonException e) {
DLog.d(LOG_NAME, "Parsing error: " + e.toString());
log.d(LOG_NAME, "Parsing error: " + e.toString());
throw e;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private boolean makeActionRequest(String action, String target) throws DaemonException {
private boolean makeActionRequest(Log log, String action, String target) throws DaemonException {
try {
@ -204,16 +204,16 @@ public class Tfb4rtAdapter implements IDaemonAdapter { @@ -204,16 +204,16 @@ public class Tfb4rtAdapter implements IDaemonAdapter {
}
} catch (DaemonException e) {
DLog.d(LOG_NAME, action + " request error: " + e.toString());
log.d(LOG_NAME, action + " request error: " + e.toString());
throw e;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private boolean makeFileUploadRequest(String action, String target) throws DaemonException {
private boolean makeFileUploadRequest(Log log, String action, String target) throws DaemonException {
try {
@ -241,10 +241,10 @@ public class Tfb4rtAdapter implements IDaemonAdapter { @@ -241,10 +241,10 @@ public class Tfb4rtAdapter implements IDaemonAdapter {
}
} catch (DaemonException e) {
DLog.d(LOG_NAME, action + " request error: " + e.toString());
log.d(LOG_NAME, action + " request error: " + e.toString());
throw e;
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
@ -252,7 +252,6 @@ public class Tfb4rtAdapter implements IDaemonAdapter { @@ -252,7 +252,6 @@ public class Tfb4rtAdapter implements IDaemonAdapter {
/**
* Instantiates an HTTP client that can be used for all Torrentflux-b4rt requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On conflicting or missing settings
*/
private void initialise() throws DaemonException {

4
app/src/main/java/org/transdroid/daemon/TorrentFilesComparator.java

@ -49,7 +49,7 @@ public class TorrentFilesComparator implements Comparator<TorrentFile> { @@ -49,7 +49,7 @@ public class TorrentFilesComparator implements Comparator<TorrentFile> {
case PartDone:
return Float.compare(file1.getPartDone(), file2.getPartDone());
case TotalSize:
return new Long(file1.getTotalSize()).compareTo(file2.getTotalSize());
return Long.valueOf(file1.getTotalSize()).compareTo(file2.getTotalSize());
default:
return alphanumComparator.compare(file1.getName().toLowerCase(), file2.getName().toLowerCase());
}
@ -58,7 +58,7 @@ public class TorrentFilesComparator implements Comparator<TorrentFile> { @@ -58,7 +58,7 @@ public class TorrentFilesComparator implements Comparator<TorrentFile> {
case PartDone:
return 0 - Float.compare(file1.getPartDone(), file2.getPartDone());
case TotalSize:
return 0 - new Long(file1.getTotalSize()).compareTo(file2.getTotalSize());
return 0 - Long.valueOf(file1.getTotalSize()).compareTo(file2.getTotalSize());
default:
return 0 - alphanumComparator.compare(file1.getName().toLowerCase(), file2.getName().toLowerCase());
}

20
app/src/main/java/org/transdroid/daemon/TorrentsComparator.java

@ -67,15 +67,15 @@ public class TorrentsComparator implements Comparator<Torrent> { @@ -67,15 +67,15 @@ public class TorrentsComparator implements Comparator<Torrent> {
case DateDone:
return tor1.getDateDone().compareTo(tor2.getDateDone());
case Percent:
return new Float(tor1.getDownloadedPercentage()).compareTo(new Float(tor2.getDownloadedPercentage()));
return Float.valueOf(tor1.getDownloadedPercentage()).compareTo(tor2.getDownloadedPercentage());
case DownloadSpeed:
return new Integer(tor1.getRateDownload()).compareTo(new Integer(tor2.getRateDownload()));
return Integer.valueOf(tor1.getRateDownload()).compareTo(tor2.getRateDownload());
case UploadSpeed:
return new Integer(tor1.getRateUpload()).compareTo(new Integer(tor2.getRateUpload()));
return Integer.valueOf(tor1.getRateUpload()).compareTo(tor2.getRateUpload());
case Ratio:
return new Double(tor1.getRatio()).compareTo(new Double(tor2.getRatio()));
return Double.valueOf(tor1.getRatio()).compareTo(tor2.getRatio());
case Size:
return new Double(tor1.getTotalSize()).compareTo(new Double(tor2.getTotalSize()));
return Double.valueOf(tor1.getTotalSize()).compareTo((double) tor2.getTotalSize());
default:
return alphanumComparator.compare(tor1.getName().toLowerCase(), tor2.getName().toLowerCase());
}
@ -92,15 +92,15 @@ public class TorrentsComparator implements Comparator<Torrent> { @@ -92,15 +92,15 @@ public class TorrentsComparator implements Comparator<Torrent> {
case DateDone:
return 0 - tor1.getDateDone().compareTo(tor2.getDateDone());
case Percent:
return 0 - (new Float(tor1.getDownloadedPercentage()).compareTo(new Float(tor2.getDownloadedPercentage())));
return 0 - (Float.valueOf(tor1.getDownloadedPercentage()).compareTo(tor2.getDownloadedPercentage()));
case DownloadSpeed:
return 0 - (new Integer(tor1.getRateDownload()).compareTo(new Integer(tor2.getRateDownload())));
return 0 - (Integer.valueOf(tor1.getRateDownload()).compareTo(tor2.getRateDownload()));
case UploadSpeed:
return 0 - (new Integer(tor1.getRateUpload()).compareTo(new Integer(tor2.getRateUpload())));
return 0 - (Integer.valueOf(tor1.getRateUpload()).compareTo(tor2.getRateUpload()));
case Ratio:
return 0 - new Double(tor1.getRatio()).compareTo(new Double(tor2.getRatio()));
return 0 - Double.valueOf(tor1.getRatio()).compareTo(tor2.getRatio());
case Size:
return 0 - new Double(tor1.getTotalSize()).compareTo(new Double(tor2.getTotalSize()));
return 0 - Double.valueOf(tor1.getTotalSize()).compareTo((double) tor2.getTotalSize());
default:
return 0 - alphanumComparator.compare(tor1.getName().toLowerCase(), tor2.getName().toLowerCase());
}

621
app/src/main/java/org/transdroid/daemon/Transmission/TransmissionAdapter.java

@ -17,16 +17,6 @@ @@ -17,16 +17,6 @@
*/
package org.transdroid.daemon.Transmission;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
@ -37,6 +27,7 @@ import org.base64.android.Base64.InputStream; @@ -37,6 +27,7 @@ import org.base64.android.Base64.InputStream;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
@ -54,6 +45,7 @@ import org.transdroid.daemon.task.DaemonTask; @@ -54,6 +45,7 @@ import org.transdroid.daemon.task.DaemonTask;
import org.transdroid.daemon.task.DaemonTaskFailureResult;
import org.transdroid.daemon.task.DaemonTaskResult;
import org.transdroid.daemon.task.DaemonTaskSuccessResult;
import org.transdroid.daemon.task.ForceRecheckTask;
import org.transdroid.daemon.task.GetFileListTask;
import org.transdroid.daemon.task.GetFileListTaskSuccessResult;
import org.transdroid.daemon.task.GetStatsTask;
@ -69,21 +61,28 @@ import org.transdroid.daemon.task.SetAlternativeModeTask; @@ -69,21 +61,28 @@ import org.transdroid.daemon.task.SetAlternativeModeTask;
import org.transdroid.daemon.task.SetDownloadLocationTask;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* The daemon adapter from the Transmission torrent client.
*
* @author erickok
*
*/
public class TransmissionAdapter implements IDaemonAdapter {
private static final String LOG_NAME = "Transdroid daemon";
private static final int FOR_ALL = -1;
private static final String RPC_ID = "id";
private static final String RPC_NAME = "name";
private static final String RPC_STATUS = "status";
@ -104,234 +103,255 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -104,234 +103,255 @@ public class TransmissionAdapter implements IDaemonAdapter {
private static final String RPC_DATEDONE = "doneDate";
private static final String RPC_AVAILABLE = "desiredAvailable";
private static final String RPC_COMMENT = "comment";
private static final String RPC_FILE_NAME = "name";
private static final String RPC_FILE_LENGTH = "length";
private static final String RPC_FILE_COMPLETED = "bytesCompleted";
private static final String RPC_FILESTAT_WANTED = "wanted";
private static final String RPC_FILESTAT_PRIORITY = "priority";
private static String sessionToken;
private DaemonSettings settings;
private DefaultHttpClient httpclient;
private static String sessionToken;
private long rpcVersion = -1;
public TransmissionAdapter(DaemonSettings settings) {
this.settings = settings;
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
// Get the server version
if (rpcVersion <= -1) {
// Get server session statistics
JSONObject response = makeRequest(buildRequestObject("session-get", new JSONObject()));
JSONObject response = makeRequest(log, buildRequestObject("session-get", new JSONObject()));
rpcVersion = response.getJSONObject("arguments").getInt("rpc-version");
}
JSONObject request = new JSONObject();
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
JSONArray fields = new JSONArray();
final String[] fieldsArray = new String[] { RPC_ID, RPC_NAME, RPC_ERROR, RPC_ERRORSTRING, RPC_STATUS,
RPC_DOWNLOADDIR, RPC_RATEDOWNLOAD, RPC_RATEUPLOAD, RPC_PEERSGETTING, RPC_PEERSSENDING,
RPC_PEERSCONNECTED, RPC_ETA, RPC_DOWNLOADSIZE1, RPC_DOWNLOADSIZE2, RPC_UPLOADEDEVER,
RPC_TOTALSIZE, RPC_DATEADDED, RPC_DATEDONE, RPC_AVAILABLE, RPC_COMMENT };
for (String field : fieldsArray) {
fields.put(field);
}
request.put("fields", fields);
JSONObject result = makeRequest(buildRequestObject("torrent-get", request));
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonRetrieveTorrents(result.getJSONObject("arguments")),null);
case GetStats:
// Request the current server statistics
JSONObject stats = makeRequest(buildRequestObject("session-get", new JSONObject())).getJSONObject("arguments");
return new GetStatsTaskSuccessResult((GetStatsTask) task, stats.getBoolean("alt-speed-enabled"),
rpcVersion >= 12? stats.getLong("download-dir-free-space"): -1);
case GetTorrentDetails:
// Request fine details of a specific torrent
JSONArray dfields = new JSONArray();
dfields.put("trackers");
dfields.put("trackerStats");
JSONObject buildDGet = buildTorrentRequestObject(task.getTargetTorrent().getUniqueID(), null, false);
buildDGet.put("fields", dfields);
JSONObject getDResult = makeRequest(buildRequestObject("torrent-get", buildDGet));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, parseJsonTorrentDetails(getDResult.getJSONObject("arguments")));
case GetFileList:
// Request all details for a specific torrent
JSONArray ffields = new JSONArray();
ffields.put("files");
ffields.put("fileStats");
JSONObject buildGet = buildTorrentRequestObject(task.getTargetTorrent().getUniqueID(), null, false);
buildGet.put("fields", ffields);
JSONObject getResult = makeRequest(buildRequestObject("torrent-get", buildGet));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFileList(getResult.getJSONObject("arguments"), task.getTargetTorrent()));
case AddByFile:
// Add a torrent to the server by sending the contents of a local .torrent file
String file = ((AddByFileTask)task).getFile();
// Encode the .torrent file's data
InputStream in = new Base64.InputStream(new FileInputStream(new File(URI.create(file))), Base64.ENCODE);
StringWriter writer = new StringWriter();
int c;
while ((c = in.read())!= -1) {
writer.write(c);
}
in.close();
// Request to add a torrent by Base64-encoded meta data
request.put("metainfo", writer.toString());
makeRequest(buildRequestObject("torrent-add", request));
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask)task).getUrl();
request.put("filename", url);
makeRequest(buildRequestObject("torrent-add", request));
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask)task).getUrl();
request.put("filename", magnet);
makeRequest(buildRequestObject("torrent-add", request));
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(buildRequestObject("torrent-remove", buildTorrentRequestObject(removeTask.getTargetTorrent().getUniqueID(), "delete-local-data", removeTask.includingData())));
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
PauseTask pauseTask = (PauseTask) task;
makeRequest(buildRequestObject("torrent-stop", buildTorrentRequestObject(pauseTask.getTargetTorrent().getUniqueID(), null, false)));
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Resume all torrents
makeRequest(buildRequestObject("torrent-stop", buildTorrentRequestObject(FOR_ALL, null, false)));
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
ResumeTask resumeTask = (ResumeTask) task;
makeRequest(buildRequestObject("torrent-start", buildTorrentRequestObject(resumeTask.getTargetTorrent().getUniqueID(), null, false)));
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeRequest(buildRequestObject("torrent-start", buildTorrentRequestObject(FOR_ALL, null, false)));
return new DaemonTaskSuccessResult(task);
case SetDownloadLocation:
// Change the download location
SetDownloadLocationTask sdlTask = (SetDownloadLocationTask) task;
// Build request
JSONObject sdlrequest = new JSONObject();
JSONArray sdlids = new JSONArray();
sdlids.put(Long.parseLong(task.getTargetTorrent().getUniqueID()));
sdlrequest.put("ids", sdlids);
sdlrequest.put("location", sdlTask.getNewLocation());
sdlrequest.put("move", true);
makeRequest(buildRequestObject("torrent-set-location", sdlrequest));
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
// Set priorities of the files of some torrent
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
// Build request
JSONObject prequest = new JSONObject();
JSONArray ids = new JSONArray();
ids.put(Long.parseLong(task.getTargetTorrent().getUniqueID()));
prequest.put("ids", ids);
JSONArray fileids = new JSONArray();
for (TorrentFile forfile : prioTask.getForFiles()) {
fileids.put(Integer.parseInt(forfile.getKey())); // The keys are the indices of the files, so always numeric
}
switch (prioTask.getNewPriority()) {
case Off:
prequest.put("files-unwanted", fileids);
break;
case Low:
prequest.put("files-wanted", fileids);
prequest.put("priority-low", fileids);
break;
case Normal:
prequest.put("files-wanted", fileids);
prequest.put("priority-normal", fileids);
break;
case High:
prequest.put("files-wanted", fileids);
prequest.put("priority-high", fileids);
break;
}
makeRequest(buildRequestObject("torrent-set", prequest));
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
if (ratesTask.getUploadRate() == null) {
request.put("speed-limit-up-enabled", false);
} else {
request.put("speed-limit-up-enabled", true);
request.put("speed-limit-up", ratesTask.getUploadRate().intValue());
}
if (ratesTask.getDownloadRate() == null) {
request.put("speed-limit-down-enabled", false);
} else {
request.put("speed-limit-down-enabled", true);
request.put("speed-limit-down", ratesTask.getDownloadRate().intValue());
}
case Retrieve:
// Request all torrents from server
JSONArray fields = new JSONArray();
final String[] fieldsArray =
new String[]{RPC_ID, RPC_NAME, RPC_ERROR, RPC_ERRORSTRING, RPC_STATUS, RPC_DOWNLOADDIR,
RPC_RATEDOWNLOAD, RPC_RATEUPLOAD, RPC_PEERSGETTING, RPC_PEERSSENDING,
RPC_PEERSCONNECTED, RPC_ETA, RPC_DOWNLOADSIZE1, RPC_DOWNLOADSIZE2, RPC_UPLOADEDEVER,
RPC_TOTALSIZE, RPC_DATEADDED, RPC_DATEDONE, RPC_AVAILABLE, RPC_COMMENT};
for (String field : fieldsArray) {
fields.put(field);
}
request.put("fields", fields);
JSONObject result = makeRequest(log, buildRequestObject("torrent-get", request));
return new RetrieveTaskSuccessResult((RetrieveTask) task,
parseJsonRetrieveTorrents(result.getJSONObject("arguments")), null);
case GetStats:
// Request the current server statistics
JSONObject stats = makeRequest(log, buildRequestObject("session-get", new JSONObject()))
.getJSONObject("arguments");
return new GetStatsTaskSuccessResult((GetStatsTask) task, stats.getBoolean("alt-speed-enabled"),
rpcVersion >= 12 ? stats.getLong("download-dir-free-space") : -1);
makeRequest(buildRequestObject("session-set", request));
return new DaemonTaskSuccessResult(task);
case SetAlternativeMode:
// Request to set the alternative speed mode (Tutle Mode)
SetAlternativeModeTask altModeTask = (SetAlternativeModeTask) task;
request.put("alt-speed-enabled", altModeTask.isAlternativeModeEnabled());
makeRequest(buildRequestObject("session-set", request));
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, task.getMethod() + " is not supported by " + getType()));
case GetTorrentDetails:
// Request fine details of a specific torrent
JSONArray dfields = new JSONArray();
dfields.put("trackers");
dfields.put("trackerStats");
JSONObject buildDGet =
buildTorrentRequestObject(task.getTargetTorrent().getUniqueID(), null, false);
buildDGet.put("fields", dfields);
JSONObject getDResult = makeRequest(log, buildRequestObject("torrent-get", buildDGet));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
parseJsonTorrentDetails(getDResult.getJSONObject("arguments")));
case GetFileList:
// Request all details for a specific torrent
JSONArray ffields = new JSONArray();
ffields.put("files");
ffields.put("fileStats");
JSONObject buildGet = buildTorrentRequestObject(task.getTargetTorrent().getUniqueID(), null, false);
buildGet.put("fields", ffields);
JSONObject getResult = makeRequest(log, buildRequestObject("torrent-get", buildGet));
return new GetFileListTaskSuccessResult((GetFileListTask) task,
parseJsonFileList(getResult.getJSONObject("arguments"), task.getTargetTorrent()));
case AddByFile:
// Add a torrent to the server by sending the contents of a local .torrent file
String file = ((AddByFileTask) task).getFile();
// Encode the .torrent file's data
InputStream in =
new Base64.InputStream(new FileInputStream(new File(URI.create(file))), Base64.ENCODE);
StringWriter writer = new StringWriter();
int c;
while ((c = in.read()) != -1) {
writer.write(c);
}
in.close();
// Request to add a torrent by Base64-encoded meta data
request.put("metainfo", writer.toString());
makeRequest(log, buildRequestObject("torrent-add", request));
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
request.put("filename", url);
makeRequest(log, buildRequestObject("torrent-add", request));
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
request.put("filename", magnet);
makeRequest(log, buildRequestObject("torrent-add", request));
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(log, buildRequestObject("torrent-remove",
buildTorrentRequestObject(removeTask.getTargetTorrent().getUniqueID(), "delete-local-data",
removeTask.includingData())));
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
PauseTask pauseTask = (PauseTask) task;
makeRequest(log, buildRequestObject("torrent-stop",
buildTorrentRequestObject(pauseTask.getTargetTorrent().getUniqueID(), null, false)));
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Resume all torrents
makeRequest(log,
buildRequestObject("torrent-stop", buildTorrentRequestObject(FOR_ALL, null, false)));
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
ResumeTask resumeTask = (ResumeTask) task;
makeRequest(log, buildRequestObject("torrent-start",
buildTorrentRequestObject(resumeTask.getTargetTorrent().getUniqueID(), null, false)));
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeRequest(log,
buildRequestObject("torrent-start", buildTorrentRequestObject(FOR_ALL, null, false)));
return new DaemonTaskSuccessResult(task);
case SetDownloadLocation:
// Change the download location
SetDownloadLocationTask sdlTask = (SetDownloadLocationTask) task;
// Build request
JSONObject sdlrequest = new JSONObject();
JSONArray sdlids = new JSONArray();
sdlids.put(Long.parseLong(task.getTargetTorrent().getUniqueID()));
sdlrequest.put("ids", sdlids);
sdlrequest.put("location", sdlTask.getNewLocation());
sdlrequest.put("move", true);
makeRequest(log, buildRequestObject("torrent-set-location", sdlrequest));
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
// Set priorities of the files of some torrent
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
// Build request
JSONObject prequest = new JSONObject();
JSONArray ids = new JSONArray();
ids.put(Long.parseLong(task.getTargetTorrent().getUniqueID()));
prequest.put("ids", ids);
JSONArray fileids = new JSONArray();
for (TorrentFile forfile : prioTask.getForFiles()) {
fileids.put(Integer.parseInt(
forfile.getKey())); // The keys are the indices of the files, so always numeric
}
switch (prioTask.getNewPriority()) {
case Off:
prequest.put("files-unwanted", fileids);
break;
case Low:
prequest.put("files-wanted", fileids);
prequest.put("priority-low", fileids);
break;
case Normal:
prequest.put("files-wanted", fileids);
prequest.put("priority-normal", fileids);
break;
case High:
prequest.put("files-wanted", fileids);
prequest.put("priority-high", fileids);
break;
}
makeRequest(log, buildRequestObject("torrent-set", prequest));
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
if (ratesTask.getUploadRate() == null) {
request.put("speed-limit-up-enabled", false);
} else {
request.put("speed-limit-up-enabled", true);
request.put("speed-limit-up", ratesTask.getUploadRate().intValue());
}
if (ratesTask.getDownloadRate() == null) {
request.put("speed-limit-down-enabled", false);
} else {
request.put("speed-limit-down-enabled", true);
request.put("speed-limit-down", ratesTask.getDownloadRate().intValue());
}
makeRequest(log, buildRequestObject("session-set", request));
return new DaemonTaskSuccessResult(task);
case SetAlternativeMode:
// Request to set the alternative speed mode (Tutle Mode)
SetAlternativeModeTask altModeTask = (SetAlternativeModeTask) task;
request.put("alt-speed-enabled", altModeTask.isAlternativeModeEnabled());
makeRequest(log, buildRequestObject("session-set", request));
return new DaemonTaskSuccessResult(task);
case ForceRecheck:
// Verify torrent data integrity
ForceRecheckTask verifyTask = (ForceRecheckTask) task;
makeRequest(log, buildRequestObject("torrent-verify",
buildTorrentRequestObject(verifyTask.getTargetTorrent().getUniqueID(), null, false)));
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
@ -343,12 +363,14 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -343,12 +363,14 @@ public class TransmissionAdapter implements IDaemonAdapter {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.FileAccessError, e.toString()));
}
}
private JSONObject buildTorrentRequestObject(String torrentID, String extraKey, boolean extraValue ) throws JSONException {
private JSONObject buildTorrentRequestObject(String torrentID, String extraKey, boolean extraValue)
throws JSONException {
return buildTorrentRequestObject(Long.parseLong(torrentID), extraKey, extraValue);
}
private JSONObject buildTorrentRequestObject(long torrentID, String extraKey, boolean extraValue ) throws JSONException {
private JSONObject buildTorrentRequestObject(long torrentID, String extraKey, boolean extraValue)
throws JSONException {
// Build request for one specific torrent
JSONObject request = new JSONObject();
@ -363,7 +385,7 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -363,7 +385,7 @@ public class TransmissionAdapter implements IDaemonAdapter {
return request;
}
private JSONObject buildRequestObject(String sendMethod, JSONObject arguments) throws JSONException {
// Build request for method
@ -373,90 +395,89 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -373,90 +395,89 @@ public class TransmissionAdapter implements IDaemonAdapter {
request.put("tag", 0);
return request;
}
private synchronized JSONObject makeRequest(JSONObject data) throws DaemonException {
private synchronized JSONObject makeRequest(Log log, JSONObject data) throws DaemonException {
try {
// Initialise the HTTP client
if (httpclient == null) {
initialise();
}
final String sessionHeader = "X-Transmission-Session-Id";
// Setup request using POST stream with URL and data
HttpPost httppost = new HttpPost(buildWebUIUrl());
StringEntity se = new StringEntity(data.toString(), "UTF-8");
httppost.setEntity(se);
// Send the stored session token as a header
if (sessionToken != null) {
httppost.addHeader(sessionHeader, sessionToken);
}
// Execute
DLog.d(LOG_NAME, "Execute " + data.getString("method") + " request to " + httppost.getURI().toString());
log.d(LOG_NAME, "Execute " + data.getString("method") + " request to " + httppost.getURI().toString());
HttpResponse response = httpclient.execute(httppost);
// Authentication error?
if (response.getStatusLine().getStatusCode() == 401) {
if (response.getStatusLine().getStatusCode() == 401) {
throw new DaemonException(ExceptionType.AuthenticationFailure,
"401 HTTP response (username or password incorrect)");
}
// 409 error because of a session id?
if (response.getStatusLine().getStatusCode() == 409) {
// Retry post, but this time with the new session token that was encapsulated in the 409 response
DLog.d(LOG_NAME, "Receive HTTP 409 with new session code; now try again for the actual request");
log.d(LOG_NAME, "Receive HTTP 409 with new session code; now try again for the actual request");
sessionToken = response.getFirstHeader(sessionHeader).getValue();
httppost.addHeader(sessionHeader, sessionToken);
DLog.d(LOG_NAME, "Retry to execute " + data.getString("method") + " request, now with " + sessionHeader
+ ": " + sessionToken);
log.d(LOG_NAME,
"Retry to execute " + data.getString("method") + " request, now with " + sessionHeader + ": " +
sessionToken);
response = httpclient.execute(httppost);
}
HttpEntity entity = response.getEntity();
if (entity != null) {
// Read JSON response
java.io.InputStream instream = entity.getContent();
String result = HttpHelper.convertStreamToString(instream);
DLog.d(LOG_NAME,
"Received content response starting with "
+ (result.length() > 100 ? result.substring(0, 100) + "..." : result));
log.d(LOG_NAME, "Received content response starting with " +
(result.length() > 100 ? result.substring(0, 100) + "..." : result));
JSONObject json = new JSONObject(result);
instream.close();
// Return the JSON object
return json;
}
DLog.d(LOG_NAME, "Error: No entity in HTTP response");
log.d(LOG_NAME, "Error: No entity in HTTP response");
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response.");
} catch (DaemonException e) {
throw e;
} catch (JSONException e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ParsingFailed, e.toString());
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
/**
* Instantiates an HTTP client with proper credentials that can be used for all Transmission requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On conflicting or missing settings
*/
private void initialise() throws DaemonException {
httpclient = HttpHelper.createStandardHttpClient(settings, true);
}
/**
* Build the URL of the Transmission web UI from the user settings.
* @return The URL of the RPC API
@ -467,22 +488,23 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -467,22 +488,23 @@ public class TransmissionAdapter implements IDaemonAdapter {
// Allow the user's folder setting to override /transmission (as per Transmission's rpc-url option)
folder = settings.getFolder().trim();
// Strip any trailing slashes
if (folder.endsWith("/"))
if (folder.endsWith("/")) {
folder = folder.substring(0, folder.length() - 1);
}
}
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() + folder
+ "/rpc";
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() +
folder + "/rpc";
}
private ArrayList<Torrent> parseJsonRetrieveTorrents(JSONObject response) throws JSONException {
// Parse response
ArrayList<Torrent> torrents = new ArrayList<Torrent>();
JSONArray rarray = response.getJSONArray("torrents");
for (int i = 0; i < rarray.length(); i++) {
JSONObject tor = rarray.getJSONObject(i);
// Add the parsed torrent to the list
float have = (float)(tor.getLong(RPC_DOWNLOADSIZE1) + tor.getLong(RPC_DOWNLOADSIZE2));
float have = (float) (tor.getLong(RPC_DOWNLOADSIZE1) + tor.getLong(RPC_DOWNLOADSIZE2));
long total = tor.getLong(RPC_TOTALSIZE);
// Error is a number, see https://trac.transmissionbt.com/browser/trunk/libtransmission/transmission.h#L1747
// We only consider it a real error if it is local (blocking), which is error code 3
@ -490,16 +512,18 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -490,16 +512,18 @@ public class TransmissionAdapter implements IDaemonAdapter {
String errorString = tor.getString(RPC_ERRORSTRING).trim();
String commentString = tor.getString(RPC_COMMENT).trim();
if (!commentString.equals("")) {
errorString = errorString.equals("")? commentString : errorString + "\n" + commentString;
errorString = errorString.equals("") ? commentString : errorString + "\n" + commentString;
}
String locationDir = tor.getString(RPC_DOWNLOADDIR);
if (!locationDir.endsWith(settings.getOS().getPathSeperator()))
if (!locationDir.endsWith(settings.getOS().getPathSeperator())) {
locationDir += settings.getOS().getPathSeperator();
}
// @formatter:off
torrents.add(new Torrent(
tor.getInt(RPC_ID),
null,
tor.getString(RPC_NAME),
hasError? TorrentStatus.Error: getStatus(tor.getInt(RPC_STATUS)),
hasError ? TorrentStatus.Error : getStatus(tor.getInt(RPC_STATUS)),
locationDir,
tor.getInt(RPC_RATEDOWNLOAD),
tor.getInt(RPC_RATEUPLOAD),
@ -512,18 +536,19 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -512,18 +536,19 @@ public class TransmissionAdapter implements IDaemonAdapter {
tor.getLong(RPC_UPLOADEDEVER),
tor.getLong(RPC_TOTALSIZE),
//(float) tor.getDouble(RPC_PERCENTDONE),
(total == 0? 0: have/(float)total),
(total == 0? 0: (have+(float)tor.getLong(RPC_AVAILABLE))/(float)total),
null, // No label/category/group support in the RPC API for now
(total == 0 ? 0 : have / (float) total),
(total == 0 ? 0 : (have + (float) tor.getLong(RPC_AVAILABLE)) / (float) total),
// No label/category/group support in the RPC API for now
null,
new Date(tor.getLong(RPC_DATEADDED) * 1000L),
new Date(tor.getLong(RPC_DATEDONE) * 1000L),
errorString,
settings.getType()));
errorString, settings.getType()));
// @formatter:on
}
// Return the list
return torrents;
}
private TorrentStatus getStatus(int status) {
@ -531,20 +556,20 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -531,20 +556,20 @@ public class TransmissionAdapter implements IDaemonAdapter {
return TorrentStatus.Unknown;
} else if (rpcVersion >= 14) {
switch (status) {
case 0:
return TorrentStatus.Paused;
case 1:
return TorrentStatus.Waiting;
case 2:
return TorrentStatus.Checking;
case 3:
return TorrentStatus.Queued;
case 4:
return TorrentStatus.Downloading;
case 5:
return TorrentStatus.Queued;
case 6:
return TorrentStatus.Seeding;
case 0:
return TorrentStatus.Paused;
case 1:
return TorrentStatus.Waiting;
case 2:
return TorrentStatus.Checking;
case 3:
return TorrentStatus.Queued;
case 4:
return TorrentStatus.Downloading;
case 5:
return TorrentStatus.Queued;
case 6:
return TorrentStatus.Seeding;
}
return TorrentStatus.Unknown;
} else {
@ -553,7 +578,7 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -553,7 +578,7 @@ public class TransmissionAdapter implements IDaemonAdapter {
}
private ArrayList<TorrentFile> parseJsonFileList(JSONObject response, Torrent torrent) throws JSONException {
// Parse response
ArrayList<TorrentFile> torrentfiles = new ArrayList<TorrentFile>();
JSONArray rarray = response.getJSONArray("torrents");
@ -563,20 +588,22 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -563,20 +588,22 @@ public class TransmissionAdapter implements IDaemonAdapter {
for (int i = 0; i < files.length(); i++) {
JSONObject file = files.getJSONObject(i);
JSONObject stat = fileStats.getJSONObject(i);
// @formatter:off
torrentfiles.add(new TorrentFile(
"" + i,
String.valueOf(i),
file.getString(RPC_FILE_NAME),
file.getString(RPC_FILE_NAME),
torrent.getLocationDir() + file.getString(RPC_FILE_NAME),
file.getLong(RPC_FILE_LENGTH),
file.getLong(RPC_FILE_COMPLETED),
convertTransmissionPriority(stat.getBoolean(RPC_FILESTAT_WANTED), stat.getInt(RPC_FILESTAT_PRIORITY))));
// @formatter:on
}
}
// Return the list
return torrentfiles;
}
private Priority convertTransmissionPriority(boolean isWanted, int priority) {
@ -584,18 +611,18 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -584,18 +611,18 @@ public class TransmissionAdapter implements IDaemonAdapter {
return Priority.Off;
} else {
switch (priority) {
case 1:
return Priority.High;
case -1:
return Priority.Low;
default:
return Priority.Normal;
case 1:
return Priority.High;
case -1:
return Priority.Low;
default:
return Priority.Normal;
}
}
}
private TorrentDetails parseJsonTorrentDetails(JSONObject response) throws JSONException {
// Parse response
// NOTE: Assumes only details for one torrent are requested at a time
JSONArray rarray = response.getJSONArray("torrents");
@ -616,9 +643,9 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -616,9 +643,9 @@ public class TransmissionAdapter implements IDaemonAdapter {
}
return new TorrentDetails(trackers, errors);
}
return null;
}
@Override
@ -630,5 +657,5 @@ public class TransmissionAdapter implements IDaemonAdapter { @@ -630,5 +657,5 @@ public class TransmissionAdapter implements IDaemonAdapter {
public DaemonSettings getSettings() {
return this.settings;
}
}

607
app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java

@ -17,25 +17,18 @@ @@ -17,25 +17,18 @@
*/
package org.transdroid.daemon.Utorrent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
@ -66,28 +59,59 @@ import org.transdroid.daemon.task.SetLabelTask; @@ -66,28 +59,59 @@ import org.transdroid.daemon.task.SetLabelTask;
import org.transdroid.daemon.task.SetTrackersTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.task.StartTask;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* An adapter that allows for easy access to uTorrent torrent data. Communication
* is handled via authenticated JSON-RPC HTTP GET requests and responses.
*
* An adapter that allows for easy access to uTorrent torrent data. Communication is handled via authenticated JSON-RPC
* HTTP GET requests and responses.
* @author erickok
*
*/
public class UtorrentAdapter implements IDaemonAdapter {
private static final String LOG_NAME = "uTorrent daemon";
private static final String RPC_URL_HASH = "&hash=";
private static final int NAME_IDX = 0;
private static final int COUNT_IDX = 1;
// These are the positions inside the JSON response array of a torrent
// See http://forum.utorrent.com/viewtopic.php?id=25661
private static final int RPC_HASH_IDX = 0;
private static final int RPC_STATUS_IDX = 1;
private static final int RPC_NAME_IDX = 2;
private static final int RPC_SIZE_IDX = 3;
private static final int RPC_PARTDONE = 4;
private static final int RPC_DOWNLOADED_IDX = 5;
private static final int RPC_UPLOADED_IDX = 6;
private static final int RPC_DOWNLOADSPEED_IDX = 9;
private static final int RPC_UPLOADSPEED_IDX = 8;
private static final int RPC_ETA_IDX = 10;
private static final int RPC_LABEL_IDX = 11;
private static final int RPC_PEERSCONNECTED_IDX = 12;
private static final int RPC_PEERSINSWARM_IDX = 13;
private static final int RPC_SEEDSCONNECTED_IDX = 14;
private static final int RPC_SEEDSINSWARM_IDX = 15;
private static final int RPC_AVAILABILITY_IDX = 16;
private static final int RPC_ADDEDON_IDX = 23;
private static final int RPC_COMPLETEDON_IDX = 24;
// These are the positions inside the JSON response array of a torrent
// See http://forum.utorrent.com/viewtopic.php?id=25661
private static final int RPC_FILENAME_IDX = 0;
private static final int RPC_FILESIZE_IDX = 1;
private static final int RPC_FILEDOWNLOADED_IDX = 2;
private static final int RPC_FILEPRIORITY_IDX = 3;
private static String authtoken;
private DaemonSettings settings;
private DefaultHttpClient httpclient;
private static String authtoken;
/**
* Initialises an adapter that provides operations to the uTorrent web daemon
@ -97,166 +121,180 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -97,166 +121,180 @@ public class UtorrentAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
JSONObject result = makeUtorrentRequest("&list=1");
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonRetrieveTorrents(result.getJSONArray("torrents")),parseJsonRetrieveGetLabels(result.getJSONArray("label")));
case GetTorrentDetails:
// Request fine details of a specific torrent
JSONObject dresult = makeUtorrentRequest("&action=getprops" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, parseJsonTorrentDetails(dresult.getJSONArray("props")));
case GetFileList:
// Get the file listing of a torrent
JSONObject files = makeUtorrentRequest("&action=getfiles" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFileListing(files.getJSONArray("files").getJSONArray(1), task.getTargetTorrent()));
case AddByFile:
// Add a torrent to the server by sending the contents of a local .torrent file
String file = ((AddByFileTask)task).getFile();
uploadTorrentFile(file);
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask)task).getUrl();
if (url == null || url.equals(""))
throw new DaemonException(DaemonException.ExceptionType.ParsingFailed, "No url specified");
makeUtorrentRequest("&action=add-url&s=" + URLEncoder.encode(url, "UTF-8"));
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask)task).getUrl();
makeUtorrentRequest("&action=add-url&s=" + URLEncoder.encode(magnet, "UTF-8"));
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
if (removeTask.includingData()) {
makeUtorrentRequest("&action=removedata" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
} else {
makeUtorrentRequest("&action=remove" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
}
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
makeUtorrentRequest("&action=pause" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Pause all torrents
makeUtorrentRequest("&action=pause" + getAllHashes());
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeUtorrentRequest("&action=unpause" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeUtorrentRequest("&action=unpause" + getAllHashes());
return new DaemonTaskSuccessResult(task);
case Stop:
// Stop a torrent
makeUtorrentRequest("&action=stop" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case StopAll:
// Stop all torrents
makeUtorrentRequest("&action=stop" + getAllHashes());
return new DaemonTaskSuccessResult(task);
case Start:
// Start a torrent (maybe forced)
StartTask startTask = (StartTask) task;
if (startTask.isForced()) {
makeUtorrentRequest("&action=forcestart" + RPC_URL_HASH + startTask.getTargetTorrent().getUniqueID());
} else {
makeUtorrentRequest("&action=start" + RPC_URL_HASH + startTask.getTargetTorrent().getUniqueID());
}
return new DaemonTaskSuccessResult(task);
case StartAll:
// Start all torrents
makeUtorrentRequest("&action=start" + getAllHashes());
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
// Set priorities of the files of some torrent
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
String prioUrl = "&p=" + convertPriority(prioTask.getNewPriority());
for (TorrentFile forFile : prioTask.getForFiles()) {
prioUrl += "&f=" + forFile.getKey();
}
makeUtorrentRequest("&action=setprio" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID() + prioUrl);
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
makeUtorrentRequest(
"&action=setsetting&s=ul_auto_throttle&v=0&s=max_ul_rate&v=" +
(ratesTask.getUploadRate() == null? 0: ratesTask.getUploadRate().intValue()) +
"&s=max_dl_rate&v=" +
(ratesTask.getDownloadRate() == null? 0: ratesTask.getDownloadRate().intValue()));
return new DaemonTaskSuccessResult(task);
case SetLabel:
// Set the label of some torrent
SetLabelTask labelTask = (SetLabelTask) task;
makeUtorrentRequest("&action=setprops" + RPC_URL_HASH + labelTask.getTargetTorrent().getUniqueID() +
"&s=label&v=" + URLEncoder.encode(labelTask.getNewLabel(), "UTF-8"));
return new DaemonTaskSuccessResult(task);
case SetTrackers:
// Set the trackers of some torrent
SetTrackersTask trackersTask = (SetTrackersTask) task;
// Build list of tracker lines, separated by a \r\n
String newTrackersText = "";
for (String tracker : trackersTask.getNewTrackers()) {
newTrackersText += (newTrackersText.length() == 0? "": "\r\n") + tracker;
}
makeUtorrentRequest("&action=setprops" + RPC_URL_HASH + trackersTask.getTargetTorrent().getUniqueID() +
"&s=trackers&v=" + URLEncoder.encode(newTrackersText, "UTF-8"));
return new DaemonTaskSuccessResult(task);
case Retrieve:
case ForceRecheck:
// Request all torrents from server
JSONObject result = makeUtorrentRequest(log, "&list=1");
return new RetrieveTaskSuccessResult((RetrieveTask) task,
parseJsonRetrieveTorrents(result.getJSONArray("torrents")),
parseJsonRetrieveGetLabels(result.getJSONArray("label")));
// Force re-check of data on a torrent
makeUtorrentRequest("&action=recheck" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case GetTorrentDetails:
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, task.getMethod() + " is not supported by " + getType()));
// Request fine details of a specific torrent
JSONObject dresult = makeUtorrentRequest(log,
"&action=getprops" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
parseJsonTorrentDetails(dresult.getJSONArray("props")));
case GetFileList:
// Get the file listing of a torrent
JSONObject files = makeUtorrentRequest(log,
"&action=getfiles" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new GetFileListTaskSuccessResult((GetFileListTask) task,
parseJsonFileListing(files.getJSONArray("files").getJSONArray(1), task.getTargetTorrent()));
case AddByFile:
// Add a torrent to the server by sending the contents of a local .torrent file
String file = ((AddByFileTask) task).getFile();
uploadTorrentFile(file);
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
if (url == null || url.equals("")) {
throw new DaemonException(DaemonException.ExceptionType.ParsingFailed, "No url specified");
}
makeUtorrentRequest(log, "&action=add-url&s=" + URLEncoder.encode(url, "UTF-8"));
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
makeUtorrentRequest(log, "&action=add-url&s=" + URLEncoder.encode(magnet, "UTF-8"));
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
if (removeTask.includingData()) {
makeUtorrentRequest(log,
"&action=removedata" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
} else {
makeUtorrentRequest(log,
"&action=remove" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
}
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
makeUtorrentRequest(log, "&action=pause" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Pause all torrents
makeUtorrentRequest(log, "&action=pause" + getAllHashes(log));
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeUtorrentRequest(log, "&action=unpause" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeUtorrentRequest(log, "&action=unpause" + getAllHashes(log));
return new DaemonTaskSuccessResult(task);
case Stop:
// Stop a torrent
makeUtorrentRequest(log, "&action=stop" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
case StopAll:
// Stop all torrents
makeUtorrentRequest(log, "&action=stop" + getAllHashes(log));
return new DaemonTaskSuccessResult(task);
case Start:
// Start a torrent (maybe forced)
StartTask startTask = (StartTask) task;
if (startTask.isForced()) {
makeUtorrentRequest(log,
"&action=forcestart" + RPC_URL_HASH + startTask.getTargetTorrent().getUniqueID());
} else {
makeUtorrentRequest(log,
"&action=start" + RPC_URL_HASH + startTask.getTargetTorrent().getUniqueID());
}
return new DaemonTaskSuccessResult(task);
case StartAll:
// Start all torrents
makeUtorrentRequest(log, "&action=start" + getAllHashes(log));
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
// Set priorities of the files of some torrent
SetFilePriorityTask prioTask = (SetFilePriorityTask) task;
String prioUrl = "&p=" + convertPriority(prioTask.getNewPriority());
for (TorrentFile forFile : prioTask.getForFiles()) {
prioUrl += "&f=" + forFile.getKey();
}
makeUtorrentRequest(log,
"&action=setprio" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID() + prioUrl);
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
makeUtorrentRequest(log, "&action=setsetting&s=ul_auto_throttle&v=0&s=max_ul_rate&v=" +
(ratesTask.getUploadRate() == null ? 0 : ratesTask.getUploadRate()) +
"&s=max_dl_rate&v=" +
(ratesTask.getDownloadRate() == null ? 0 : ratesTask.getDownloadRate()));
return new DaemonTaskSuccessResult(task);
case SetLabel:
// Set the label of some torrent
SetLabelTask labelTask = (SetLabelTask) task;
makeUtorrentRequest(log,
"&action=setprops" + RPC_URL_HASH + labelTask.getTargetTorrent().getUniqueID() +
"&s=label&v=" + URLEncoder.encode(labelTask.getNewLabel(), "UTF-8"));
return new DaemonTaskSuccessResult(task);
case SetTrackers:
// Set the trackers of some torrent
SetTrackersTask trackersTask = (SetTrackersTask) task;
// Build list of tracker lines, separated by a \r\n
String newTrackersText = "";
for (String tracker : trackersTask.getNewTrackers()) {
newTrackersText += (newTrackersText.length() == 0 ? "" : "\r\n") + tracker;
}
makeUtorrentRequest(log,
"&action=setprops" + RPC_URL_HASH + trackersTask.getTargetTorrent().getUniqueID() +
"&s=trackers&v=" + URLEncoder.encode(newTrackersText, "UTF-8"));
return new DaemonTaskSuccessResult(task);
case ForceRecheck:
// Force re-check of data on a torrent
makeUtorrentRequest(log, "&action=recheck" + RPC_URL_HASH + task.getTargetTorrent().getUniqueID());
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
@ -265,14 +303,12 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -265,14 +303,12 @@ public class UtorrentAdapter implements IDaemonAdapter {
} catch (FileNotFoundException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.FileAccessError, e.toString()));
} catch (UnsupportedEncodingException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, e.toString()));
return new DaemonTaskFailureResult(task,
new DaemonException(ExceptionType.MethodUnsupported, e.toString()));
} catch (IOException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ConnectionError, e.toString()));
}
}
private static final int NAME_IDX = 0;
private static final int COUNT_IDX = 1;
private ArrayList<Label> parseJsonRetrieveGetLabels(JSONArray lresults) throws JSONException {
@ -282,28 +318,25 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -282,28 +318,25 @@ public class UtorrentAdapter implements IDaemonAdapter {
JSONArray lab = lresults.getJSONArray(i);
String name = lab.getString(NAME_IDX);
int count = lab.getInt(COUNT_IDX);
labels.add(new Label(
name,
count
));
labels.add(new Label(name, count));
}
return labels;
}
private JSONObject makeUtorrentRequest(String addToUrl) throws DaemonException {
return makeUtorrentRequest(addToUrl, 0);
private JSONObject makeUtorrentRequest(Log log, String addToUrl) throws DaemonException {
return makeUtorrentRequest(log, addToUrl, 0);
}
private JSONObject makeUtorrentRequest(String addToUrl, int retried) throws DaemonException {
private JSONObject makeUtorrentRequest(Log log, String addToUrl, int retried) throws DaemonException {
try {
// Initialise the HTTP client
if (httpclient == null) {
initialise();
}
ensureToken();
// Make request
@ -317,64 +350,68 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -317,64 +350,68 @@ public class UtorrentAdapter implements IDaemonAdapter {
// Auth token was invalidated; retry at max 3 times
authtoken = null; // So that ensureToken() will request a new token on the next try
if (retried < 2) {
return makeUtorrentRequest(addToUrl, retried++);
return makeUtorrentRequest(log, addToUrl, ++retried);
}
throw new DaemonException(ExceptionType.AuthenticationFailure, "Response was '" + result.replace("\n", "") + "' instead of a proper JSON object (and we used auth token '" + authtoken + "')");
throw new DaemonException(ExceptionType.AuthenticationFailure,
"Response was '" + result.replace("\n", "") +
"' instead of a proper JSON object (and we used auth token '" + authtoken + "')");
}
JSONObject json = new JSONObject(result);
instream.close();
return json;
return json;
} catch (DaemonException e) {
throw e;
} catch (JSONException e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ParsingFailed, e.toString());
} catch (Exception e) {
DLog.d(LOG_NAME, "Error: " + e.toString());
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private synchronized void ensureToken() throws IOException, ClientProtocolException, DaemonException {
private synchronized void ensureToken() throws IOException, DaemonException {
// Make sure we have a valid token
if (authtoken == null) {
// Make a request to /gui/token.html
// See https://github.com/bittorrent/webui/wiki/TokenSystem
HttpGet httpget = new HttpGet(buildWebUIUrl() + "token.html");
// Parse the response HTML
HttpResponse response = httpclient.execute(httpget);
if (response.getStatusLine().getStatusCode() == 401) {
throw new DaemonException(ExceptionType.AuthenticationFailure, "Auth denied (401) on token.html retrieval");
throw new DaemonException(ExceptionType.AuthenticationFailure,
"Auth denied (401) on token.html retrieval");
}
if (response.getStatusLine().getStatusCode() == 404) {
throw new DaemonException(ExceptionType.ConnectionError, "Not found (404); server doesn't exist or is inaccessible");
throw new DaemonException(ExceptionType.ConnectionError,
"Not found (404); server doesn't exist or is inaccessible");
}
InputStream instream = response.getEntity().getContent();
String result = HttpHelper.convertStreamToString(instream);
authtoken = result.replaceAll("\\<.*?>","").trim();
authtoken = result.replaceAll("<.*?>", "").trim();
}
}
public JSONObject uploadTorrentFile(String file) throws DaemonException, ClientProtocolException, IOException, JSONException {
public JSONObject uploadTorrentFile(String file) throws DaemonException, IOException, JSONException {
// Initialise the HTTP client
if (httpclient == null) {
initialise();
}
ensureToken();
// Build and make request
HttpPost httppost = new HttpPost(buildWebUIUrl() + "?token=" + authtoken + "&action=add-file");
File upload = new File(URI.create(file));
Part[] parts = { new FilePart("torrent_file", upload, FilePart.DEFAULT_CONTENT_TYPE, null) };
Part[] parts = {new FilePart("torrent_file", upload, FilePart.DEFAULT_CONTENT_TYPE, null)};
httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
HttpResponse response = httpclient.execute(httppost);
@ -384,24 +421,24 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -384,24 +421,24 @@ public class UtorrentAdapter implements IDaemonAdapter {
JSONObject json = new JSONObject(result);
instream.close();
return json;
}
/**
* Instantiates an HTTP client with proper credentials that can be used for all Transmission requests.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On conflicting or missing settings
*/
private void initialise() throws DaemonException {
this.httpclient = HttpHelper.createStandardHttpClient(settings, true);
}
/**
* Build the URL of the Transmission web UI from the user settings.
* @return The URL of the RPC API
*/
private String buildWebUIUrl() {
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() + "/gui/";
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() +
"/gui/";
}
private TorrentStatus convertUtorrentStatus(int uStatus, boolean finished) {
@ -430,57 +467,36 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -430,57 +467,36 @@ public class UtorrentAdapter implements IDaemonAdapter {
return TorrentStatus.Waiting;
}
}
private Priority convertUtorrentPriority(int code) {
switch (code) {
case 0:
return Priority.Off;
case 1:
return Priority.Low;
case 3:
return Priority.High;
default:
return Priority.Normal;
case 0:
return Priority.Off;
case 1:
return Priority.Low;
case 3:
return Priority.High;
default:
return Priority.Normal;
}
}
private int convertPriority(Priority newPriority) {
if (newPriority == null) {
return 2;
return 2;
}
switch (newPriority) {
case Off:
return 0;
case Low:
return 1;
case High:
return 3;
default:
return 2;
case Off:
return 0;
case Low:
return 1;
case High:
return 3;
default:
return 2;
}
}
// These are the positions inside the JSON response array of a torrent
// See http://forum.utorrent.com/viewtopic.php?id=25661
private static final int RPC_HASH_IDX = 0;
private static final int RPC_STATUS_IDX = 1;
private static final int RPC_NAME_IDX = 2;
private static final int RPC_SIZE_IDX = 3;
private static final int RPC_PARTDONE = 4;
private static final int RPC_DOWNLOADED_IDX = 5;
private static final int RPC_UPLOADED_IDX = 6;
private static final int RPC_DOWNLOADSPEED_IDX = 9;
private static final int RPC_UPLOADSPEED_IDX = 8;
private static final int RPC_ETA_IDX = 10;
private static final int RPC_LABEL_IDX = 11;
private static final int RPC_PEERSCONNECTED_IDX = 12;
private static final int RPC_PEERSINSWARM_IDX = 13;
private static final int RPC_SEEDSCONNECTED_IDX = 14;
private static final int RPC_SEEDSINSWARM_IDX = 15;
private static final int RPC_AVAILABILITY_IDX = 16;
private static final int RPC_ADDEDON_IDX = 23;
private static final int RPC_COMPLETEDON_IDX = 24;
private ArrayList<Torrent> parseJsonRetrieveTorrents(JSONArray results) throws JSONException {
// Parse response
@ -490,7 +506,7 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -490,7 +506,7 @@ public class UtorrentAdapter implements IDaemonAdapter {
JSONArray tor = results.getJSONArray(i);
String name = tor.getString(RPC_NAME_IDX);
boolean downloaded = (tor.getLong(RPC_PARTDONE) == 1000l);
float available = ((float)tor.getInt(RPC_AVAILABILITY_IDX)) / 65536f; // Integer in 1/65536ths
float available = ((float) tor.getInt(RPC_AVAILABILITY_IDX)) / 65536f; // Integer in 1/65536ths
// The full torrent path is not available in uTorrent web UI API
// Guess the torrent's directory based on the user-specific default download dir and the torrent name
String dir = null;
@ -505,35 +521,22 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -505,35 +521,22 @@ public class UtorrentAdapter implements IDaemonAdapter {
TorrentStatus status = convertUtorrentStatus(tor.getInt(RPC_STATUS_IDX), downloaded);
long addedOn = tor.optInt(RPC_ADDEDON_IDX, -1);
long completedOn = tor.optInt(RPC_COMPLETEDON_IDX, -1);
Date addedOnDate = addedOn == -1? null: new Date(addedOn * 1000L);
Date completedOnDate = completedOn == -1? null: new Date(completedOn * 1000L);
torrents.add(new Torrent(
i, // No ID but a hash is used
tor.getString(RPC_HASH_IDX),
name,
status,
dir,
tor.getInt(RPC_DOWNLOADSPEED_IDX),
tor.getInt(RPC_UPLOADSPEED_IDX),
tor.getInt(RPC_SEEDSCONNECTED_IDX),
tor.getInt(RPC_SEEDSINSWARM_IDX),
tor.getInt(RPC_PEERSCONNECTED_IDX),
tor.getInt(RPC_PEERSINSWARM_IDX),
tor.getInt(RPC_ETA_IDX),
tor.getLong(RPC_DOWNLOADED_IDX),
tor.getLong(RPC_UPLOADED_IDX),
tor.getLong(RPC_SIZE_IDX),
Date addedOnDate = addedOn == -1 ? null : new Date(addedOn * 1000L);
Date completedOnDate = completedOn == -1 ? null : new Date(completedOn * 1000L);
torrents.add(new Torrent(i, // No ID but a hash is used
tor.getString(RPC_HASH_IDX), name, status, dir, tor.getInt(RPC_DOWNLOADSPEED_IDX),
tor.getInt(RPC_UPLOADSPEED_IDX), tor.getInt(RPC_SEEDSCONNECTED_IDX),
tor.getInt(RPC_SEEDSINSWARM_IDX), tor.getInt(RPC_PEERSCONNECTED_IDX),
tor.getInt(RPC_PEERSINSWARM_IDX), tor.getInt(RPC_ETA_IDX), tor.getLong(RPC_DOWNLOADED_IDX),
tor.getLong(RPC_UPLOADED_IDX), tor.getLong(RPC_SIZE_IDX),
((float) tor.getLong(RPC_PARTDONE)) / 1000f, // Integer in promille
Math.min(available, 1f), // Can be > 100% if multiple peers have 100%
tor.getString(RPC_LABEL_IDX).trim(),
addedOnDate,
completedOnDate,
tor.getString(RPC_LABEL_IDX).trim(), 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,
settings.getType()));
status == TorrentStatus.Error ? "See GUI for error message" : null, settings.getType()));
}
return torrents;
}
private TorrentDetails parseJsonTorrentDetails(JSONArray results) throws JSONException {
@ -541,7 +544,7 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -541,7 +544,7 @@ public class UtorrentAdapter implements IDaemonAdapter {
// Parse response
// NOTE: Assumes only details for one torrent are requested at a time
if (results.length() > 0) {
JSONObject tor = results.getJSONObject(0);
List<String> trackers = new ArrayList<String>();
for (String tracker : tor.getString("trackers").split("\\r\\n")) {
@ -554,53 +557,49 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -554,53 +557,49 @@ public class UtorrentAdapter implements IDaemonAdapter {
// See http://forum.utorrent.com/viewtopic.php?pid=553340#p553340
return new TorrentDetails(trackers, null);
}
return null;
}
// These are the positions inside the JSON response array of a torrent
// See http://forum.utorrent.com/viewtopic.php?id=25661
private static final int RPC_FILENAME_IDX = 0;
private static final int RPC_FILESIZE_IDX = 1;
private static final int RPC_FILEDOWNLOADED_IDX = 2;
private static final int RPC_FILEPRIORITY_IDX = 3;
}
private ArrayList<TorrentFile> parseJsonFileListing(JSONArray results, Torrent torrent) throws JSONException {
// Parse response
ArrayList<TorrentFile> files = new ArrayList<TorrentFile>();
boolean createPaths = torrent != null && torrent.getLocationDir() != null && !torrent.getLocationDir().equals("");
boolean createPaths =
torrent != null && torrent.getLocationDir() != null && !torrent.getLocationDir().equals("");
final String pathSep = settings.getOS().getPathSeperator();
for (int i = 0; i < results.length(); i++) {
JSONArray file = results.getJSONArray(i);
// Add the parsed torrent to the list
files.add(new TorrentFile(
"" + i,
file.getString(RPC_FILENAME_IDX), // Name
(createPaths? file.getString(RPC_FILENAME_IDX).replace((pathSep.equals("/")? "\\": "/"), pathSep): null), // Relative path; 'wrong' path slashes will be replaced
(createPaths? torrent.getLocationDir() + file.getString(RPC_FILENAME_IDX).replace((pathSep.equals("/")? "\\": "/"), pathSep): null), // Full path; 'wrong' path slashes will be replaced
file.getLong(RPC_FILESIZE_IDX), // Total size
file.getLong(RPC_FILEDOWNLOADED_IDX), // Part done
convertUtorrentPriority(file.getInt(RPC_FILEPRIORITY_IDX)))); // Priority
files.add(new TorrentFile("" + i, file.getString(RPC_FILENAME_IDX), // Name
(createPaths ?
file.getString(RPC_FILENAME_IDX).replace((pathSep.equals("/") ? "\\" : "/"), pathSep) :
null), // Relative path; 'wrong' path slashes will be replaced
(createPaths ? torrent.getLocationDir() +
file.getString(RPC_FILENAME_IDX).replace((pathSep.equals("/") ? "\\" : "/"), pathSep) :
null), // Full path; 'wrong' path slashes will be replaced
file.getLong(RPC_FILESIZE_IDX), // Total size
file.getLong(RPC_FILEDOWNLOADED_IDX), // Part done
convertUtorrentPriority(file.getInt(RPC_FILEPRIORITY_IDX)))); // Priority
}
return files;
}
private String getAllHashes() throws DaemonException, JSONException {
private String getAllHashes(Log log) throws DaemonException, JSONException {
// Make a retrieve torrents call first to gather all hashes
JSONObject result = makeUtorrentRequest("&list=1");
JSONObject result = makeUtorrentRequest(log, "&list=1");
ArrayList<Torrent> torrents = parseJsonRetrieveTorrents(result.getJSONArray("torrents"));
// Build a string of hashes of all the torrents
String hashes = "";
for (Torrent torrent : torrents) {
hashes += RPC_URL_HASH + torrent.getUniqueID();
}
return hashes;
}
@Override
@ -612,5 +611,5 @@ public class UtorrentAdapter implements IDaemonAdapter { @@ -612,5 +611,5 @@ public class UtorrentAdapter implements IDaemonAdapter {
public DaemonSettings getSettings() {
return this.settings;
}
}

28
app/src/main/java/org/transdroid/daemon/Vuze/VuzeAdapter.java

@ -29,6 +29,7 @@ import java.util.List; @@ -29,6 +29,7 @@ import java.util.List;
import java.util.Map;
import org.apache.openjpa.lib.util.Base16Encoder;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
@ -52,7 +53,6 @@ import org.transdroid.daemon.task.RetrieveTask; @@ -52,7 +53,6 @@ import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.DLog;
/**
* An adapter that allows for easy access to Vuze torrent data. Communication
@ -81,14 +81,14 @@ public class VuzeAdapter implements IDaemonAdapter { @@ -81,14 +81,14 @@ public class VuzeAdapter implements IDaemonAdapter {
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
Object result = makeVuzeCall(DaemonMethod.Retrieve, "getDownloads");
return new RetrieveTaskSuccessResult((RetrieveTask) task, onTorrentsRetrieved(result),null);
return new RetrieveTaskSuccessResult((RetrieveTask) task, onTorrentsRetrieved(log, result),null);
case GetFileList:
@ -189,8 +189,10 @@ public class VuzeAdapter implements IDaemonAdapter { @@ -189,8 +189,10 @@ public class VuzeAdapter implements IDaemonAdapter {
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
makeVuzeCall(DaemonMethod.SetTransferRates, "setBooleanParameter[String,boolean]", new Object[] { "Auto Upload Speed Enabled", false } );
makeVuzeCall(DaemonMethod.SetTransferRates, "setCoreIntParameter[String,int]", new Object[] { "Max Upload Speed KBs", (ratesTask.getUploadRate() == null? 0: ratesTask.getUploadRate().intValue())} );
makeVuzeCall(DaemonMethod.SetTransferRates, "setCoreIntParameter[String,int]", new Object[] { "Max Download Speed KBs", (ratesTask.getDownloadRate() == null? 0: ratesTask.getDownloadRate().intValue())} );
makeVuzeCall(DaemonMethod.SetTransferRates, "setCoreIntParameter[String,int]", new Object[] { "Max Upload Speed KBs", (ratesTask.getUploadRate() == null? 0:
ratesTask.getUploadRate())} );
makeVuzeCall(DaemonMethod.SetTransferRates, "setCoreIntParameter[String,int]", new Object[] { "Max Download Speed KBs", (ratesTask.getDownloadRate() == null? 0:
ratesTask.getDownloadRate())} );
return new DaemonTaskSuccessResult(task);
default:
@ -318,7 +320,7 @@ public class VuzeAdapter implements IDaemonAdapter { @@ -318,7 +320,7 @@ public class VuzeAdapter implements IDaemonAdapter {
Map<String, Object> torrentData = rpcclient.callXMLRPC(vuzeObjectID, serverMethod, params, savedConnectionID, false);
serverMethod = "addDownload[Torrent]";
vuzeObjectID = savedDownloadManagerID;
params = new String[] { ((Long) torrentData.get("_object_id")).toString() };
params = new String[] { torrentData.get("_object_id").toString() };
paramsAreVuzeObjects = true;
}
@ -346,7 +348,7 @@ public class VuzeAdapter implements IDaemonAdapter { @@ -346,7 +348,7 @@ public class VuzeAdapter implements IDaemonAdapter {
}
@SuppressWarnings("unchecked")
private List<Torrent> onTorrentsRetrieved(Object result) throws DaemonException {
private List<Torrent> onTorrentsRetrieved(Log log, Object result) throws DaemonException {
Map<String, Object> response = (Map<String, Object>) result;
@ -355,7 +357,7 @@ public class VuzeAdapter implements IDaemonAdapter { @@ -355,7 +357,7 @@ public class VuzeAdapter implements IDaemonAdapter {
return new ArrayList<Torrent>();
}
DLog.d(LOG_NAME, response.toString().length() > 300? response.toString().substring(0, 300) + "... (" + response.toString().length() + " chars)": response.toString());
log.d(LOG_NAME, response.toString().length() > 300? response.toString().substring(0, 300) + "... (" + response.toString().length() + " chars)": response.toString());
List<Torrent> torrents = new ArrayList<Torrent>();
@ -376,7 +378,7 @@ public class VuzeAdapter implements IDaemonAdapter { @@ -376,7 +378,7 @@ public class VuzeAdapter implements IDaemonAdapter {
}
*/
Map<String, Object> info = (Map<String, Object>) response.get(key);
if (info == null || !info.containsKey("_object_id") || ((Long)info.get("_object_id")) == null) {
if (info == null || !info.containsKey("_object_id") || info.get("_object_id") == null) {
// No valid XML data object returned
throw new DaemonException(DaemonException.ExceptionType.UnexpectedResponse, "Map of objects returned by Vuze, but these object do not have some <info> attached or no <_object_id> is available");
}
@ -396,10 +398,10 @@ public class VuzeAdapter implements IDaemonAdapter { @@ -396,10 +398,10 @@ public class VuzeAdapter implements IDaemonAdapter {
torrents.add(new Torrent(
(Long) info.get("_object_id"), // id
((Long) info.get("_object_id")).toString(), // hash //(String) torrentinfo.get("hash"), // hash
(String) info.get("name").toString().trim(), // name
info.get("_object_id").toString(), // hash //(String) torrentinfo.get("hash"), // hash
info.get("name").toString().trim(), // name
convertTorrentStatus((Long) info.get("state")), // status
(String) statsinfo.get("target_file_or_dir") + "/", // locationDir
statsinfo.get("target_file_or_dir") + "/", // locationDir
rateDownload, // rateDownload
((Long)statsinfo.get("upload_average")).intValue(), // rateUpload
announceSeedCount, // seedersConnected
@ -461,7 +463,7 @@ public class VuzeAdapter implements IDaemonAdapter { @@ -461,7 +463,7 @@ public class VuzeAdapter implements IDaemonAdapter {
String file = (String)info.get("file");
files.add(new TorrentFile(
"" + (Long)info.get("_object_id"),
String.valueOf(info.get("_object_id")),
new File(file).getName(), // name
(file.length() > torrent.getLocationDir().length()? file.substring(torrent.getLocationDir().length()): file), // name
file, // fullPath

76
app/src/main/java/org/transdroid/daemon/task/DaemonTask.java

@ -15,46 +15,51 @@ @@ -15,46 +15,51 @@
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.transdroid.daemon.task;
package org.transdroid.daemon.task;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonMethod;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Torrent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
/**
* A daemon task represents some action that needs to be performed
* on the server daemon. It has no capabilities on itself; these are
* marshaled to the daemon adapter. Therefore all needed info (the
* parameters) needs to be added to the extras bundle.
*
* To help create these tasks and there data, each possible daemon
* method is created using a task-specific separate class with a
* create() method.
*
* This class is Parcelable so it can be persisted in between an
* Activity breakdown and recreation.
*
* A daemon task represents some action that needs to be performed on the server daemon. It has no capabilities on
* itself; these are marshaled to the daemon adapter. Therefore all needed info (the parameters) needs to be added to
* the extras bundle.
* <p/>
* To help create these tasks and there data, each possible daemon method is created using a task-specific separate
* class with a create() method.
* <p/>
* This class is Parcelable so it can be persisted in between an Activity breakdown and recreation.
* @author erickok
*
*/
public class DaemonTask implements Parcelable {
protected IDaemonAdapter adapter;
public static final Parcelable.Creator<DaemonTask> CREATOR = new Parcelable.Creator<DaemonTask>() {
public DaemonTask createFromParcel(Parcel in) {
return new DaemonTask(in);
}
public DaemonTask[] newArray(int size) {
return new DaemonTask[size];
}
};
protected final DaemonMethod method;
protected final Torrent targetTorrent;
protected final Bundle extras;
protected IDaemonAdapter adapter;
private DaemonTask(Parcel in) {
this.method = DaemonMethod.getStatus(in.readInt());
this.targetTorrent = in.readParcelable(Torrent.class.getClassLoader());
this.extras = in.readBundle();
}
protected DaemonTask(IDaemonAdapter adapter, DaemonMethod method, Torrent targetTorrent, Bundle extras) {
this.adapter = adapter;
this.method = method;
@ -65,18 +70,19 @@ public class DaemonTask implements Parcelable { @@ -65,18 +70,19 @@ public class DaemonTask implements Parcelable {
this.extras = extras;
}
}
/**
* Execute the task on the appropriate daemon adapter
* @param log The logger to use when writing exceptions and debug information
*/
public DaemonTaskResult execute() {
return adapter.executeTask(this);
public DaemonTaskResult execute(Log log) {
return adapter.executeTask(log, this);
}
public DaemonMethod getMethod() {
return method;
}
public Daemon getAdapterType() {
return this.adapter.getType();
}
@ -89,33 +95,27 @@ public class DaemonTask implements Parcelable { @@ -89,33 +95,27 @@ public class DaemonTask implements Parcelable {
return extras;
}
public static final Parcelable.Creator<DaemonTask> CREATOR = new Parcelable.Creator<DaemonTask>() {
public DaemonTask createFromParcel(Parcel in) {
return new DaemonTask(in);
}
public DaemonTask[] newArray(int size) {
return new DaemonTask[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(method.getCode());
dest.writeParcelable(targetTorrent, 0);
dest.writeBundle(extras);
}
/**
* Returns a readable description of this task in the form 'MethodName on AdapterName with TorrentName and AllExtras'
* Returns a readable description of this task in the form 'MethodName on AdapterName with TorrentName and
* AllExtras'
*/
public String toString() {
return method.toString() + (adapter == null? "": " on " + adapter.getType()) + (targetTorrent != null || extras != null? " with ": "") + (targetTorrent == null? "": targetTorrent.toString() + (targetTorrent != null && extras != null? " and ": "")) + (extras == null? "": extras.toString());
return method.toString() + (adapter == null ? "" : " on " + adapter.getType()) +
(targetTorrent != null || extras != null ? " with " : "") +
(targetTorrent == null ? "" : targetTorrent.toString() + (extras != null ? " and " : "")) +
(extras == null ? "" : extras.toString());
}
}

7
app/src/main/java/org/transdroid/daemon/util/Collections2.java

@ -1,7 +1,5 @@ @@ -1,7 +1,5 @@
package org.transdroid.daemon.util;
import java.util.Iterator;
/**
* Helpers on Collections
*/
@ -13,9 +11,8 @@ public class Collections2 { @@ -13,9 +11,8 @@ public class Collections2 {
public static <T> String joinString(Iterable<T> iterable, String separator) {
boolean first = true;
String result = "";
Iterator<T> it = iterable.iterator();
while (it.hasNext()) {
result += (first ? "" : separator) + it.next().toString();
for (T anIterable : iterable) {
result += (first ? "" : separator) + anIterable.toString();
first = false;
}
return result;

59
app/src/main/java/org/transdroid/daemon/util/DLog.java

@ -1,59 +0,0 @@ @@ -1,59 +0,0 @@
/*
* This file is part of Transdroid <http://www.transdroid.org>
*
* Transdroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Transdroid is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.transdroid.daemon.util;
/**
* Universal logger; applications using this library should
* attach an ITLogger using <code>setLogger(ITLogger)</code>
* to receive any logging information from the daemons.
*
* @author erickok
*/
public class DLog {
private static final String LOG_TAG = "Transdroid";
private static ITLogger instance = null;
public static void setLogger(ITLogger logger) {
instance = logger;
}
/**
* Send a DEBUG log message.
* @param self Unique source tag, identifying the part of Transdroid it happens in
* @param msg The debug message to log
*/
public static void d(String self, String msg) {
if (instance != null) {
instance.d(LOG_TAG, self + ": " + msg);
}
}
/**
* Send an ERROR log message.
* @param self Unique source tag, identifying the part of Transdroid it happens in
* @param msg The error message to log
*/
public static void e(String self, String msg) {
if (instance != null) {
instance.e(LOG_TAG, self + ": " + msg);
}
}
}

158
app/src/main/java/org/transdroid/daemon/util/HttpHelper.java

@ -17,15 +17,7 @@ @@ -17,15 +17,7 @@
*/
package org.transdroid.daemon.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import android.net.Uri;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
@ -53,7 +45,15 @@ import org.transdroid.daemon.DaemonException; @@ -53,7 +45,15 @@ import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
import android.net.Uri;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
/**
* Provides a set of general helper methods that can be used in web-based communication.
@ -62,15 +62,41 @@ import android.net.Uri; @@ -62,15 +62,41 @@ import android.net.Uri;
public class HttpHelper {
public static final int DEFAULT_CONNECTION_TIMEOUT = 8000;
public static final String SCHEME_HTTP = "http";
public static final String SCHEME_HTTPS = "https";
public static final String SCHEME_MAGNET = "magnet";
public static final String SCHEME_FILE = "file";
/**
* The 'User-Agent' name to send to the server
*/
public static String userAgent = "Transdroid Torrent Connect";
/**
* HTTP request interceptor to allow for GZip-encoded data transfer
*/
public static HttpRequestInterceptor gzipRequestInterceptor = new HttpRequestInterceptor() {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
if (!request.containsHeader("Accept-Encoding")) {
request.addHeader("Accept-Encoding", "gzip");
}
}
};
/**
* HTTP response interceptor that decodes GZipped data
*/
public static HttpResponseInterceptor gzipResponseInterceptor = new HttpResponseInterceptor() {
public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException {
HttpEntity entity = response.getEntity();
Header ceheader = entity.getContentEncoding();
if (ceheader != null) {
HeaderElement[] codecs = ceheader.getElements();
for (HeaderElement codec : codecs) {
if (codec.getName().equalsIgnoreCase("gzip")) {
response.setEntity(new GzipDecompressingEntity(response.getEntity()));
return;
}
}
}
}
};
/**
* Creates a standard Apache HttpClient that is thread safe, supports different SSL auth methods and basic
@ -98,8 +124,8 @@ public class HttpHelper { @@ -98,8 +124,8 @@ public class HttpHelper {
* @throws DaemonException Thrown when information (such as username/password) is missing
*/
public static DefaultHttpClient createStandardHttpClient(boolean userBasicAuth, String username, String password,
boolean sslTrustAll, String sslTrustKey, int timeout, String authAddress, int authPort)
throws DaemonException {
boolean sslTrustAll, String sslTrustKey, int timeout,
String authAddress, int authPort) throws DaemonException {
// Register http and https sockets
SchemeRegistry registry = new SchemeRegistry();
@ -122,8 +148,8 @@ public class HttpHelper { @@ -122,8 +148,8 @@ public class HttpHelper {
HttpProtocolParams.setUserAgent(httpparams, userAgent);
}
DefaultHttpClient httpclient = new DefaultHttpClient(new ThreadSafeClientConnManager(httpparams, registry),
httpparams);
DefaultHttpClient httpclient =
new DefaultHttpClient(new ThreadSafeClientConnManager(httpparams, registry), httpparams);
// Authentication credentials
if (userBasicAuth) {
@ -131,78 +157,20 @@ public class HttpHelper { @@ -131,78 +157,20 @@ public class HttpHelper {
throw new DaemonException(ExceptionType.AuthenticationFailure,
"No username or password was provided while we had authentication enabled");
}
httpclient.getCredentialsProvider().setCredentials(
new AuthScope(authAddress, authPort, AuthScope.ANY_REALM),
new UsernamePasswordCredentials(username, password));
httpclient.getCredentialsProvider()
.setCredentials(new AuthScope(authAddress, authPort, AuthScope.ANY_REALM),
new UsernamePasswordCredentials(username, password));
}
return httpclient;
}
/**
* HTTP request interceptor to allow for GZip-encoded data transfer
*/
public static HttpRequestInterceptor gzipRequestInterceptor = new HttpRequestInterceptor() {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
if (!request.containsHeader("Accept-Encoding")) {
request.addHeader("Accept-Encoding", "gzip");
}
}
};
/**
* HTTP response interceptor that decodes GZipped data
*/
public static HttpResponseInterceptor gzipResponseInterceptor = new HttpResponseInterceptor() {
public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException {
HttpEntity entity = response.getEntity();
Header ceheader = entity.getContentEncoding();
if (ceheader != null) {
HeaderElement[] codecs = ceheader.getElements();
for (int i = 0; i < codecs.length; i++) {
if (codecs[i].getName().equalsIgnoreCase("gzip")) {
response.setEntity(new HttpHelper.GzipDecompressingEntity(response.getEntity()));
return;
}
}
}
}
};
/**
* HTTP entity wrapper to decompress GZipped HTTP responses
*/
private static class GzipDecompressingEntity extends HttpEntityWrapper {
public GzipDecompressingEntity(final HttpEntity entity) {
super(entity);
}
@Override
public InputStream getContent() throws IOException, IllegalStateException {
// the wrapped entity's getContent() decides about repeatability
InputStream wrappedin = wrappedEntity.getContent();
return new GZIPInputStream(wrappedin);
}
@Override
public long getContentLength() {
// length of ungzipped content is not known
return -1;
}
}
/*
* To convert the InputStream to String we use the BufferedReader.readLine() method. We iterate until the
* BufferedReader return null which means there's no more data to read. Each line will appended to a StringBuilder
* and returned as String.
*
*
* Taken from http://senior.ceng.metu.edu.tr/2009/praeda/2009/01/11/a-simple-restful-client-at-android/
*/
public static String convertStreamToString(InputStream is, String encoding) throws UnsupportedEncodingException {
@ -215,10 +183,10 @@ public class HttpHelper { @@ -215,10 +183,10 @@ public class HttpHelper {
BufferedReader reader = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line = null;
String line;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
sb.append(line).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
@ -272,4 +240,30 @@ public class HttpHelper { @@ -272,4 +240,30 @@ public class HttpHelper {
}
/**
* HTTP entity wrapper to decompress GZipped HTTP responses
*/
private static class GzipDecompressingEntity extends HttpEntityWrapper {
public GzipDecompressingEntity(final HttpEntity entity) {
super(entity);
}
@Override
public InputStream getContent() throws IOException, IllegalStateException {
// the wrapped entity's getContent() decides about repeatability
InputStream wrappedin = wrappedEntity.getContent();
return new GZIPInputStream(wrappedin);
}
@Override
public long getContentLength() {
// length of ungzipped content is not known
return -1;
}
}
}

44
app/src/main/java/org/transdroid/daemon/util/ITLogger.java

@ -1,44 +0,0 @@ @@ -1,44 +0,0 @@
/*
* This file is part of Transdroid <http://www.transdroid.org>
*
* Transdroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Transdroid is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.transdroid.daemon.util;
/**
* Interface that should be implemented for any logging
* information to get from the daemons. Applications using
* this library should attach an instance using
* <code>TLog.setLogger(ITLogger)</code>
*
* @author erickok
*/
public interface ITLogger {
/**
* Send a DEBUG log message.
* @param self Unique source tag, identifying the part of Transdroid it happens in
* @param msg The debug message to log
*/
public abstract void d(String self, String msg);
/**
* Send an ERROR log message.
* @param self Unique source tag, identifying the part of Transdroid it happens in
* @param msg The error message to log
*/
public abstract void e(String self, String msg);
}

77
app/src/main/java/org/transdroid/daemon/util/Pair.java

@ -1,77 +0,0 @@ @@ -1,77 +0,0 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.transdroid.daemon.util;
/**
* Container to ease passing around a tuple of two objects. This object provides a sensible
* implementation of equals(), returning true if equals() is true on each of the contained
* objects.
*/
public class Pair<F, S> {
public final F first;
public final S second;
/**
* Constructor for a Pair. If either are null then equals() and hashCode() will throw
* a NullPointerException.
* @param first the first object in the Pair
* @param second the second object in the pair
*/
public Pair(F first, S second) {
this.first = first;
this.second = second;
}
/**
* Checks the two objects for equality by delegating to their respective equals() methods.
* @param o the Pair to which this one is to be checked for equality
* @return true if the underlying objects of the Pair are both considered equals()
*/
@SuppressWarnings("unchecked")
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Pair)) return false;
final Pair<F, S> other;
try {
other = (Pair<F, S>) o;
} catch (ClassCastException e) {
return false;
}
return first.equals(other.first) && second.equals(other.second);
}
/**
* Compute a hash code using the hash codes of the underlying objects
* @return a hashcode of the Pair
*/
public int hashCode() {
int result = 17;
result = 31 * result + first.hashCode();
result = 31 * result + second.hashCode();
return result;
}
/**
* Convenience method for creating an appropriately typed pair.
* @param a the first object in the Pair
* @param b the second object in the pair
* @return a Pair that is templatized with the types of a and b
*/
public static <A, B> Pair <A, B> create(A a, B b) {
return new Pair<A, B>(a, b);
}
}

19
app/src/main/java/org/transdroid/daemon/util/SelfSignedTrustManager.java

@ -27,7 +27,6 @@ import javax.net.ssl.X509TrustManager; @@ -27,7 +27,6 @@ import javax.net.ssl.X509TrustManager;
public class SelfSignedTrustManager implements X509TrustManager {
private static final X509Certificate[] acceptedIssuers = new X509Certificate[]{};
private static final String LOG_NAME = "TrustManager";
private String certKey = null;
@ -50,9 +49,9 @@ public class SelfSignedTrustManager implements X509TrustManager { @@ -50,9 +49,9 @@ public class SelfSignedTrustManager implements X509TrustManager {
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
StringBuffer buf = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; ++i) {
buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]);
buf.append(hexDigits[bytes[i] & 0x0f]);
for (byte aByte : bytes) {
buf.append(hexDigits[(aByte & 0xf0) >> 4]);
buf.append(hexDigits[aByte & 0x0f]);
}
return buf.toString();
@ -75,15 +74,15 @@ public class SelfSignedTrustManager implements X509TrustManager { @@ -75,15 +74,15 @@ public class SelfSignedTrustManager implements X509TrustManager {
// Assume self-signed root is okay?
X509Certificate sslCert = chain[0];
String thumbprint = SelfSignedTrustManager.getThumbPrint(sslCert);
DLog.d(LOG_NAME, thumbprint);
if (ourKey.equalsIgnoreCase(thumbprint)) {
return;
} else {
CertificateException certificateException =
new CertificateException("Certificate key [" + thumbprint + "] doesn't match expected value.");
DLog.e(SelfSignedTrustManager.class.getSimpleName(), certificateException.toString());
throw certificateException;
}
CertificateException certificateException =
new CertificateException("Certificate key [" + thumbprint + "] doesn't match expected value.");
//Log.e(SelfSignedTrustManager.class.getSimpleName(), certificateException.toString());
throw certificateException;
} catch (NoSuchAlgorithmException e) {
throw new CertificateException("Unable to check self-signed cert, unknown algorithm. " + e.toString());
}

3
app/src/main/java/org/transdroid/daemon/util/TlsSniSocketFactory.java

@ -19,6 +19,7 @@ package org.transdroid.daemon.util; @@ -19,6 +19,7 @@ package org.transdroid.daemon.util;
import android.annotation.TargetApi;
import android.net.SSLCertificateSocketFactory;
import android.os.Build;
import android.util.Log;
import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.conn.ssl.StrictHostnameVerifier;
@ -130,7 +131,7 @@ public class TlsSniSocketFactory implements LayeredSocketFactory { @@ -130,7 +131,7 @@ public class TlsSniSocketFactory implements LayeredSocketFactory {
java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class);
setHostnameMethod.invoke(ssl, host);
} catch (Exception e) {
DLog.d(TlsSniSocketFactory.class.getSimpleName(), "SNI not usable: " + e);
Log.d(TlsSniSocketFactory.class.getSimpleName(), "SNI not usable: " + e);
}
}

Loading…
Cancel
Save