diff --git a/android/res/values/changelog.xml b/android/res/values/changelog.xml index 923175ba..49e661a5 100644 --- a/android/res/values/changelog.xml +++ b/android/res/values/changelog.xml @@ -1,6 +1,16 @@ +Transdroid 1.1.12\n +- Option to disable app update checking\n +- Transmission: support non-Western characters in directories\n +- Deluge: don\'t treat trackers errors as blocking\n +- Added Pirate Bay mirror search support\n +\n +Transdroid 1.1.11\n +- rTorrent: fix crash with creationtime and non-i8 dialect\n +- Fixed picking up local .torrent files\n +\n Transdroid 1.1.10\n - Updated translations (thanks to all translators)\n - Special thanks Tom Briden for the following contributions:\n diff --git a/lite/res/drawable/loading_progress.xml b/lite/res/drawable/loading_progress.xml new file mode 100644 index 00000000..df2175ef --- /dev/null +++ b/lite/res/drawable/loading_progress.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/lite/res/layout/fragment_details.xml b/lite/res/layout/fragment_details.xml index 6c17e9e2..d627b53b 100644 --- a/lite/res/layout/fragment_details.xml +++ b/lite/res/layout/fragment_details.xml @@ -23,6 +23,6 @@ android:text="@string/navigation_emptydetails" android:gravity="center" android:textIsSelectable="false" - android:visibility="gone" /> + android:visibility="visible" /> \ No newline at end of file diff --git a/lite/res/layout/fragment_torrents.xml b/lite/res/layout/fragment_torrents.xml index 634058ed..869766dc 100644 --- a/lite/res/layout/fragment_torrents.xml +++ b/lite/res/layout/fragment_torrents.xml @@ -11,6 +11,13 @@ android:listSelector="@drawable/selectable_background_transdroid" android:visibility="gone" /> + + + + + + + + + \ No newline at end of file diff --git a/lite/res/menu/fragment_details.xml b/lite/res/menu/fragment_details.xml index aea9239e..5b32d61d 100644 --- a/lite/res/menu/fragment_details.xml +++ b/lite/res/menu/fragment_details.xml @@ -13,12 +13,17 @@ + android:showAsAction="ifRoom" + android:title="@string/action_start"> + + + + + Settings Help Start + Normal start + Force start Stop Resume Pause @@ -39,6 +41,9 @@ Download using (S)FTP Remove settings + Transdroid allows you to monitor and manage the torrent client you run at home or on your seedbox. Setting things up can be a bit tricky, but we offer step-by-step guides and promise it\'ll be worth it! + Connected, but no torrent are active within the current filter + Select a torrent to view its details SERVERS STATUS LABELS @@ -47,10 +52,8 @@ Uploading Active Inactive - Connected, but no torrent are active within the current filter - Select a torrent to view its details - Transdroid allows you to monitor and manage the torrent client you run at home or on your seedbox. Setting things up can be a bit tricky, but we offer step-by-step guides and promise it\'ll be worth it! + Status: %1%s Waiting to check… Verifying local data… Waiting to download %s @@ -96,6 +99,7 @@ Torrents stopped Torrents started (refreshing) Trackers updated + Label set to \'%1$s\' Torrent moved to \'%1$s\' Torrent search diff --git a/lite/src/org/transdroid/core/gui/DetailsActivity.java b/lite/src/org/transdroid/core/gui/DetailsActivity.java index 68076526..0f597715 100644 --- a/lite/src/org/transdroid/core/gui/DetailsActivity.java +++ b/lite/src/org/transdroid/core/gui/DetailsActivity.java @@ -24,12 +24,21 @@ import org.transdroid.daemon.TorrentDetails; import org.transdroid.daemon.TorrentFile; import org.transdroid.daemon.task.DaemonTaskFailureResult; import org.transdroid.daemon.task.DaemonTaskResult; +import org.transdroid.daemon.task.DaemonTaskSuccessResult; import org.transdroid.daemon.task.GetFileListTask; import org.transdroid.daemon.task.GetFileListTaskSuccessResult; import org.transdroid.daemon.task.GetTorrentDetailsTask; import org.transdroid.daemon.task.GetTorrentDetailsTaskSuccessResult; +import org.transdroid.daemon.task.PauseTask; +import org.transdroid.daemon.task.RemoveTask; +import org.transdroid.daemon.task.ResumeTask; import org.transdroid.daemon.task.RetrieveTask; import org.transdroid.daemon.task.RetrieveTaskSuccessResult; +import org.transdroid.daemon.task.SetDownloadLocationTask; +import org.transdroid.daemon.task.SetLabelTask; +import org.transdroid.daemon.task.SetTrackersTask; +import org.transdroid.daemon.task.StartTask; +import org.transdroid.daemon.task.StopTask; import android.annotation.TargetApi; import android.content.Intent; @@ -40,7 +49,7 @@ import com.actionbarsherlock.app.SherlockFragmentActivity; @EActivity(R.layout.activity_details) @OptionsMenu(R.menu.activity_details) -public class DetailsActivity extends SherlockFragmentActivity { +public class DetailsActivity extends SherlockFragmentActivity implements TorrentTasksExecutor { @Extra @InstanceState @@ -102,12 +111,6 @@ public class DetailsActivity extends SherlockFragmentActivity { } } - @UiThread - protected void onTorrentsRetrieved(List torrents, List labels) { - // Update the details fragment - fragmentDetails.perhapsUpdateTorrent(torrents); - } - @Background protected void refreshTorrentDetails() { if (!Daemon.supportsFineDetails(torrent.getDaemon())) @@ -120,12 +123,6 @@ public class DetailsActivity extends SherlockFragmentActivity { } } - @UiThread - protected void onTorrentDetailsRetrieved(TorrentDetails torrentDetails) { - // Update the details fragment with the new fine details for the shown torrent - fragmentDetails.updateTorrentDetails(torrentDetails); - } - @Background protected void refreshTorrentFiles() { if (!Daemon.supportsFileListing(torrent.getDaemon())) @@ -138,12 +135,106 @@ public class DetailsActivity extends SherlockFragmentActivity { } } + @Background + @Override + public void resumeTorrent(Torrent torrent) { + torrent.mimicResume(); + DaemonTaskResult result = ResumeTask.create(currentConnection, torrent).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_resumed); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void pauseTorrent(Torrent torrent) { + torrent.mimicPause(); + DaemonTaskResult result = PauseTask.create(currentConnection, torrent).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_paused); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void startTorrent(Torrent torrent, boolean forced) { + torrent.mimicStart(); + DaemonTaskResult result = StartTask.create(currentConnection, torrent, forced).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_started); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void stopTorrent(Torrent torrent) { + torrent.mimicStop(); + DaemonTaskResult result = StopTask.create(currentConnection, torrent).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_stopped); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void removeTorrent(Torrent torrent, boolean withData) { + DaemonTaskResult result = RemoveTask.create(currentConnection, torrent, withData).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, withData ? R.string.result_removed_with_data + : R.string.result_removed); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void updateLabel(Torrent torrent, String newLabel) { + torrent.mimicNewLabel(newLabel); + DaemonTaskResult result = SetLabelTask.create(currentConnection, torrent, newLabel).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_labelset, newLabel); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void updateTrackers(Torrent torrent, List newTrackers) { + DaemonTaskResult result = SetTrackersTask.create(currentConnection, torrent, newTrackers).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_trackersupdated); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void updateLocation(Torrent torrent, String newLocation) { + DaemonTaskResult result = SetDownloadLocationTask.create(currentConnection, torrent, newLocation).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_locationset, newLocation); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + @UiThread - protected void onTorrentFilesRetrieved(List torrentFiles) { - // Update the details fragment with the newly retrieved list of files - fragmentDetails.updateTorrentFiles(new ArrayList(torrentFiles)); + protected void onTaskSucceeded(DaemonTaskSuccessResult result, int successMessageId, String... messageParams) { + // TODO: Properly report this success + Toast.makeText(this, getString(successMessageId, (Object[]) messageParams), Toast.LENGTH_LONG).show(); } - + @UiThread protected void onCommunicationError(DaemonTaskFailureResult result) { // TODO: Properly report this error @@ -151,4 +242,22 @@ public class DetailsActivity extends SherlockFragmentActivity { Toast.LENGTH_LONG).show(); } + @UiThread + protected void onTorrentsRetrieved(List torrents, List labels) { + // Update the details fragment + fragmentDetails.perhapsUpdateTorrent(torrents); + } + + @UiThread + protected void onTorrentDetailsRetrieved(TorrentDetails torrentDetails) { + // Update the details fragment with the new fine details for the shown torrent + fragmentDetails.updateTorrentDetails(torrentDetails); + } + + @UiThread + protected void onTorrentFilesRetrieved(List torrentFiles) { + // Update the details fragment with the newly retrieved list of files + fragmentDetails.updateTorrentFiles(new ArrayList(torrentFiles)); + } + } diff --git a/lite/src/org/transdroid/core/gui/DetailsFragment.java b/lite/src/org/transdroid/core/gui/DetailsFragment.java index bb4738a4..685a5527 100644 --- a/lite/src/org/transdroid/core/gui/DetailsFragment.java +++ b/lite/src/org/transdroid/core/gui/DetailsFragment.java @@ -17,6 +17,7 @@ import org.transdroid.daemon.Torrent; import org.transdroid.daemon.TorrentDetails; import org.transdroid.daemon.TorrentFile; +import android.view.View; import android.widget.TextView; import com.actionbarsherlock.app.SherlockFragment; @@ -50,7 +51,6 @@ public class DetailsFragment extends SherlockFragment { protected void init() { detailsList.setAdapter(new DetailsAdapter(getActivity())); - detailsList.setEmptyView(emptyText); if (torrent != null) updateTorrent(torrent); if (torrentDetails != null) @@ -68,6 +68,9 @@ public class DetailsFragment extends SherlockFragment { clear(); this.torrent = newTorrent; ((DetailsAdapter) detailsList.getAdapter()).updateTorrent(newTorrent); + // Make the list (with detials header) visible + detailsList.setVisibility(View.VISIBLE); + emptyText.setVisibility(View.INVISIBLE); // Also update the available actions in the action bar getActivity().supportInvalidateOptionsMenu(); } @@ -112,7 +115,9 @@ public class DetailsFragment extends SherlockFragment { * Clear the screen by fully clearing the internal merge list (with header and other lists) */ public void clear() { - ((DetailsAdapter)detailsList.getAdapter()).clear(); + detailsList.setAdapter(new DetailsAdapter(getActivity())); + detailsList.setVisibility(View.INVISIBLE); + emptyText.setVisibility(View.VISIBLE); torrent = null; torrentDetails = null; torrentFiles = null; @@ -136,20 +141,59 @@ public class DetailsFragment extends SherlockFragment { menu.findItem(R.id.action_updatetrackers).setVisible(setTrackers); } - - @OptionsItem(R.id.action_start) - protected void startTorrent() { - + + @OptionsItem(R.id.action_resume) + protected void resumeTorrent() { + getTasksExecutor().resumeTorrent(torrent); } - - public interface DetailsTasksExecutor { - void resumeTorrent(Torrent torrent); - void pauseTorrent(Torrent torrent); - void startTorrent(Torrent torrent); - void stopTorrent(Torrent torrent); - void removeTorrent(Torrent torrent, boolean withData); - void setLabel(Torrent torrent); - void updateTrackers(Torrent torrent); + + @OptionsItem(R.id.action_pause) + protected void pauseTorrent() { + getTasksExecutor().pauseTorrent(torrent); + } + + @OptionsItem(R.id.action_start_default) + protected void startTorrentDefault() { + getTasksExecutor().startTorrent(torrent, false); + } + + @OptionsItem(R.id.action_start_forced) + protected void startTorrentForced() { + getTasksExecutor().startTorrent(torrent, true); + } + + @OptionsItem(R.id.action_stop) + protected void stopTorrent() { + getTasksExecutor().stopTorrent(torrent); + } + + @OptionsItem(R.id.action_remove_default) + protected void removeTorrentDefault() { + getTasksExecutor().removeTorrent(torrent, false); + } + + @OptionsItem(R.id.action_remove_withdata) + protected void removeTorrentWithData() { + getTasksExecutor().removeTorrent(torrent, true); + } + + @OptionsItem(R.id.action_setlabel) + protected void setLabel() { + // TODO: Show label selection dialog + } + + @OptionsItem(R.id.action_updatetrackers) + protected void updateTrackers() { + // TODO: Show trackers edit dialog + } + + /** + * Returns the object responsible for executing torrent tasks against a connected server + * @return The executor for tasks on some torrent + */ + private TorrentTasksExecutor getTasksExecutor() { + // NOTE: Assumes the activity implements all the required torrent tasks + return (TorrentTasksExecutor) getActivity(); } } diff --git a/lite/src/org/transdroid/core/gui/TorrentTasksExecutor.java b/lite/src/org/transdroid/core/gui/TorrentTasksExecutor.java new file mode 100644 index 00000000..c8bc7e55 --- /dev/null +++ b/lite/src/org/transdroid/core/gui/TorrentTasksExecutor.java @@ -0,0 +1,16 @@ +package org.transdroid.core.gui; + +import java.util.List; + +import org.transdroid.daemon.Torrent; + +public interface TorrentTasksExecutor { + void resumeTorrent(Torrent torrent); + void pauseTorrent(Torrent torrent); + void startTorrent(Torrent torrent, boolean forced); + void stopTorrent(Torrent torrent); + void removeTorrent(Torrent torrent, boolean withData); + void updateLabel(Torrent torrent, String newLabel); + void updateTrackers(Torrent torrent, List newTrackers); + void updateLocation(Torrent torrent, String newLocation); +} \ No newline at end of file diff --git a/lite/src/org/transdroid/core/gui/TorrentsActivity.java b/lite/src/org/transdroid/core/gui/TorrentsActivity.java index d8cc073c..0493cc99 100644 --- a/lite/src/org/transdroid/core/gui/TorrentsActivity.java +++ b/lite/src/org/transdroid/core/gui/TorrentsActivity.java @@ -17,16 +17,14 @@ import org.androidannotations.annotations.ViewById; import org.transdroid.core.R; import org.transdroid.core.app.settings.ApplicationSettings; import org.transdroid.core.app.settings.ServerSetting; -import org.transdroid.core.gui.DetailsFragment.DetailsTasksExecutor; -import org.transdroid.core.gui.TorrentsFragment.TorrentsTasksExecutor; import org.transdroid.core.gui.lists.LocalTorrent; import org.transdroid.core.gui.lists.SimpleListItem; import org.transdroid.core.gui.navigation.FilterListAdapter; import org.transdroid.core.gui.navigation.FilterListAdapter_; -import org.transdroid.core.gui.navigation.Label; +import org.transdroid.core.gui.navigation.NavigationFilter; import org.transdroid.core.gui.navigation.NavigationHelper; +import org.transdroid.core.gui.navigation.NavigationSelectionView.NavigationFilterManager; import org.transdroid.core.gui.navigation.StatusType; -import org.transdroid.core.gui.navigation.StatusType.StatusTypeFilter; import org.transdroid.core.gui.settings.MainSettingsActivity_; import org.transdroid.daemon.Daemon; import org.transdroid.daemon.IDaemonAdapter; @@ -34,9 +32,19 @@ import org.transdroid.daemon.Torrent; import org.transdroid.daemon.task.DaemonTaskFailureResult; import org.transdroid.daemon.task.DaemonTaskResult; import org.transdroid.daemon.task.DaemonTaskSuccessResult; +import org.transdroid.daemon.task.GetStatsTask; +import org.transdroid.daemon.task.GetStatsTaskSuccessResult; +import org.transdroid.daemon.task.PauseTask; +import org.transdroid.daemon.task.RemoveTask; import org.transdroid.daemon.task.ResumeTask; import org.transdroid.daemon.task.RetrieveTask; import org.transdroid.daemon.task.RetrieveTaskSuccessResult; +import org.transdroid.daemon.task.SetAlternativeModeTask; +import org.transdroid.daemon.task.SetDownloadLocationTask; +import org.transdroid.daemon.task.SetLabelTask; +import org.transdroid.daemon.task.SetTrackersTask; +import org.transdroid.daemon.task.StartTask; +import org.transdroid.daemon.task.StopTask; import android.annotation.TargetApi; import android.app.SearchManager; @@ -56,7 +64,7 @@ import com.actionbarsherlock.widget.SearchView; @EActivity(R.layout.activity_torrents) @OptionsMenu(R.menu.activity_torrents) -public class TorrentsActivity extends SherlockFragmentActivity implements OnNavigationListener, DetailsTasksExecutor, TorrentsTasksExecutor { +public class TorrentsActivity extends SherlockFragmentActivity implements OnNavigationListener, TorrentTasksExecutor, NavigationFilterManager { // Navigation components @Bean @@ -75,6 +83,8 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi boolean firstStart = true; private IDaemonAdapter currentConnection = null; @InstanceState + protected NavigationFilter currentFilter = null; + @InstanceState protected boolean turleModeEnabled = false; // Torrents list components @@ -91,7 +101,7 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi // Set up navigation, with an action bar spinner and possibly (if room) with a filter list getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); getSupportActionBar().setHomeButtonEnabled(false); - navigationSpinnerAdapter = FilterListAdapter_.getInstance_(this); + navigationSpinnerAdapter = FilterListAdapter_.getInstance_(this).setNavigationFilterManager(this); // Servers are always added to the action bar spinner navigationSpinnerAdapter.updateServers(applicationSettings.getServerSettings()); getSupportActionBar().setListNavigationCallbacks(navigationSpinnerAdapter, this); @@ -240,21 +250,35 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi // Update connection to the newly selected server and refresh currentConnection = server.createServerAdapter(); + applicationSettings.setLastUsedServer(server); clearScreens(); refreshTorrents(); + return; } - if (item instanceof StatusTypeFilter) { - // TODO: Update the torrent list view - } - - if (item instanceof Label) { - // TODO: Update the torrent list view + // Status type or label selection - both of which are navigation filters + if (item instanceof NavigationFilter) { + currentFilter = (NavigationFilter) item; + fragmentTorrents.applyFilter(currentFilter); + // Clear the details view + if (fragmentDetails != null) { + fragmentDetails.clear(); + } } } + @Override + public String getActiveFilterText() { + return currentFilter.getName(); + } + + @Override + public String getActiveServerText() { + return currentConnection.getSettings().getName(); + } + /** * If required, add torrents, switch to a specific server, etc. */ @@ -265,7 +289,17 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi @OptionsItem(R.id.action_refresh) protected void refreshScreen() { refreshTorrents(); - // TODO: Retrieve turtle mode status + getAdditionalStats(); + } + + @OptionsItem(R.id.action_enableturtle) + protected void enableTurtleMode() { + updateTurtleMode(true); + } + + @OptionsItem(R.id.action_disableturtle) + protected void disableTurtleMode() { + updateTurtleMode(false); } @OptionsItem(R.id.action_settings) @@ -291,28 +325,25 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi } } - @UiThread - protected void onTorrentsRetrieved(List torrents, List labels) { - // Report the newly retrieved list of torrents to the torrents fragment - fragmentTorrents.updateTorrents(new ArrayList(torrents)); - // Update the details fragment if the currently shown torrent is in the newly retrieved list - if (fragmentDetails != null) { - fragmentDetails.perhapsUpdateTorrent(torrents); + @Background + protected void getAdditionalStats() { + DaemonTaskResult result = GetStatsTask.create(currentConnection).execute(); + if (result instanceof GetStatsTaskSuccessResult) { + onTurtleModeRetrieved(((GetStatsTaskSuccessResult) result).isAlternativeModeEnabled()); + } else { + onCommunicationError((DaemonTaskFailureResult)result); } - // TODO: Update local list of labels - } - - @UiThread - protected void onTaskSucceeded(DaemonTaskSuccessResult result, int successMessageId, Torrent target) { - // TODO: Properly report this success - Toast.makeText(this, getString(successMessageId, target.getName()),Toast.LENGTH_LONG).show(); } - - @UiThread - protected void onCommunicationError(DaemonTaskFailureResult result) { - // TODO: Properly report this error - Toast.makeText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), - Toast.LENGTH_LONG).show(); + + @Background + protected void updateTurtleMode(boolean enable) { + DaemonTaskResult result = SetAlternativeModeTask.create(currentConnection, enable).execute(); + if (result instanceof GetStatsTaskSuccessResult) { + // Success; no need to retrieve it again - just update the visual indicator + onTurtleModeRetrieved(enable); + } else { + onCommunicationError((DaemonTaskFailureResult)result); + } } @Background @@ -321,70 +352,122 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi torrent.mimicResume(); DaemonTaskResult result = ResumeTask.create(currentConnection, torrent).execute(); if (result instanceof DaemonTaskResult) { - onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_resumed, torrent); + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_resumed); } else { onCommunicationError((DaemonTaskFailureResult) result); } } + @Background @Override public void pauseTorrent(Torrent torrent) { - // TODO Auto-generated method stub - + torrent.mimicPause(); + DaemonTaskResult result = PauseTask.create(currentConnection, torrent).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_paused); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } } + @Background @Override - public void startTorrent(Torrent torrent) { - // TODO Auto-generated method stub - + public void startTorrent(Torrent torrent, boolean forced) { + torrent.mimicStart(); + DaemonTaskResult result = StartTask.create(currentConnection, torrent, forced).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_started); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } } + @Background @Override public void stopTorrent(Torrent torrent) { - // TODO Auto-generated method stub - + torrent.mimicStop(); + DaemonTaskResult result = StopTask.create(currentConnection, torrent).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_stopped); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } } + @Background @Override public void removeTorrent(Torrent torrent, boolean withData) { - // TODO Auto-generated method stub - + DaemonTaskResult result = RemoveTask.create(currentConnection, torrent, withData).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, withData ? R.string.result_removed_with_data + : R.string.result_removed); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } } + @Background @Override - public void setLabel(Torrent torrent) { - // TODO Auto-generated method stub - + public void updateLabel(Torrent torrent, String newLabel) { + torrent.mimicNewLabel(newLabel); + DaemonTaskResult result = SetLabelTask.create(currentConnection, torrent, newLabel).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_labelset, newLabel); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } } + @Background @Override - public void updateTrackers(Torrent torrent) { - // TODO Auto-generated method stub - + public void updateTrackers(Torrent torrent, List newTrackers) { + DaemonTaskResult result = SetTrackersTask.create(currentConnection, torrent, newTrackers).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_trackersupdated); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } } + @Background @Override - public void resumeTorrents(List torrents) { - // TODO Auto-generated method stub - + public void updateLocation(Torrent torrent, String newLocation) { + DaemonTaskResult result = SetDownloadLocationTask.create(currentConnection, torrent, newLocation).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, R.string.result_locationset, newLocation); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } } - @Override - public void pauseTorrents(List torrents) { - // TODO Auto-generated method stub - + @UiThread + protected void onTaskSucceeded(DaemonTaskSuccessResult result, int successMessageId, String... messageParams) { + // TODO: Properly report this success + Toast.makeText(this, getString(successMessageId, (Object[]) messageParams), Toast.LENGTH_LONG).show(); } - @Override - public void removeTorrents(List torrents, boolean withData) { - // TODO Auto-generated method stub - + @UiThread + protected void onCommunicationError(DaemonTaskFailureResult result) { + // TODO: Properly report this error + Toast.makeText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), + Toast.LENGTH_LONG).show(); } - @Override - public void setLabels(List torrents) { - // TODO Auto-generated method stub - + @UiThread + protected void onTorrentsRetrieved(List torrents, List labels) { + // Report the newly retrieved list of torrents to the torrents fragment + fragmentTorrents.updateTorrents(new ArrayList(torrents)); + // Update the details fragment if the currently shown torrent is in the newly retrieved list + if (fragmentDetails != null) { + fragmentDetails.perhapsUpdateTorrent(torrents); + } + // TODO: Update local list of labels + } + + @UiThread + protected void onTurtleModeRetrieved(boolean turtleModeEnabled) { + turleModeEnabled = turtleModeEnabled; + supportInvalidateOptionsMenu(); } } diff --git a/lite/src/org/transdroid/core/gui/TorrentsFragment.java b/lite/src/org/transdroid/core/gui/TorrentsFragment.java index afaba547..d0bbeaa1 100644 --- a/lite/src/org/transdroid/core/gui/TorrentsFragment.java +++ b/lite/src/org/transdroid/core/gui/TorrentsFragment.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; import org.androidannotations.annotations.AfterViews; -import org.androidannotations.annotations.Background; import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.InstanceState; import org.androidannotations.annotations.ItemClick; @@ -12,9 +11,12 @@ import org.androidannotations.annotations.ViewById; import org.transdroid.core.R; import org.transdroid.core.gui.lists.TorrentsAdapter; import org.transdroid.core.gui.lists.TorrentsAdapter_; +import org.transdroid.core.gui.navigation.NavigationFilter; +import org.transdroid.core.gui.navigation.StatusType; import org.transdroid.daemon.Torrent; import android.view.View; +import android.widget.ProgressBar; import android.widget.TextView; import com.actionbarsherlock.app.SherlockFragment; @@ -30,6 +32,12 @@ public class TorrentsFragment extends SherlockFragment { // Local data @InstanceState protected ArrayList torrents = null; + @InstanceState + protected NavigationFilter currentFilter = null; + @InstanceState + protected boolean hasAConnection = false; + @InstanceState + protected boolean isLoading = false; // Views @ViewById(R.id.torrent_list) @@ -38,11 +46,12 @@ public class TorrentsFragment extends SherlockFragment { protected TextView emptyText; @ViewById protected TextView nosettingsText; + @ViewById + protected ProgressBar loadingProgress; @AfterViews protected void init() { torrentsList.setAdapter(TorrentsAdapter_.getInstance_(getActivity())); - torrentsList.setEmptyView(emptyText); torrentsList.setMultiChoiceModeListener(onTorrentsSelected); if (torrents != null) updateTorrents(torrents); @@ -54,14 +63,7 @@ public class TorrentsFragment extends SherlockFragment { */ public void updateTorrents(ArrayList newTorrents) { torrents = newTorrents; - if (newTorrents == null) { - // Hide list adapter as well as empty text - torrentsList.setVisibility(View.GONE); - emptyText.setVisibility(View.GONE); - } else { - ((TorrentsAdapter) torrentsList.getAdapter()).update(newTorrents); - // NOTE: This will also make visible again the list or empty view - } + applyFilter(null); // Resets the filter and shown list of torrents } /** @@ -71,6 +73,26 @@ public class TorrentsFragment extends SherlockFragment { updateTorrents(null); } + /** + * Apply a filter on the current list of all torrents, showing the appropriate sublist of torrents only + * @param currentFilter + */ + public void applyFilter(NavigationFilter currentFilter) { + this.currentFilter = currentFilter; + if (this.currentFilter == null) + this.currentFilter = StatusType.getShowAllType(getActivity()); + if (torrents != null) { + // Build a local list of torrents that match the selected navigation filter + ArrayList filteredTorrents = new ArrayList(); + for (Torrent torrent : torrents) { + if (currentFilter.matches(torrent)) + filteredTorrents.add(torrent); + } + ((TorrentsAdapter) torrentsList.getAdapter()).update(filteredTorrents); + } + updateViewVisibility(); + } + private MultiChoiceModeListenerCompat onTorrentsSelected = new MultiChoiceModeListenerCompat() { @Override @@ -91,10 +113,33 @@ public class TorrentsFragment extends SherlockFragment { } // Execute the requested action - // TODO: Add the other actions switch (item.getItemId()) { - case R.id.action_start: - startTorrents(checked); + case R.id.action_resume: + for (Torrent torrent : checked) { + getTasksExecutor().resumeTorrent(torrent); + } + mode.finish(); + return true; + case R.id.action_pause: + for (Torrent torrent : checked) { + getTasksExecutor().pauseTorrent(torrent); + } + mode.finish(); + return true; + case R.id.action_remove_default: + for (Torrent torrent : checked) { + getTasksExecutor().removeTorrent(torrent, false); + } + mode.finish(); + return true; + case R.id.action_remove_withdata: + for (Torrent torrent : checked) { + getTasksExecutor().removeTorrent(torrent, true); + } + mode.finish(); + return true; + case R.id.action_setlabel: + // TODO: Open label selection dialogue mode.finish(); return true; default: @@ -126,31 +171,37 @@ public class TorrentsFragment extends SherlockFragment { /** * Updates the shown screen depending on whether we have a connection (so torrents can be shown) or not (in case we * need to show a message suggesting help) - * @param hasAConnection True if the user has servers configured and therefor has a conenction that can be used + * @param hasAConnection True if the user has servers configured and therefore has a connection that can be used */ public void updateConnectionStatus(boolean hasAConnection) { - if (!hasAConnection) { + this.hasAConnection = hasAConnection; + if (!hasAConnection) clear(); + updateViewVisibility(); + } + + private void updateViewVisibility() { + if (!hasAConnection) { torrentsList.setVisibility(View.GONE); emptyText.setVisibility(View.GONE); + loadingProgress.setVisibility(View.GONE); nosettingsText.setVisibility(View.VISIBLE); - } else { - nosettingsText.setVisibility(View.GONE); - torrentsList.setVisibility(torrentsList.getAdapter().isEmpty()? View.GONE: View.VISIBLE); - emptyText.setVisibility(torrentsList.getAdapter().isEmpty()? View.VISIBLE: View.GONE); + return; } + boolean isEmpty = torrents == null || torrentsList.getAdapter().isEmpty(); + nosettingsText.setVisibility(View.GONE); + torrentsList.setVisibility(!isLoading && !isEmpty? View.GONE: View.VISIBLE); + loadingProgress.setVisibility(isLoading? View.VISIBLE: View.GONE); + emptyText.setVisibility(!isLoading && isEmpty? View.VISIBLE: View.GONE); } - @Background - protected void startTorrents(List torrents) { - // TODO: Implement action + /** + * Returns the object responsible for executing torrent tasks against a connected server + * @return The executor for tasks on some torrent + */ + private TorrentTasksExecutor getTasksExecutor() { + // NOTE: Assumes the activity implements all the required torrent tasks + return (TorrentTasksExecutor) getActivity(); } - public interface TorrentsTasksExecutor { - void resumeTorrents(List torrents); - void pauseTorrents(List torrents); - void removeTorrents(List torrents, boolean withData); - void setLabels(List torrents); - } - } diff --git a/lite/src/org/transdroid/core/gui/lists/DetailsAdapter.java b/lite/src/org/transdroid/core/gui/lists/DetailsAdapter.java index ba60ee43..77460a11 100644 --- a/lite/src/org/transdroid/core/gui/lists/DetailsAdapter.java +++ b/lite/src/org/transdroid/core/gui/lists/DetailsAdapter.java @@ -113,15 +113,6 @@ public class DetailsAdapter extends MergeAdapter { } } - /** - * The details adapter is empty if it is not showing any torrent (regardless of errors, trackers and files) - */ - @Override - public boolean isEmpty() { - //return torrentDetailsView.isBound(); - return false; - } - /** * Clear currently visible torrent, including header and shown lists */ diff --git a/lite/src/org/transdroid/core/gui/lists/SimpleListItem.java b/lite/src/org/transdroid/core/gui/lists/SimpleListItem.java index 605aa3ca..0128a20d 100644 --- a/lite/src/org/transdroid/core/gui/lists/SimpleListItem.java +++ b/lite/src/org/transdroid/core/gui/lists/SimpleListItem.java @@ -1,5 +1,6 @@ package org.transdroid.core.gui.lists; + /** * Represents a filter item as shown in the navigation list or spinner. * diff --git a/lite/src/org/transdroid/core/gui/lists/SimpleListItemAdapter.java b/lite/src/org/transdroid/core/gui/lists/SimpleListItemAdapter.java index 43c92426..f402ab53 100644 --- a/lite/src/org/transdroid/core/gui/lists/SimpleListItemAdapter.java +++ b/lite/src/org/transdroid/core/gui/lists/SimpleListItemAdapter.java @@ -45,7 +45,7 @@ public class SimpleListItemAdapter extends BaseAdapter { @Override public View getView(int position, View convertView, ViewGroup parent) { SimpleListItemView filterItemView; - if (convertView == null) { + if (convertView == null || !(convertView instanceof SimpleListItemView)) { filterItemView = SimpleListItemView_.build(context); } else { filterItemView = (SimpleListItemView) convertView; diff --git a/lite/src/org/transdroid/core/gui/lists/TorrentDetailsView.java b/lite/src/org/transdroid/core/gui/lists/TorrentDetailsView.java index 2245e4b7..db714017 100644 --- a/lite/src/org/transdroid/core/gui/lists/TorrentDetailsView.java +++ b/lite/src/org/transdroid/core/gui/lists/TorrentDetailsView.java @@ -21,8 +21,6 @@ import android.widget.TextView; @EViewGroup(R.layout.fragment_details_header) public class TorrentDetailsView extends RelativeLayout { - private boolean isShowingData = false; - @ViewById protected TextView labelText, dateaddedText, uploadedText, uploadedunitText, ratioText, upspeedText, seedersText, downloadedunitText, downloadedText, totalsizeText, downspeedText, leechersText, statusText; @@ -38,11 +36,9 @@ public class TorrentDetailsView extends RelativeLayout { public void update(Torrent torrent) { if (torrent == null) { - isShowingData = false; return; } - isShowingData = true; LocalTorrent local = LocalTorrent.fromTorrent(torrent); // Set label text @@ -67,7 +63,7 @@ public class TorrentDetailsView extends RelativeLayout { } else { dateaddedText.setVisibility(View.INVISIBLE); } - statusText.setText(local.getProgressStatusEta(getResources())); + statusText.setText(getResources().getString(R.string.status_status, local.getProgressStatusEta(getResources()))); ratioText.setText(getResources().getString(R.string.status_ratio, local.getRatioString())); // TODO: Implement separate numbers of seeders and leechers seedersText.setText(getResources().getString(R.string.status_peers, torrent.getPeersSendingToUs(), @@ -90,8 +86,4 @@ public class TorrentDetailsView extends RelativeLayout { } - public boolean isBound() { - return isShowingData ; - } - } diff --git a/lite/src/org/transdroid/core/gui/navigation/FilterListAdapter.java b/lite/src/org/transdroid/core/gui/navigation/FilterListAdapter.java index d155e801..98a365a9 100644 --- a/lite/src/org/transdroid/core/gui/navigation/FilterListAdapter.java +++ b/lite/src/org/transdroid/core/gui/navigation/FilterListAdapter.java @@ -7,9 +7,12 @@ import org.androidannotations.annotations.RootContext; import org.transdroid.core.R; import org.transdroid.core.gui.lists.SimpleListItem; import org.transdroid.core.gui.lists.SimpleListItemAdapter; -import org.transdroid.core.gui.navigation.FilterSeparatorView_; +import org.transdroid.core.gui.navigation.NavigationSelectionView.NavigationFilterManager; import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SpinnerAdapter; import com.commonsware.cwac.merge.MergeAdapter; @@ -19,14 +22,25 @@ import com.commonsware.cwac.merge.MergeAdapter; * @author Eric Kok */ @EBean -public class FilterListAdapter extends MergeAdapter { +public class FilterListAdapter extends MergeAdapter implements SpinnerAdapter { @RootContext protected Context context; private SimpleListItemAdapter serverItems = null; private SimpleListItemAdapter statusTypeItems = null; private SimpleListItemAdapter labelItems = null; + private NavigationFilterManager navigationFilterManager; + /** + * Stores which screen, or manager, handles navigation selection and display + * @param manager The navigation manager, which knows about the currently selected filter and server + * @return Itself, for method chaining + */ + public FilterListAdapter setNavigationFilterManager(NavigationFilterManager manager) { + this.navigationFilterManager = manager; + return this; + } + /** * Update the list of available servers * @param servers The new list of available servers @@ -75,4 +89,16 @@ public class FilterListAdapter extends MergeAdapter { } } + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + NavigationSelectionView filterItemView; + if (convertView == null || !(convertView instanceof NavigationSelectionView)) { + filterItemView = NavigationSelectionView_.build(context).setNavigationFilterManager(navigationFilterManager); + } else { + filterItemView = (NavigationSelectionView) convertView; + } + filterItemView.bind(); + return filterItemView; + } + } diff --git a/lite/src/org/transdroid/core/gui/navigation/Label.java b/lite/src/org/transdroid/core/gui/navigation/Label.java index 2a27e6b6..748d3a7e 100644 --- a/lite/src/org/transdroid/core/gui/navigation/Label.java +++ b/lite/src/org/transdroid/core/gui/navigation/Label.java @@ -1,12 +1,16 @@ package org.transdroid.core.gui.navigation; import org.transdroid.core.gui.lists.SimpleListItem; +import org.transdroid.daemon.Torrent; + +import android.os.Parcel; +import android.os.Parcelable; /** * Represents some label that is active or available on the server. * @author Eric Kok */ -public class Label implements SimpleListItem { +public class Label implements SimpleListItem, NavigationFilter { private final String name; @@ -19,4 +23,33 @@ public class Label implements SimpleListItem { return this.name; } + @Override + public boolean matches(Torrent torrent) { + return torrent.getLabelName() != null && torrent.getLabelName().equals(name); + } + + private Label(Parcel in) { + this.name = in.readString(); + } + + public static final Parcelable.Creator