diff --git a/app/build.gradle b/app/build.gradle index b9f0be42..ef8a2aef 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ff92f30d..2f24b2c4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -245,7 +245,7 @@ - + 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 @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 @Override public void updatePriority(Torrent torrent, List files, Priority priority) { DaemonTaskResult result = SetFilePriorityTask.create(currentConnection, torrent, priority, - new ArrayList(files)).execute(); + new ArrayList(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 @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())), diff --git a/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java b/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java index 40431852..484709d8 100644 --- a/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java +++ b/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java @@ -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 /** * 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 newTorrentFiles) { // Check if these are actually the details of the torrent we are now showing diff --git a/app/src/main/java/org/transdroid/core/gui/ServerStatusView.java b/app/src/main/java/org/transdroid/core/gui/ServerStatusView.java index 6361ac00..7ed6b0c1 100644 --- a/app/src/main/java/org/transdroid/core/gui/ServerStatusView.java +++ b/app/src/main/java/org/transdroid/core/gui/ServerStatusView.java @@ -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 torrents, boolean dormantAsInactive) { @@ -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; diff --git a/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java b/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java index 5792f2fc..3c112e4d 100644 --- a/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java +++ b/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java @@ -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, // 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, } 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, // 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, @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, 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, 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, @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, @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, @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, @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, @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, 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, 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, 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, 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, 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, @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, @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, @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, @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, @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, 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, @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, @Background @Override public void updateTrackers(Torrent torrent, List 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, @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, @Override public void updatePriority(Torrent torrent, List files, Priority priority) { DaemonTaskResult result = SetFilePriorityTask.create(currentConnection, torrent, priority, - new ArrayList(files)).execute(); + new ArrayList(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, @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, @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); diff --git a/app/src/main/java/org/transdroid/core/gui/TorrentsFragment.java b/app/src/main/java/org/transdroid/core/gui/TorrentsFragment.java index cc63a66c..6bb7a267 100644 --- a/app/src/main/java/org/transdroid/core/gui/TorrentsFragment.java +++ b/app/src/main/java/org/transdroid/core/gui/TorrentsFragment.java @@ -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 /** * 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 // Filter the list of torrents to show according to navigation and text filters ArrayList filteredTorrents = new ArrayList(torrents); - if (filteredTorrents != null && currentNavigationFilter != null) { + if (currentNavigationFilter != null) { // Remove torrents that do not match the selected navigation filter for (Iterator 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 torrentIter = filteredTorrents.iterator(); torrentIter.hasNext();) { if (!torrentIter.next().getName().toLowerCase(Locale.getDefault()) diff --git a/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java b/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java index 68093301..a42d95a4 100644 --- a/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java +++ b/app/src/main/java/org/transdroid/core/gui/lists/DetailsAdapter.java @@ -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; diff --git a/app/src/main/java/org/transdroid/core/gui/log/ErrorLogSender.java b/app/src/main/java/org/transdroid/core/gui/log/ErrorLogSender.java index 4eba0ce3..d6e22b63 100644 --- a/app/src/main/java/org/transdroid/core/gui/log/ErrorLogSender.java +++ b/app/src/main/java/org/transdroid/core/gui/log/ErrorLogSender.java @@ -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 { 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()); } } diff --git a/app/src/main/java/org/transdroid/core/gui/log/Log.java b/app/src/main/java/org/transdroid/core/gui/log/Log.java index d0bd08f5..346bf17f 100644 --- a/app/src/main/java/org/transdroid/core/gui/log/Log.java +++ b/app/src/main/java/org/transdroid/core/gui/log/Log.java @@ -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 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 { 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); } } diff --git a/app/src/main/java/org/transdroid/core/gui/log/LogUncaughtExceptionHandler.java b/app/src/main/java/org/transdroid/core/gui/log/LogUncaughtExceptionHandler.java index d617a001..dbcce1f0 100644 --- a/app/src/main/java/org/transdroid/core/gui/log/LogUncaughtExceptionHandler.java +++ b/app/src/main/java/org/transdroid/core/gui/log/LogUncaughtExceptionHandler.java @@ -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 diff --git a/app/src/main/java/org/transdroid/core/gui/navigation/DialogHelper.java b/app/src/main/java/org/transdroid/core/gui/navigation/DialogHelper.java index 9710e63d..d5b90a94 100644 --- a/app/src/main/java/org/transdroid/core/gui/navigation/DialogHelper.java +++ b/app/src/main/java/org/transdroid/core/gui/navigation/DialogHelper.java @@ -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; diff --git a/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java b/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java index 263d45bd..b6dd48f0 100644 --- a/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java +++ b/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java @@ -49,6 +49,8 @@ public class RssfeedsActivity extends Activity { // Settings and local data @Bean + protected Log log; + @Bean protected ApplicationSettings applicationSettings; protected List loaders; @@ -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 { 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(); diff --git a/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java b/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java index 3eef3ac3..bb43609b 100644 --- a/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java +++ b/app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java @@ -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; diff --git a/app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java b/app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java index 3115bc7e..962c5cc7 100644 --- a/app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java +++ b/app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java @@ -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; 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 { // 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 { // Get checked torrents List checked = new ArrayList(); 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 { 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 { 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 { } mode.finish(); return true; - + } } @@ -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); + } } diff --git a/app/src/main/java/org/transdroid/core/gui/search/SearchActivity.java b/app/src/main/java/org/transdroid/core/gui/search/SearchActivity.java index b950abc5..87773171 100644 --- a/app/src/main/java/org/transdroid/core/gui/search/SearchActivity.java +++ b/app/src/main/java/org/transdroid/core/gui/search/SearchActivity.java @@ -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; 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 { @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 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 { 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 { 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 { 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 { 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 { 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 { // 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 { // 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); diff --git a/app/src/main/java/org/transdroid/core/gui/search/SearchResultsFragment.java b/app/src/main/java/org/transdroid/core/gui/search/SearchResultsFragment.java index a1d27d27..c58953dc 100644 --- a/app/src/main/java/org/transdroid/core/gui/search/SearchResultsFragment.java +++ b/app/src/main/java/org/transdroid/core/gui/search/SearchResultsFragment.java @@ -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; 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 { // 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 { // Get checked torrents List checked = new ArrayList(); 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 { } 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 { } }; + @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); + } } diff --git a/app/src/main/java/org/transdroid/core/gui/settings/MainSettingsActivity.java b/app/src/main/java/org/transdroid/core/gui/settings/MainSettingsActivity.java index 289a0e54..0f4d02a9 100644 --- a/app/src/main/java/org/transdroid/core/gui/settings/MainSettingsActivity.java +++ b/app/src/main/java/org/transdroid/core/gui/settings/MainSettingsActivity.java @@ -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; 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; 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; 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 { @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 { 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 { 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 { 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 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 { } else { List 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 { // Add existing websearch sites List 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 searchsites = searchHelper.getAvailableSites(); - if (searchsites == null) + if (searchsites == null) { searchsites = new ArrayList(); + } List siteNames = new ArrayList(websearches.size() + searchsites.size()); List siteValues = new ArrayList(websearches.size() + searchsites.size()); for (SearchSite searchSite : searchsites) { @@ -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)); - } - }; - } diff --git a/app/src/main/java/org/transdroid/core/gui/settings/ServerSettingsActivity.java b/app/src/main/java/org/transdroid/core/gui/settings/ServerSettingsActivity.java index e6a88591..1a8b3d59 100644 --- a/app/src/main/java/org/transdroid/core/gui/settings/ServerSettingsActivity.java +++ b/app/src/main/java/org/transdroid/core/gui/settings/ServerSettingsActivity.java @@ -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 diff --git a/app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettingsActivity.java b/app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettingsActivity.java index 4c3b5be7..fd5f6389 100644 --- a/app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettingsActivity.java +++ b/app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettingsActivity.java @@ -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; @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 { } 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 { edit.putString("seedbox_xirvikshared_rpc_" + key, result); pref.setSummary(result); } - edit.commit(); + edit.apply(); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) diff --git a/app/src/main/java/org/transdroid/core/service/AppUpdateService.java b/app/src/main/java/org/transdroid/core/service/AppUpdateService.java index 3225812b..bcf02df2 100644 --- a/app/src/main/java/org/transdroid/core/service/AppUpdateService.java +++ b/app/src/main/java/org/transdroid/core/service/AppUpdateService.java @@ -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 { 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 { 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 { // 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 { // 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 { } 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 { * @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("\\|"); diff --git a/app/src/main/java/org/transdroid/core/service/BootReceiver.java b/app/src/main/java/org/transdroid/core/service/BootReceiver.java index d90e499a..2c16852a 100644 --- a/app/src/main/java/org/transdroid/core/service/BootReceiver.java +++ b/app/src/main/java/org/transdroid/core/service/BootReceiver.java @@ -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; 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 { 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 { 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 { } } + @Override + public void onReceive(Context context, Intent intent) { + startBackgroundServices(context, false); + startAppUpdatesService(context); + } + } diff --git a/app/src/main/java/org/transdroid/core/service/ControlService.java b/app/src/main/java/org/transdroid/core/service/ControlService.java index 4a038dae..be5ce6d6 100644 --- a/app/src/main/java/org/transdroid/core/service/ControlService.java +++ b/app/src/main/java/org/transdroid/core/service/ControlService.java @@ -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; 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 { 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 { @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 { 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 { 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 { // 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 { // 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); - + } } diff --git a/app/src/main/java/org/transdroid/core/service/RssCheckerService.java b/app/src/main/java/org/transdroid/core/service/RssCheckerService.java index c5cd5459..637360f7 100644 --- a/app/src/main/java/org/transdroid/core/service/RssCheckerService.java +++ b/app/src/main/java/org/transdroid/core/service/RssCheckerService.java @@ -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; 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; @EService public class RssCheckerService extends IntentService { + @Bean + protected Log log; @Bean protected ConnectivityHelper connectivityHelper; @Bean @@ -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 { 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 { 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 { } // 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()); } diff --git a/app/src/main/java/org/transdroid/core/service/ServerCheckerService.java b/app/src/main/java/org/transdroid/core/service/ServerCheckerService.java index 22334832..cf3f9dec 100644 --- a/app/src/main/java/org/transdroid/core/service/ServerCheckerService.java +++ b/app/src/main/java/org/transdroid/core/service/ServerCheckerService.java @@ -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 { 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 { // 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 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 { 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()); diff --git a/app/src/main/java/org/transdroid/core/widget/ListWidgetConfigActivity.java b/app/src/main/java/org/transdroid/core/widget/ListWidgetConfigActivity.java index 0083c95e..783b46a4 100644 --- a/app/src/main/java/org/transdroid/core/widget/ListWidgetConfigActivity.java +++ b/app/src/main/java/org/transdroid/core/widget/ListWidgetConfigActivity.java @@ -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; 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; 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 { protected ListView torrentsList; @ViewById protected View navigationView, serverstatusView; - private List 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 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 { } // 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 { for (TorrentsSortBy order : TorrentsSortBy.values()) { sortOrders.add(new SortByListItem(this, order)); } - serverSpinner.setAdapter(new SimpleListItemSpinnerAdapter(this, 0, applicationSettings - .getAllServerSettings())); - filterSpinner.setAdapter(new SimpleListItemSpinnerAdapter(this, 0, StatusType - .getAllStatusTypes(this))); + serverSpinner.setAdapter( + new SimpleListItemSpinnerAdapter(this, 0, applicationSettings.getAllServerSettings())); + filterSpinner.setAdapter( + new SimpleListItemSpinnerAdapter(this, 0, StatusType.getAllStatusTypes(this))); sortSpinner.setAdapter(new SimpleListItemSpinnerAdapter(this, 0, sortOrders)); // TODO: Update to AndroidAnnotations 3.0 and use @CheckedChanged reverseorderCheckBox.setOnCheckedChangeListener(reverseorderCheckedChanged); @@ -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 { 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 filteredTorrents = new ArrayList(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 { 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 { 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(); - - } - }; - } diff --git a/app/src/main/java/org/transdroid/core/widget/ListWidgetProvider.java b/app/src/main/java/org/transdroid/core/widget/ListWidgetProvider.java index 2684d677..f7a0c3a2 100644 --- a/app/src/main/java/org/transdroid/core/widget/ListWidgetProvider.java +++ b/app/src/main/java/org/transdroid/core/widget/ListWidgetProvider.java @@ -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; 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 { @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 { 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 { 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 { } + @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); + } + } + } diff --git a/app/src/main/java/org/transdroid/core/widget/ListWidgetViewsService.java b/app/src/main/java/org/transdroid/core/widget/ListWidgetViewsService.java index 19d7bd73..e15f0506 100644 --- a/app/src/main/java/org/transdroid/core/widget/ListWidgetViewsService.java +++ b/app/src/main/java/org/transdroid/core/widget/ListWidgetViewsService.java @@ -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; 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 { private final Context context; private final int appWidgetId; + private final Log log; private List 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 { 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 { ArrayList filteredTorrents = new ArrayList(); List 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 { // 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 { 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 { 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 { @Override public void onDestroy() { - if (torrents != null) + if (torrents != null) { torrents.clear(); + } torrents = null; } @@ -220,8 +233,9 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @Override public int getCount() { - if (torrents == null) + if (torrents == null) { return 0; + } return torrents.size(); } diff --git a/app/src/main/java/org/transdroid/daemon/AlphanumComparator.java b/app/src/main/java/org/transdroid/daemon/AlphanumComparator.java index d4db86dc..5ab88490 100644 --- a/app/src/main/java/org/transdroid/daemon/AlphanumComparator.java +++ b/app/src/main/java/org/transdroid/daemon/AlphanumComparator.java @@ -29,12 +29,12 @@ import java.util.Comparator; public class AlphanumComparator implements Comparator { - 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 { 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 { 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(); diff --git a/app/src/main/java/org/transdroid/daemon/Aria2c/Aria2Adapter.java b/app/src/main/java/org/transdroid/daemon/Aria2c/Aria2Adapter.java index 29c27eac..b2e62534 100644 --- a/app/src/main/java/org/transdroid/daemon/Aria2c/Aria2Adapter.java +++ b/app/src/main/java/org/transdroid/daemon/Aria2c/Aria2Adapter.java @@ -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; 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; 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 { } @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 torrents = new ArrayList(); - 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 torrents = new ArrayList(); + 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 { } - 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 { } // 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 { 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 { 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 { 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 { List errors = new ArrayList(); 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 { // 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; } } diff --git a/app/src/main/java/org/transdroid/daemon/BitComet/BitCometAdapter.java b/app/src/main/java/org/transdroid/daemon/BitComet/BitCometAdapter.java index 1043d171..098d9e24 100644 --- a/app/src/main/java/org/transdroid/daemon/BitComet/BitCometAdapter.java +++ b/app/src/main/java/org/transdroid/daemon/BitComet/BitCometAdapter.java @@ -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; 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; 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 + *

+ * 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 { 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("", 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 params = new ArrayList(); 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 { 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 parseHttpTorrents(String response) throws DaemonException { - + private ArrayList parseHttpTorrents(Log log, String response) throws DaemonException { + ArrayList torrents = new ArrayList(); - + try { - - // Find, prepare and split substring with HTML tag TABLE - String[] parts = response.substring(response.indexOf("")).replaceAll("", "").replaceAll("", "").replaceAll("\n", "").split(""); - - for (int i=2; i")).replaceAll("", "") + .replaceAll("", "").replaceAll("\n", "").split(""); + + for (int i = 2; i < parts.length; i++) { + String[] subParts = parts[i].replaceAll("", "")+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("

Value
")).split(""); - comment = sumParts[7].substring(sumParts[7].indexOf("")+4, sumParts[7].indexOf("")); - - // 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("")+4, sumParts[idx].indexOf(""))); - //sizeDone = convertSize(sumParts[sizeIdx].substring(sumParts[sizeIdx].indexOf("")+4, sumParts[sizeIdx].indexOf(" ("))); - sizeUp = convertSize(sumParts[sizeIdx+1].substring(sumParts[sizeIdx+1].indexOf("")+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("
Value
")) + .split(""); + comment = sumParts[7].substring(sumParts[7].indexOf("") + 4, sumParts[7].indexOf("")); + + // 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("") + 4, sumParts[idx].indexOf(""))); + //sizeDone = convertSize(sumParts[sizeIdx].substring(sumParts[sizeIdx].indexOf("")+4, sumParts[sizeIdx].indexOf(" ("))); + sizeUp = convertSize(sumParts[sizeIdx + 1] + .substring(sumParts[sizeIdx + 1].indexOf("") + 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 { 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 parseXmlTorrents(String response) throws DaemonException { - + private ArrayList parseXmlTorrents(String response) throws DaemonException { + ArrayList torrents = new ArrayList(); try { @@ -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 { 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 { 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. 'BT' next = xpp.next(); if (next == XmlPullParser.TEXT) { @@ -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 { 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 parseHttpTorrentFiles(String response, String hash) throws DaemonException { - + // Parse response ArrayList torrentfiles = new ArrayList(); - - try { - - String[] files = response.substring(response.indexOf("Operation Method")+27, response.lastIndexOf("")).replaceAll("", "").replaceAll("", "").split(""); - + + try { + + String[] files = response.substring(response.indexOf("Operation Method") + 27, + response.lastIndexOf("")).replaceAll("", "").replaceAll("", "").split(""); + for (int i = 1; i < files.length; i++) { - - String[] fileDetails = files[i].replace(">","").split("", "").split(" - * */ 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 { // 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 { // 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 { } } - 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 { 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 { 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 { /** * 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 { 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 { 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 { 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 { 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; } } diff --git a/app/src/main/java/org/transdroid/daemon/Daemon.java b/app/src/main/java/org/transdroid/daemon/Daemon.java index e4d5cc92..c19fe717 100644 --- a/app/src/main/java/org/transdroid/daemon/Daemon.java +++ b/app/src/main/java/org/transdroid/daemon/Daemon.java @@ -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 { } 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) { diff --git a/app/src/main/java/org/transdroid/daemon/Deluge/DelugeAdapter.java b/app/src/main/java/org/transdroid/daemon/Deluge/DelugeAdapter.java index 2c7972dd..2d0be0a0 100644 --- a/app/src/main/java/org/transdroid/daemon/Deluge/DelugeAdapter.java +++ b/app/src/main/java/org/transdroid/daemon/Deluge/DelugeAdapter.java @@ -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; 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; 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 { 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 { 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 { // 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 { 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 { 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 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 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 { 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 'Deluge: Web UI 1.3.6' @@ -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 { } } } 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 { 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 { } } } - + // 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 { if (!cookiePresent) { httpclient.getCookieStore().addCookie(sessionCookie); } - + // Execute HttpResponse response = httpclient.execute(httppost); @@ -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 { 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 parseJsonRetrieveTorrents(JSONObject response) throws JSONException, DaemonException { @@ -625,43 +632,47 @@ public class DelugeAdapter implements IDaemonAdapter { // Parse response ArrayList torrents = new ArrayList(); 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 { private ArrayList