diff --git a/core/res/layout-v11/widget_torrents_dark.xml b/core/res/layout-v11/widget_torrents_dark.xml index 31b51077..19db23ec 100644 --- a/core/res/layout-v11/widget_torrents_dark.xml +++ b/core/res/layout-v11/widget_torrents_dark.xml @@ -42,11 +42,33 @@ android:scaleType="fitXY" android:src="@drawable/ic_action_refresh_dark" /> + + + + + + + + Start Normal start Force start + Start all Stop + Stop all Resume + Resume all Pause + Pause all Remove Remove torrent Remove and delete data diff --git a/core/res/xml/appwidget_info.xml b/core/res/xml/listwidget_info.xml similarity index 94% rename from core/res/xml/appwidget_info.xml rename to core/res/xml/listwidget_info.xml index 7846f411..685df506 100644 --- a/core/res/xml/appwidget_info.xml +++ b/core/res/xml/listwidget_info.xml @@ -16,7 +16,7 @@ along with Transdroid. If not, see . --> applicationSettings.getMaxServer()) { - Log.e(this, "Tried to start with " + WidgetProvider.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 { lastUsed = applicationSettings.getServerSetting(serverId); - if (getIntent().hasExtra(WidgetProvider.EXTRA_TORRENT)) - startTorrent = getIntent().getParcelableExtra(WidgetProvider.EXTRA_TORRENT); + if (getIntent().hasExtra(ListWidgetProvider.EXTRA_TORRENT)) + startTorrent = getIntent().getParcelableExtra(ListWidgetProvider.EXTRA_TORRENT); } } diff --git a/core/src/org/transdroid/core/service/ControlService.java b/core/src/org/transdroid/core/service/ControlService.java new file mode 100644 index 00000000..57c5376e --- /dev/null +++ b/core/src/org/transdroid/core/service/ControlService.java @@ -0,0 +1,155 @@ +package org.transdroid.core.service; + +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.daemon.IDaemonAdapter; +import org.transdroid.daemon.task.DaemonTask; +import org.transdroid.daemon.task.DaemonTaskResult; +import org.transdroid.daemon.task.DaemonTaskSuccessResult; +import org.transdroid.daemon.task.PauseAllTask; +import org.transdroid.daemon.task.ResumeAllTask; +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 { + + // NOTE: These are the same strings as Transdroid 1, for backwards compatibility + public static final String INTENT_SETTRANSFERRATES = "org.transdroid.control.SET_TRANSFER_RATES"; + public static final String INTENT_PAUSEALL = "org.transdroid.control.PAUSE_ALL"; + public static final String INTENT_RESUMEALL = "org.transdroid.control.RESUME_ALL"; + public static final String INTENT_STARTALL = "org.transdroid.control.START_ALL"; + public static final String INTENT_STOPALL = "org.transdroid.control.STOP_ALL"; + public static final String EXTRA_DAEMON = "DAEMON"; + public static final String EXTRA_UPLOAD_RATE = "UPLOAD_RATE"; + public static final String EXTRA_DOWNLOAD_RATE = "DOWNLOAD_RATE"; + + @Bean + protected ConnectivityHelper connectivityHelper; + @Bean + protected ApplicationSettings applicationSettings; + + public ControlService() { + super("ControlService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + + if (intent == null) + return; + + // We should have been supplied either am EXTRA_DAEMON or an AppWidgetManager.EXTRA_APPWIDGET_ID + ServerSetting server = null; + int appWidgetId = -1; + if (intent.hasExtra(EXTRA_DAEMON)) { + + // See if the supplied server id is pointing to a valid server + int serverId = intent.getIntExtra(EXTRA_DAEMON, -1); + if (serverId < 0 || serverId > applicationSettings.getMaxServer()) { + // 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."); + return; + } + server = applicationSettings.getServerSetting(serverId); + + } else if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) { + + // This was called directly form a home screen widget + 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."); + return; + } + int serverId = config.getServerId(); + if (serverId < 0 || serverId > applicationSettings.getMaxServer()) { + Log.e(this, "The home screen widget points to a server that no longer exists."); + return; + } + server = applicationSettings.getServerSetting(serverId); + + } else { + + // Simply use the last-used server + server = applicationSettings.getLastUsedServer(); + + } + + // 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."); + return; + } + + // See which action should be performed on the server + IDaemonAdapter adapter = server.createServerAdapter(connectivityHelper.getConnectedNetworkName(), this); + DaemonTask task = null; + if (intent.getAction().equals(INTENT_RESUMEALL)) { + task = ResumeAllTask.create(adapter); + } else if (intent.getAction().equals(INTENT_PAUSEALL)) { + task = PauseAllTask.create(adapter); + } else if (intent.getAction().equals(INTENT_STARTALL)) { + task = StartAllTask.create(adapter, false); + } else if (intent.getAction().equals(INTENT_STOPALL)) { + task = StopAllTask.create(adapter); + } else if (intent.getAction().equals(INTENT_SETTRANSFERRATES)) { + // 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); + } + + // 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"); + return; + } + DaemonTaskResult result = task.execute(); + if (result instanceof DaemonTaskSuccessResult) { + 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)"); + // 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) { + } + + // 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/core/src/org/transdroid/core/widget/WidgetConfig.java b/core/src/org/transdroid/core/widget/ListWidgetConfig.java similarity index 92% rename from core/src/org/transdroid/core/widget/WidgetConfig.java rename to core/src/org/transdroid/core/widget/ListWidgetConfig.java index aea02cb8..bbe76b25 100644 --- a/core/src/org/transdroid/core/widget/WidgetConfig.java +++ b/core/src/org/transdroid/core/widget/ListWidgetConfig.java @@ -23,7 +23,7 @@ import org.transdroid.daemon.TorrentsSortBy; * Represents a set of settings that define how the user configured a specific app widget. * @author Eric Kok */ -public class WidgetConfig { +public class ListWidgetConfig { private final int serverId; private final StatusType statusType; @@ -31,7 +31,7 @@ public class WidgetConfig { private final boolean reserveSort; private final boolean useDarkTheme; - public WidgetConfig(int serverId, StatusType statusType, TorrentsSortBy sortBy, boolean reverseSort, + public ListWidgetConfig(int serverId, StatusType statusType, TorrentsSortBy sortBy, boolean reverseSort, boolean useDarkTheme) { this.serverId = serverId; this.statusType = statusType; diff --git a/core/src/org/transdroid/core/widget/WidgetConfigActivity.java b/core/src/org/transdroid/core/widget/ListWidgetConfigActivity.java similarity index 95% rename from core/src/org/transdroid/core/widget/WidgetConfigActivity.java rename to core/src/org/transdroid/core/widget/ListWidgetConfigActivity.java index d9a377a9..8f435156 100644 --- a/core/src/org/transdroid/core/widget/WidgetConfigActivity.java +++ b/core/src/org/transdroid/core/widget/ListWidgetConfigActivity.java @@ -64,7 +64,7 @@ import com.actionbarsherlock.app.SherlockActivity; @TargetApi(Build.VERSION_CODES.HONEYCOMB) @EActivity(resName = "activity_widgetconfig") -public class WidgetConfigActivity extends SherlockActivity { +public class ListWidgetConfigActivity extends SherlockActivity { // Views and adapters @ViewById @@ -211,7 +211,7 @@ public class WidgetConfigActivity extends SherlockActivity { .sort(filteredTorrents, new TorrentsComparator(serverType, sortBy, reverseorderCheckBox.isChecked())); // Finally update the widget preview with the live, filtered and sorted torrents list - torrentsList.setAdapter(new WidgetPreviewAdapter(this, 0, filteredTorrents)); + torrentsList.setAdapter(new ListWidgetPreviewAdapter(this, 0, filteredTorrents)); torrentsList.setVisibility(View.VISIBLE); errorText.setVisibility(View.GONE); } @@ -241,13 +241,13 @@ public class WidgetConfigActivity extends SherlockActivity { TorrentsSortBy sortBy = ((SortByListItem) sortSpinner.getSelectedItem()).getSortBy(); boolean reverseSort = reverseorderCheckBox.isChecked(); boolean useDarkTheme = darkthemeCheckBox.isChecked(); - WidgetConfig config = new WidgetConfig(server, statusType, sortBy, reverseSort, useDarkTheme); + ListWidgetConfig config = new ListWidgetConfig(server, statusType, sortBy, reverseSort, useDarkTheme); applicationSettings.setWidgetConfig(appWidgetId, config); // Return the widget configuration result - AppWidgetManager manager = AppWidgetManager.getInstance(WidgetConfigActivity.this); + AppWidgetManager manager = AppWidgetManager.getInstance(ListWidgetConfigActivity.this); manager.updateAppWidget(appWidgetId, - WidgetProvider.buildRemoteViews(getApplicationContext(), appWidgetId, config)); + 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/core/src/org/transdroid/core/widget/WidgetPreviewAdapter.java b/core/src/org/transdroid/core/widget/ListWidgetPreviewAdapter.java similarity index 95% rename from core/src/org/transdroid/core/widget/WidgetPreviewAdapter.java rename to core/src/org/transdroid/core/widget/ListWidgetPreviewAdapter.java index efd9c4d7..b42d694d 100644 --- a/core/src/org/transdroid/core/widget/WidgetPreviewAdapter.java +++ b/core/src/org/transdroid/core/widget/ListWidgetPreviewAdapter.java @@ -33,7 +33,7 @@ import android.widget.TextView; * A list list item adapter that shows torrents as simplified, widget-style list items; the light theme is always used. * @author Eric Kok */ -public class WidgetPreviewAdapter extends ArrayAdapter { +public class ListWidgetPreviewAdapter extends ArrayAdapter { /** * Constructs the custom array adapter that shows torrents in a widget list style for preview. @@ -41,7 +41,7 @@ public class WidgetPreviewAdapter extends ArrayAdapter { * @param foo Ignored parameter; the light theme widget appearance is always used * @param torrents The already-retrieved, non-null list of torrents to show */ - public WidgetPreviewAdapter(Context context, int foo, List torrents) { + public ListWidgetPreviewAdapter(Context context, int foo, List torrents) { super(context, R.layout.list_item_widget_light, torrents); } diff --git a/core/src/org/transdroid/core/widget/WidgetProvider.java b/core/src/org/transdroid/core/widget/ListWidgetProvider.java similarity index 75% rename from core/src/org/transdroid/core/widget/WidgetProvider.java rename to core/src/org/transdroid/core/widget/ListWidgetProvider.java index 2c073080..7f7e271e 100644 --- a/core/src/org/transdroid/core/widget/WidgetProvider.java +++ b/core/src/org/transdroid/core/widget/ListWidgetProvider.java @@ -22,6 +22,7 @@ import org.transdroid.core.R; import org.transdroid.core.app.settings.*; 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; @@ -33,9 +34,14 @@ import android.net.Uri; import android.os.Build; import android.widget.RemoteViews; +/** + * 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}. + * @author Eric Kok + */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) @EReceiver -public class WidgetProvider extends AppWidgetProvider { +public class ListWidgetProvider extends AppWidgetProvider { public static final String INTENT_STARTSERVER = "org.transdroid.START_SERVER"; public static final String EXTRA_TORRENT = "extra_torrent"; @@ -47,15 +53,27 @@ public class WidgetProvider extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { - if (intent != null && intent.hasExtra(EXTRA_REFRESH)) { + 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 - int appWidgetId = intent.getIntExtra(EXTRA_REFRESH, -1); AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, buildRemoteViews(context, appWidgetId, applicationSettings.getWidgetConfig(appWidgetId))); AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(appWidgetId, R.id.torrents_list); return; } - super.onReceive(context, intent); + + // 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 @@ -83,7 +101,7 @@ public class WidgetProvider extends AppWidgetProvider { * @return A fully initialised set of remote views to update the widget with the AppWidgetManager */ @SuppressWarnings("deprecation") - public static RemoteViews buildRemoteViews(Context context, int appWidgetId, WidgetConfig config) { + 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) @@ -100,7 +118,7 @@ public class WidgetProvider extends AppWidgetProvider { config.shouldUseDarkTheme() ? R.layout.widget_torrents_dark : R.layout.widget_torrents_light); // Set up the widget's list view loading service which refers to the WidgetViewsFactory - Intent data = new Intent(context, WidgetService_.class); + Intent data = new Intent(context, ListWidgetViewsService_.class); data.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); data.setData(Uri.parse(data.toUri(Intent.URI_INTENT_SCHEME))); rv.setRemoteAdapter(appWidgetId, R.id.torrents_list, data); @@ -129,13 +147,27 @@ public class WidgetProvider extends AppWidgetProvider { // Set up the widgets refresh button pending intent (calling this WidgetProvider itself) // Make sure that the intent is unique using a custom data path (rather than just the extras) - Intent refresh = new Intent(context, WidgetProvider_.class); + Intent refresh = new Intent(context, ListWidgetProvider_.class); refresh.setData(Uri.parse("intent://widget/" + appWidgetId + "/refresh")); refresh.putExtra(EXTRA_REFRESH, appWidgetId); refresh.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); rv.setOnClickPendingIntent(R.id.refresh_button, PendingIntent.getBroadcast(context, appWidgetId, refresh, PendingIntent.FLAG_UPDATE_CURRENT)); + // Set up the control (pause and resume) buttons (calling the WidgetProvider itself) + Intent pauseall = new Intent(context, ListWidgetProvider_.class); + pauseall.setData(Uri.parse("intent://widget/" + appWidgetId + "/pauseall")); + pauseall.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + pauseall.setAction(ControlService.INTENT_PAUSEALL); + rv.setOnClickPendingIntent(R.id.pauseall_button, + PendingIntent.getBroadcast(context, appWidgetId, pauseall, PendingIntent.FLAG_UPDATE_CURRENT)); + Intent resumeall = new Intent(context, ListWidgetProvider_.class); + resumeall.setData(Uri.parse("intent://widget/" + appWidgetId + "/resumeall")); + resumeall.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + resumeall.setAction(ControlService.INTENT_RESUMEALL); + rv.setOnClickPendingIntent(R.id.resumeall_button, + PendingIntent.getBroadcast(context, appWidgetId, resumeall, PendingIntent.FLAG_UPDATE_CURRENT)); + return rv; } diff --git a/core/src/org/transdroid/core/widget/WidgetService.java b/core/src/org/transdroid/core/widget/ListWidgetViewsService.java similarity index 92% rename from core/src/org/transdroid/core/widget/WidgetService.java rename to core/src/org/transdroid/core/widget/ListWidgetViewsService.java index 1bacebfc..d7682bbb 100644 --- a/core/src/org/transdroid/core/widget/WidgetService.java +++ b/core/src/org/transdroid/core/widget/ListWidgetViewsService.java @@ -43,9 +43,14 @@ import android.os.Build; import android.widget.RemoteViews; import android.widget.RemoteViewsService; +/** + * A service for the list widget to update the remote views that a list widget shows, by getting the torrents from the + * server (synchronously) and building {@link RemoteViews} objects for each torrent. + * @author Eric Kok + */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) @EService -public class WidgetService extends RemoteViewsService { +public class ListWidgetViewsService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { @@ -60,7 +65,7 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { private final Context context; private final int appWidgetId; private List torrents = null; - private WidgetConfig config = null; + private ListWidgetConfig config = null; public WidgetViewsFactory(Context applicationContext, Intent intent) { this.context = applicationContext; @@ -153,8 +158,8 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { } rv.setInt(R.id.status_view, "setBackgroundColor", context.getResources().getColor(statusColour)); Intent startIntent = new Intent(); - startIntent.putExtra(WidgetProvider.EXTRA_SERVER, config.getServerId()); - startIntent.putExtra(WidgetProvider.EXTRA_TORRENT, torrent); + startIntent.putExtra(ListWidgetProvider.EXTRA_SERVER, config.getServerId()); + startIntent.putExtra(ListWidgetProvider.EXTRA_TORRENT, torrent); rv.setOnClickFillInIntent(R.id.widget_line_layout, startIntent); return rv; diff --git a/full/AndroidManifest.xml b/full/AndroidManifest.xml index a1adc295..3242a73b 100644 --- a/full/AndroidManifest.xml +++ b/full/AndroidManifest.xml @@ -16,6 +16,7 @@ along with Transdroid. If not, see . --> @@ -53,8 +54,8 @@ @@ -235,7 +236,7 @@ android:label="@string/rss_feeds" android:theme="@style/TransdroidTheme" /> - + @@ -249,24 +250,37 @@ + + + + + + + + + + + android:name="org.transdroid.core.widget.ListWidgetConfigActivity_" + android:enabled="@bool/widget_available" + android:theme="@style/TransdroidTheme.WidgetConfig" > @@ -274,8 +288,8 @@ + android:resource="@xml/listwidget_info" /> - + \ No newline at end of file diff --git a/lib/src/org/transdroid/daemon/TorrentsComparator.java b/lib/src/org/transdroid/daemon/TorrentsComparator.java index b2ccc812..34772030 100644 --- a/lib/src/org/transdroid/daemon/TorrentsComparator.java +++ b/lib/src/org/transdroid/daemon/TorrentsComparator.java @@ -59,6 +59,10 @@ public class TorrentsComparator implements Comparator { case Status: return tor1.getStatusCode().compareStatusCodeTo(tor2.getStatusCode()); case DateAdded: + if (tor1.getDateAdded() == null) + return -1; + if (tor2.getDateAdded() == null) + return 1; return tor1.getDateAdded().compareTo(tor2.getDateAdded()); case DateDone: return tor1.getDateDone().compareTo(tor2.getDateDone()); @@ -74,6 +78,10 @@ public class TorrentsComparator implements Comparator { case Status: return 0 - tor1.getStatusCode().compareStatusCodeTo(tor2.getStatusCode()); case DateAdded: + if (tor1.getDateAdded() == null) + return 1; + if (tor2.getDateAdded() == null) + return -1; return 0 - tor1.getDateAdded().compareTo(tor2.getDateAdded()); case DateDone: return 0 - tor1.getDateDone().compareTo(tor2.getDateDone()); diff --git a/lite/AndroidManifest.xml b/lite/AndroidManifest.xml index bc0443ea..f9c940f1 100644 --- a/lite/AndroidManifest.xml +++ b/lite/AndroidManifest.xml @@ -16,6 +16,7 @@ along with Transdroid. If not, see . --> @@ -53,8 +54,8 @@ @@ -195,25 +196,37 @@ + + + + + + + + + + + android:name="org.transdroid.core.widget.ListWidgetConfigActivity_" + android:enabled="@bool/widget_available" + android:theme="@style/TransdroidTheme.WidgetConfig" > @@ -221,8 +234,8 @@ + android:resource="@xml/listwidget_info" /> - + \ No newline at end of file