From 1f6d4c893d73d2edc17c41fa996edfe16ec21733 Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Wed, 9 Oct 2013 16:34:28 +0200 Subject: [PATCH] Fully developer (but untest) widget configuration, service and remote views factory. --- .../actionbar_donebutton.xml | 0 .../activity_widgetconfig.xml | 0 core/res/layout-v11/list_item_widget_dark.xml | 66 ++++++ .../res/layout-v11/list_item_widget_light.xml | 66 ++++++ .../widget_torrents_dark.xml | 0 .../widget_torrents_light.xml | 2 +- core/res/layout/actionbar_progressitem.xml | 40 ---- .../bools.xml} | 27 +-- core/res/values/bools.xml | 2 + core/res/values/colors.xml | 14 ++ core/res/xml/appwidget_info.xml | 2 +- .../app/settings/ApplicationSettings.java | 32 ++- .../core/gui/FilterEntryDialog.java | 2 + .../transdroid/core/gui/TorrentsActivity.java | 24 ++- .../gui/lists/TorrentFilePriorityLayout.java | 9 +- .../core/gui/lists/TorrentStatusLayout.java | 29 +-- .../core/gui/navigation/StatusType.java | 12 +- .../core/gui/search/BarcodeHelper.java | 2 + .../core/gui/search/FilePickerHelper.java | 2 + .../core/gui/search/UrlEntryDialog.java | 2 + ...{WidgetSettings.java => WidgetConfig.java} | 4 +- .../core/widget/WidgetConfigActivity.java | 19 +- .../core/widget/WidgetProvider.java | 101 ++++++++- .../transdroid/core/widget/WidgetService.java | 198 ++++++++++++++++++ full/AndroidManifest.xml | 19 +- lite/AndroidManifest.xml | 32 +++ 26 files changed, 592 insertions(+), 114 deletions(-) rename core/res/{layout => layout-v11}/actionbar_donebutton.xml (100%) rename core/res/{layout => layout-v11}/activity_widgetconfig.xml (100%) create mode 100644 core/res/layout-v11/list_item_widget_dark.xml create mode 100644 core/res/layout-v11/list_item_widget_light.xml rename core/res/{layout => layout-v11}/widget_torrents_dark.xml (100%) rename core/res/{layout => layout-v11}/widget_torrents_light.xml (97%) delete mode 100644 core/res/layout/actionbar_progressitem.xml rename core/res/{layout-v14/actionbar_progressitem.xml => values-v11/bools.xml} (50%) rename core/src/org/transdroid/core/widget/{WidgetSettings.java => WidgetConfig.java} (92%) create mode 100644 core/src/org/transdroid/core/widget/WidgetService.java diff --git a/core/res/layout/actionbar_donebutton.xml b/core/res/layout-v11/actionbar_donebutton.xml similarity index 100% rename from core/res/layout/actionbar_donebutton.xml rename to core/res/layout-v11/actionbar_donebutton.xml diff --git a/core/res/layout/activity_widgetconfig.xml b/core/res/layout-v11/activity_widgetconfig.xml similarity index 100% rename from core/res/layout/activity_widgetconfig.xml rename to core/res/layout-v11/activity_widgetconfig.xml diff --git a/core/res/layout-v11/list_item_widget_dark.xml b/core/res/layout-v11/list_item_widget_dark.xml new file mode 100644 index 00000000..c4734674 --- /dev/null +++ b/core/res/layout-v11/list_item_widget_dark.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + diff --git a/core/res/layout-v11/list_item_widget_light.xml b/core/res/layout-v11/list_item_widget_light.xml new file mode 100644 index 00000000..860ef687 --- /dev/null +++ b/core/res/layout-v11/list_item_widget_light.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + diff --git a/core/res/layout/widget_torrents_dark.xml b/core/res/layout-v11/widget_torrents_dark.xml similarity index 100% rename from core/res/layout/widget_torrents_dark.xml rename to core/res/layout-v11/widget_torrents_dark.xml diff --git a/core/res/layout/widget_torrents_light.xml b/core/res/layout-v11/widget_torrents_light.xml similarity index 97% rename from core/res/layout/widget_torrents_light.xml rename to core/res/layout-v11/widget_torrents_light.xml index 8b606768..4194cfa9 100644 --- a/core/res/layout/widget_torrents_light.xml +++ b/core/res/layout-v11/widget_torrents_light.xml @@ -45,7 +45,7 @@ android:layout_alignParentBottom="true" android:background="@color/green" /> - - - - - - - - - - - - diff --git a/core/res/layout-v14/actionbar_progressitem.xml b/core/res/values-v11/bools.xml similarity index 50% rename from core/res/layout-v14/actionbar_progressitem.xml rename to core/res/values-v11/bools.xml index 810f2aa9..2ea1c4b9 100644 --- a/core/res/layout-v14/actionbar_progressitem.xml +++ b/core/res/values-v11/bools.xml @@ -15,26 +15,7 @@ You should have received a copy of the GNU General Public License along with Transdroid. If not, see . --> - - - - - - - - - - + + + true + diff --git a/core/res/values/bools.xml b/core/res/values/bools.xml index 07368b72..a79e6d70 100644 --- a/core/res/values/bools.xml +++ b/core/res/values/bools.xml @@ -18,4 +18,6 @@ true + + false diff --git a/core/res/values/colors.xml b/core/res/values/colors.xml index 3b390f16..d9096474 100644 --- a/core/res/values/colors.xml +++ b/core/res/values/colors.xml @@ -16,8 +16,22 @@ along with Transdroid. If not, see . --> + + #8acc12 #7dbb21 #c81113 #aada62 + + + #42a8fa + #a759d4 + #8acc12 + #de3939 + #9e9e9e + #c8e88e + #8acc12 + #4b6617 + #9e9e9e + diff --git a/core/res/xml/appwidget_info.xml b/core/res/xml/appwidget_info.xml index 57d66929..a236ffa0 100644 --- a/core/res/xml/appwidget_info.xml +++ b/core/res/xml/appwidget_info.xml @@ -25,5 +25,5 @@ android:minWidth="180dp" android:previewImage="@drawable/ic_launcher" android:resizeMode="horizontal|vertical" - android:updatePeriodMillis="86400000" + android:updatePeriodMillis="3600000" android:widgetCategory="home_screen|keyguard" /> diff --git a/core/src/org/transdroid/core/app/settings/ApplicationSettings.java b/core/src/org/transdroid/core/app/settings/ApplicationSettings.java index ec13a23e..62a7fc67 100644 --- a/core/src/org/transdroid/core/app/settings/ApplicationSettings.java +++ b/core/src/org/transdroid/core/app/settings/ApplicationSettings.java @@ -16,6 +16,7 @@ */ package org.transdroid.core.app.settings; +import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -31,7 +32,7 @@ import org.transdroid.core.app.search.SearchHelper; import org.transdroid.core.app.search.SearchSite; import org.transdroid.core.gui.navigation.StatusType; import org.transdroid.core.gui.search.SearchSetting; -import org.transdroid.core.widget.WidgetSettings; +import org.transdroid.core.widget.WidgetConfig; import org.transdroid.daemon.Daemon; import org.transdroid.daemon.OS; import org.transdroid.daemon.TorrentsSortBy; @@ -511,11 +512,11 @@ public class ApplicationSettings { * @param appWidgetId The unique ID of the app widget to retrieve settings for, as supplied by the AppWidgetManager * @return A widget configuration object, or null if no settings were stored for the widget ID */ - public WidgetSettings getWidgetConfig(int appWidgetId) { + public WidgetConfig getWidgetConfig(int appWidgetId) { if (!prefs.contains("widget_server_" + appWidgetId)) return null; // @formatter:off - return new WidgetSettings( + return new WidgetConfig( prefs.getInt("widget_server_" + appWidgetId, -1), StatusType.valueOf(prefs.getString("widget_status_" + appWidgetId, StatusType.ShowAll.name())), TorrentsSortBy.valueOf(prefs.getString("widget_sortby_" + appWidgetId, TorrentsSortBy.Alphanumeric.name())), @@ -524,7 +525,16 @@ public class ApplicationSettings { // @formatter:on } - public void setWidgetConfig(int appWidgetId, WidgetSettings settings) { + /** + * Stores the user settings for some specific app widget. Existing settings for the supplied app widget ID will be + * overridden. + * @param appWidgetId The unique ID of the app widget to store settings for, as supplied by the AppWidgetManager + * @param settings A widget configuration object, which may not be null + */ + public void setWidgetConfig(int appWidgetId, WidgetConfig settings) { + if (settings == null) + throw new InvalidParameterException( + "The widget setting may not be null. Use removeWidgetConfig instead to remove existing settings for some app widget."); Editor edit = prefs.edit(); edit.putInt("widget_server_" + appWidgetId, settings.getServerId()); edit.putString("widget_status_" + appWidgetId, settings.getStatusType().name()); @@ -534,4 +544,18 @@ public class ApplicationSettings { edit.commit(); } + /** + * Remove the setting for some specific app widget. + * @param appWidgetId The unique ID of the app widget to store settings for, as supplied by the AppWidgetManager + */ + public void removeWidgetConfig(int appWidgetId) { + Editor edit = prefs.edit(); + edit.remove("widget_server_" + appWidgetId); + edit.remove("widget_status_" + appWidgetId); + edit.remove("widget_sortby_" + appWidgetId); + edit.remove("widget_reverse_" + appWidgetId); + edit.remove("widget_darktheme_" + appWidgetId); + edit.commit(); + } + } diff --git a/core/src/org/transdroid/core/gui/FilterEntryDialog.java b/core/src/org/transdroid/core/gui/FilterEntryDialog.java index 8e9c2425..01c45c1b 100644 --- a/core/src/org/transdroid/core/gui/FilterEntryDialog.java +++ b/core/src/org/transdroid/core/gui/FilterEntryDialog.java @@ -16,6 +16,7 @@ */ package org.transdroid.core.gui; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -32,6 +33,7 @@ public class FilterEntryDialog { * torrents. * @param activity The activity that opens (and owns) this dialog */ + @SuppressLint("ValidFragment") public static void startFilterEntry(final TorrentsActivity activity) { new DialogFragment() { public android.app.Dialog onCreateDialog(android.os.Bundle savedInstanceState) { diff --git a/core/src/org/transdroid/core/gui/TorrentsActivity.java b/core/src/org/transdroid/core/gui/TorrentsActivity.java index 0fc45c09..5f09ef4f 100644 --- a/core/src/org/transdroid/core/gui/TorrentsActivity.java +++ b/core/src/org/transdroid/core/gui/TorrentsActivity.java @@ -56,6 +56,7 @@ import org.transdroid.core.gui.search.UrlEntryDialog; import org.transdroid.core.gui.settings.*; import org.transdroid.core.service.BootReceiver; import org.transdroid.core.service.ConnectivityHelper; +import org.transdroid.core.widget.WidgetProvider; import org.transdroid.daemon.Daemon; import org.transdroid.daemon.IDaemonAdapter; import org.transdroid.daemon.Priority; @@ -212,9 +213,19 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi // No server settings yet; return; } - if (getIntent().getExtras() == null && getIntent().hasExtra("org.transdroid.START_SERVER")) { - lastUsed = applicationSettings.getServerSetting(getIntent().getExtras().getInt( - "org.transdroid.START_SERVER")); + Torrent startTorrent = null; + if (getIntent().getAction() != null && getIntent().getAction().equals(WidgetProvider.INTENT_STARTSERVER) + && getIntent().getExtras() == null && getIntent().hasExtra(WidgetProvider.EXTRA_SERVER)) { + // A server settings order ID was provided in this org.transdroid.START_SERVER action intent + int serverId = getIntent().getExtras().getInt(WidgetProvider.EXTRA_SERVER); + if (serverId < 0 || serverId > applicationSettings.getMaxServer()) { + Log.e(this, "Tried to start with " + WidgetProvider.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); + } } // Set this as selection in the action bar spinner; we can use the server setting key since we have stable ids @@ -222,7 +233,10 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi skipNextOnNavigationItemSelectedCall = true; // Handle any start up intents - if (firstStart && getIntent() != null) { + if (startTorrent != null) { + openDetails(startTorrent); + startTorrent = null; + } else if (firstStart && getIntent() != null) { currentConnection = lastUsed.createServerAdapter(connectivityHelper.getConnectedNetworkName()); handleStartIntent(); } @@ -495,7 +509,7 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi } return true; } - + @OptionsItem(resName = "action_add_fromurl") protected void startUrlEntryDialog() { UrlEntryDialog.startUrlEntry(this); diff --git a/core/src/org/transdroid/core/gui/lists/TorrentFilePriorityLayout.java b/core/src/org/transdroid/core/gui/lists/TorrentFilePriorityLayout.java index 1e88ce48..480dfdaa 100644 --- a/core/src/org/transdroid/core/gui/lists/TorrentFilePriorityLayout.java +++ b/core/src/org/transdroid/core/gui/lists/TorrentFilePriorityLayout.java @@ -16,6 +16,7 @@ */ package org.transdroid.core.gui.lists; +import org.transdroid.core.R; import org.transdroid.daemon.Priority; import android.content.Context; @@ -56,10 +57,10 @@ public class TorrentFilePriorityLayout extends CheckableRelativeLayout { } private void initPaints() { - offPaint.setColor(0xFF9E9E9E); // Grey - lowPaint.setColor(0xFFC8E88E); // Light green - normalPaint.setColor(0xFF8ACC12); // Normal green - highPaint.setColor(0xFF4B6617); // Dark green + offPaint.setColor(getResources().getColor(R.color.file_off)); + lowPaint.setColor(getResources().getColor(R.color.file_low)); + normalPaint.setColor(getResources().getColor(R.color.file_normal)); + highPaint.setColor(getResources().getColor(R.color.file_high)); } public void setPriority(Priority priority) { diff --git a/core/src/org/transdroid/core/gui/lists/TorrentStatusLayout.java b/core/src/org/transdroid/core/gui/lists/TorrentStatusLayout.java index e6cf1767..7b9f6954 100644 --- a/core/src/org/transdroid/core/gui/lists/TorrentStatusLayout.java +++ b/core/src/org/transdroid/core/gui/lists/TorrentStatusLayout.java @@ -16,6 +16,7 @@ */ package org.transdroid.core.gui.lists; +import org.transdroid.core.R; import org.transdroid.daemon.TorrentStatus; import android.content.Context; @@ -37,10 +38,10 @@ public class TorrentStatusLayout extends CheckableRelativeLayout { private final int WIDTH = (int) (6 * scale + 0.5f); private TorrentStatus status = null; - private final Paint inactiveDonePaint = new Paint(); - private final Paint inactivePaint = new Paint(); - private final Paint progressPaint = new Paint(); - private final Paint donePaint = new Paint(); + private final Paint pausedPaint = new Paint(); + private final Paint otherPaint = new Paint(); + private final Paint downloadingPaint = new Paint(); + private final Paint seedingPaint = new Paint(); private final Paint errorPaint = new Paint(); private final RectF fullRect = new RectF(); @@ -57,11 +58,11 @@ public class TorrentStatusLayout extends CheckableRelativeLayout { } private void initPaints() { - inactiveDonePaint.setColor(0xFFA759D4); // Purple - inactivePaint.setColor(0xFF9E9E9E); // Grey - progressPaint.setColor(0xFF42A8FA); // Blue - donePaint.setColor(0xFF8ACC12); // Green - errorPaint.setColor(0xFFDE3939); // Red + pausedPaint.setColor(getResources().getColor(R.color.torrent_paused)); + otherPaint.setColor(getResources().getColor(R.color.torrent_other)); + downloadingPaint.setColor(getResources().getColor(R.color.torrent_downloading)); + seedingPaint.setColor(getResources().getColor(R.color.torrent_seeding)); + errorPaint.setColor(getResources().getColor(R.color.torrent_error)); } /** @@ -85,22 +86,22 @@ public class TorrentStatusLayout extends CheckableRelativeLayout { if (status == null) { return; } - + switch (status) { case Downloading: - canvas.drawRect(fullRect, progressPaint); + canvas.drawRect(fullRect, downloadingPaint); break; case Paused: - canvas.drawRect(fullRect, inactiveDonePaint); + canvas.drawRect(fullRect, pausedPaint); break; case Seeding: - canvas.drawRect(fullRect, donePaint); + canvas.drawRect(fullRect, seedingPaint); break; case Error: canvas.drawRect(fullRect, errorPaint); break; default: // Checking, Waiting, Queued, Unknown - canvas.drawRect(fullRect, inactivePaint); + canvas.drawRect(fullRect, otherPaint); break; } diff --git a/core/src/org/transdroid/core/gui/navigation/StatusType.java b/core/src/org/transdroid/core/gui/navigation/StatusType.java index 08c74e58..58d7c11d 100644 --- a/core/src/org/transdroid/core/gui/navigation/StatusType.java +++ b/core/src/org/transdroid/core/gui/navigation/StatusType.java @@ -35,27 +35,27 @@ import android.os.Parcelable; public enum StatusType { ShowAll { - StatusTypeFilter getFilterItem(Context context) { + public StatusTypeFilter getFilterItem(Context context) { return new StatusTypeFilter(StatusType.ShowAll, context.getString(R.string.navigation_status_showall)); } }, OnlyDownloading { - StatusTypeFilter getFilterItem(Context context) { + public StatusTypeFilter getFilterItem(Context context) { return new StatusTypeFilter(StatusType.OnlyDownloading, context.getString(R.string.navigation_status_onlydown)); } }, OnlyUploading { - StatusTypeFilter getFilterItem(Context context) { + public StatusTypeFilter getFilterItem(Context context) { return new StatusTypeFilter(StatusType.OnlyUploading, context.getString(R.string.navigation_status_onlyup)); } }, OnlyActive { - StatusTypeFilter getFilterItem(Context context) { + public StatusTypeFilter getFilterItem(Context context) { return new StatusTypeFilter(StatusType.OnlyActive, context.getString(R.string.navigation_status_onlyactive)); } }, OnlyInactive { - StatusTypeFilter getFilterItem(Context context) { + public StatusTypeFilter getFilterItem(Context context) { return new StatusTypeFilter(StatusType.OnlyInactive, context.getString(R.string.navigation_status_onlyinactive)); } }; @@ -85,7 +85,7 @@ public enum StatusType { * @param context The Android UI context, to access translations * @return A filter item object to show in the GUI */ - abstract StatusTypeFilter getFilterItem(Context context); + public abstract StatusTypeFilter getFilterItem(Context context); public static class StatusTypeFilter implements SimpleListItem, NavigationFilter { diff --git a/core/src/org/transdroid/core/gui/search/BarcodeHelper.java b/core/src/org/transdroid/core/gui/search/BarcodeHelper.java index c4e89e30..e5397368 100644 --- a/core/src/org/transdroid/core/gui/search/BarcodeHelper.java +++ b/core/src/org/transdroid/core/gui/search/BarcodeHelper.java @@ -19,6 +19,7 @@ package org.transdroid.core.gui.search; import org.transdroid.core.R; import org.transdroid.core.app.search.GoogleWebSearchBarcodeResolver; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; @@ -41,6 +42,7 @@ public class BarcodeHelper { * @param activity The calling activity, to which the result is returned or a dialog is bound that asks to install * the bar code scanner */ + @SuppressLint("ValidFragment") public static void startBarcodeScanner(final SherlockFragmentActivity activity) { try { // Start a bar code scanner that can handle the SCAN intent (specifically ZXing) diff --git a/core/src/org/transdroid/core/gui/search/FilePickerHelper.java b/core/src/org/transdroid/core/gui/search/FilePickerHelper.java index d203d0fa..8d15e397 100644 --- a/core/src/org/transdroid/core/gui/search/FilePickerHelper.java +++ b/core/src/org/transdroid/core/gui/search/FilePickerHelper.java @@ -18,6 +18,7 @@ package org.transdroid.core.gui.search; import org.transdroid.core.R; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; @@ -38,6 +39,7 @@ public class FilePickerHelper { * @param activity The calling activity, to which the result is returned or a dialog is bound that asks to install * the file picker */ + @SuppressLint("ValidFragment") public static void startFilePicker(final SherlockFragmentActivity activity) { try { // Start a file manager that can handle the PICK_FILE intent (specifically IO File Manager) diff --git a/core/src/org/transdroid/core/gui/search/UrlEntryDialog.java b/core/src/org/transdroid/core/gui/search/UrlEntryDialog.java index 77410540..c3c6906b 100644 --- a/core/src/org/transdroid/core/gui/search/UrlEntryDialog.java +++ b/core/src/org/transdroid/core/gui/search/UrlEntryDialog.java @@ -19,6 +19,7 @@ package org.transdroid.core.gui.search; import org.transdroid.core.gui.TorrentsActivity; import org.transdroid.core.gui.navigation.NavigationHelper; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -37,6 +38,7 @@ public class UrlEntryDialog { * activity's {@link TorrentsActivity#addTorrentByUrl(String, String) method}. * @param activity The activity that opens (and owns) this dialog */ + @SuppressLint("ValidFragment") public static void startUrlEntry(final TorrentsActivity activity) { new DialogFragment() { public android.app.Dialog onCreateDialog(android.os.Bundle savedInstanceState) { diff --git a/core/src/org/transdroid/core/widget/WidgetSettings.java b/core/src/org/transdroid/core/widget/WidgetConfig.java similarity index 92% rename from core/src/org/transdroid/core/widget/WidgetSettings.java rename to core/src/org/transdroid/core/widget/WidgetConfig.java index 6d036325..aea02cb8 100644 --- a/core/src/org/transdroid/core/widget/WidgetSettings.java +++ b/core/src/org/transdroid/core/widget/WidgetConfig.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 WidgetSettings { +public class WidgetConfig { private final int serverId; private final StatusType statusType; @@ -31,7 +31,7 @@ public class WidgetSettings { private final boolean reserveSort; private final boolean useDarkTheme; - public WidgetSettings(int serverId, StatusType statusType, TorrentsSortBy sortBy, boolean reverseSort, + public WidgetConfig(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/WidgetConfigActivity.java index abaa2f2e..204250e2 100644 --- a/core/src/org/transdroid/core/widget/WidgetConfigActivity.java +++ b/core/src/org/transdroid/core/widget/WidgetConfigActivity.java @@ -25,7 +25,6 @@ import org.androidannotations.annotations.Background; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.ItemSelect; -import org.androidannotations.annotations.SystemService; import org.androidannotations.annotations.UiThread; import org.androidannotations.annotations.ViewById; import org.transdroid.core.R; @@ -49,10 +48,11 @@ import org.transdroid.daemon.task.DaemonTaskResult; import org.transdroid.daemon.task.RetrieveTask; import org.transdroid.daemon.task.RetrieveTaskSuccessResult; +import android.annotation.TargetApi; import android.appwidget.AppWidgetManager; import android.content.Intent; +import android.os.Build; import android.os.Bundle; -import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.CheckBox; @@ -65,6 +65,7 @@ import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.SherlockActivity; import com.actionbarsherlock.view.SherlockListView; +@TargetApi(Build.VERSION_CODES.HONEYCOMB) @EActivity(resName = "activity_widgetconfig") public class WidgetConfigActivity extends SherlockActivity { @@ -82,10 +83,6 @@ public class WidgetConfigActivity extends SherlockActivity { private List previewTorrents = null; // Settings and helpers - @SystemService - protected AppWidgetManager appWidgetManager; - @SystemService - protected LayoutInflater layoutInflater; @Bean protected ConnectivityHelper connectivityHelper; @Bean @@ -136,7 +133,7 @@ public class WidgetConfigActivity extends SherlockActivity { // Inspired by NoNonsenseNotes's ListWidgetConfig.java (Apache License, Version 2.0) getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE); - View doneButtonFrame = layoutInflater.inflate(R.layout.actionbar_donebutton, null); + View doneButtonFrame = getLayoutInflater().inflate(R.layout.actionbar_donebutton, null); doneButtonFrame.findViewById(R.id.actionbar_done).setOnClickListener(doneClicked); getSupportActionBar().setCustomView(doneButtonFrame); @@ -252,11 +249,13 @@ public class WidgetConfigActivity extends SherlockActivity { TorrentsSortBy sortBy = ((SortByListItem) sortSpinner.getSelectedItem()).getSortBy(); boolean reverseSort = reverseorderCheckBox.isChecked(); boolean useDarkTheme = darkthemeCheckBox.isChecked(); - applicationSettings.setWidgetConfig(appWidgetId, new WidgetSettings(server, statusType, sortBy, - reverseSort, useDarkTheme)); + WidgetConfig config = new WidgetConfig(server, statusType, sortBy, reverseSort, useDarkTheme); + applicationSettings.setWidgetConfig(appWidgetId, config); // Return the widget configuration result - appWidgetManager.updateAppWidget(appWidgetId, null); + AppWidgetManager manager = AppWidgetManager.getInstance(WidgetConfigActivity.this); + manager.updateAppWidget(appWidgetId, WidgetProvider.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/WidgetProvider.java b/core/src/org/transdroid/core/widget/WidgetProvider.java index 0f7b996d..e599d7ab 100644 --- a/core/src/org/transdroid/core/widget/WidgetProvider.java +++ b/core/src/org/transdroid/core/widget/WidgetProvider.java @@ -16,19 +16,116 @@ */ package org.transdroid.core.widget; +import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EReceiver; +import org.transdroid.core.R; +import org.transdroid.core.app.settings.*; +import org.transdroid.core.app.settings.ServerSetting; +import org.transdroid.core.gui.log.Log; +import android.annotation.TargetApi; +import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.widget.RemoteViews; +@TargetApi(Build.VERSION_CODES.HONEYCOMB) @EReceiver public class WidgetProvider extends AppWidgetProvider { + public static final String INTENT_STARTSERVER = "org.transdroid.START_SERVER"; + public static final String EXTRA_TORRENT = "extra_torrent"; + public static final String EXTRA_SERVER = "extra_server"; + + @Bean + protected ApplicationSettings applicationSettings; + @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + for (int appWidgetId : appWidgetIds) { + appWidgetManager.updateAppWidget(appWidgetId, + buildRemoteViews(context, appWidgetId, applicationSettings.getWidgetConfig(appWidgetId))); + } + } + + @Override + public void onDeleted(Context context, int[] appWidgetIds) { + super.onDeleted(context, appWidgetIds); + for (int appWidgetId : appWidgetIds) { + applicationSettings.removeWidgetConfig(appWidgetId); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + + if (intent == null || intent.getAction() == null || intent.getExtras() == null + || !intent.hasExtra(EXTRA_SERVER)) + return; + + // Launch an Intent to start Transdroid on some specific server; and possibly a specific torrent too + Intent start = new Intent(INTENT_STARTSERVER); + start.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + start.putExtra(EXTRA_SERVER, intent.getIntExtra(EXTRA_SERVER, -1)); + if (intent.getAction().equals(EXTRA_TORRENT)) { + start.putExtra(EXTRA_TORRENT, intent.getParcelableExtra(EXTRA_TORRENT)); + } + context.startActivity(start); + + super.onReceive(context, intent); + + } + + /** + * 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}. + * @param context The app widget context, with access to resources + * @param appWidgetId The specific ID of the app widget to load + * @param config The user widget configuration, with filter and theme preferences + * @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) { + + // Does the server to show and its widget settings actually still exist? + if (context == null || config == null) + return null; + ApplicationSettings appSettings = ApplicationSettings_.getInstance_(context); + if (config.getServerId() < 0 || config.getServerId() > appSettings.getMaxServer()) { + Log.e(context, "Tried to set up widget " + appWidgetId + " but the bound server ID " + config.getServerId() + + " no longer exists."); + return null; + } + + // Load the dark or light widget layout xml + RemoteViews rv = new RemoteViews(context.getPackageName(), + config.shouldUseDarkTheme() ? R.layout.list_item_widget_dark : R.layout.list_item_widget_light); + + // Set up the widget's list view loading service which refers to the WidgetViewsFactory + // Use a unique data URI next to the extra to make sure the intents are unique form each widget + Intent intent = new Intent(context, WidgetService.class); + intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME) + "//widget/" + appWidgetId + "/server/" + + config.getServerId())); + intent.putExtra(EXTRA_SERVER, config.getServerId()); + rv.setRemoteAdapter(appWidgetId, R.id.torrents_list, intent); + rv.setPendingIntentTemplate(R.id.torrents_list, + PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); + rv.setEmptyView(R.id.torrents_list, R.id.error_text); + rv.setTextViewText(R.id.error_text, context.getString(R.string.navigation_emptytorrents)); + + // Show the server and status type filter from the widget configuration + 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()); + rv.setOnClickPendingIntent(R.id.icon_image, + PendingIntent.getActivity(context, 0, intent.cloneFilter(), PendingIntent.FLAG_UPDATE_CURRENT)); + + return rv; - // TODO - } + } diff --git a/core/src/org/transdroid/core/widget/WidgetService.java b/core/src/org/transdroid/core/widget/WidgetService.java new file mode 100644 index 00000000..915bebd9 --- /dev/null +++ b/core/src/org/transdroid/core/widget/WidgetService.java @@ -0,0 +1,198 @@ +/* + * Copyright 2010-2013 Eric Kok et al. + * + * Transdroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Transdroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Transdroid. If not, see . + */ +package org.transdroid.core.widget; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.androidannotations.annotations.EService; +import org.transdroid.core.R; +import org.transdroid.core.app.settings.*; +import org.transdroid.core.app.settings.ServerSetting; +import org.transdroid.core.gui.lists.LocalTorrent; +import org.transdroid.core.gui.log.Log; +import org.transdroid.core.service.*; +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.IDaemonAdapter; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentsComparator; +import org.transdroid.daemon.task.DaemonTaskResult; +import org.transdroid.daemon.task.RetrieveTask; +import org.transdroid.daemon.task.RetrieveTaskSuccessResult; + +import android.annotation.TargetApi; +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Build; +import android.widget.RemoteViews; +import android.widget.RemoteViewsService; + +@TargetApi(Build.VERSION_CODES.HONEYCOMB) +@EService +public class WidgetService extends RemoteViewsService { + + @Override + public RemoteViewsFactory onGetViewFactory(Intent intent) { + return new WidgetViewsFactory(this.getApplicationContext(), intent); + } + +} + +@TargetApi(Build.VERSION_CODES.HONEYCOMB) +class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { + + private final Context context; + private final int appWidgetId; + private List torrents = null; + private WidgetConfig config = null; + + public WidgetViewsFactory(Context applicationContext, Intent intent) { + this.context = applicationContext; + this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID); + } + + @Override + public void onCreate() { + // Nothing to do here (wait for onDataSetChanged) + } + + @Override + public void onDataSetChanged() { + + // Load the widget settings + 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"); + 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) + torrents.clear(); + return; + } + + // Load the torrents; synchronously + IDaemonAdapter connection = server.createServerAdapter(ConnectivityHelper_.getInstance_(context) + .getConnectedNetworkName()); + DaemonTaskResult result = RetrieveTask.create(connection).execute(); + 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) + torrents.clear(); + return; + } + + // We have data; filter, sort and store it to use later when getViewAt gets called + ArrayList filteredTorrents = new ArrayList(); + for (Torrent torrent : ((RetrieveTaskSuccessResult) result).getTorrents()) { + if (config.getStatusType().getFilterItem(context).matches(torrent)) + filteredTorrents.add(torrent); + } + if (filteredTorrents.size() > 0) { + // Only sort when there are actually torrents left after filtering + Daemon serverType = filteredTorrents.get(0).getDaemon(); + Collections.sort(filteredTorrents, + new TorrentsComparator(serverType, config.getSortBy(), config.shouldReserveSort())); + } + torrents = filteredTorrents; + + } + + @Override + public RemoteViews getViewAt(int position) { + + // Load the dark or light widget list item layout xml + RemoteViews rv = new RemoteViews(context.getPackageName(), + config.shouldUseDarkTheme() ? R.layout.list_item_widget_dark : R.layout.list_item_widget_light); + + // Bind the torrent details texts and status colour + Torrent torrent = torrents.get(position); + LocalTorrent local = LocalTorrent.fromTorrent(torrent); + Resources r = context.getResources(); + rv.setTextViewText(R.id.name_text, torrent.getName()); + rv.setTextViewText(R.id.progress_text, local.getProgressSizeText(r, false)); + rv.setTextViewText(R.id.ratio_text, local.getProgressEtaRatioText(r)); + int statusColour; + switch (torrent.getStatusCode()) { + case Downloading: + statusColour = r.getColor(r.getColor(R.color.torrent_downloading)); + break; + case Paused: + statusColour = r.getColor(r.getColor(R.color.torrent_paused)); + break; + case Seeding: + statusColour = r.getColor(r.getColor(R.color.torrent_seeding)); + break; + case Error: + statusColour = r.getColor(r.getColor(R.color.torrent_error)); + break; + default: // Checking, Waiting, Queued, Unknown + statusColour = r.getColor(r.getColor(R.color.torrent_other)); + break; + } + rv.setInt(R.id.status_view, "setBackgroundColor", r.getColor(statusColour)); + Intent startIntent = new Intent(); + startIntent.putExtra(WidgetProvider.EXTRA_SERVER, config.getServerId()); + startIntent.putExtra(WidgetProvider.EXTRA_TORRENT, torrent); + rv.setOnClickFillInIntent(R.id.widget_line_layout, startIntent); + + return rv; + + } + + @Override + public RemoteViews getLoadingView() { + return null; + } + + @Override + public void onDestroy() { + torrents.clear(); + } + + @Override + public int getViewTypeCount() { + return 1; + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public int getCount() { + if (torrents == null) + return 0; + return torrents.size(); + } + + @Override + public long getItemId(int position) { + return position; + } + +} diff --git a/full/AndroidManifest.xml b/full/AndroidManifest.xml index e9c276e4..2a400a74 100644 --- a/full/AndroidManifest.xml +++ b/full/AndroidManifest.xml @@ -69,6 +69,11 @@ + + + + + @@ -243,13 +248,23 @@ - + - + + + diff --git a/lite/AndroidManifest.xml b/lite/AndroidManifest.xml index 7b1a47dc..d86c892b 100644 --- a/lite/AndroidManifest.xml +++ b/lite/AndroidManifest.xml @@ -69,6 +69,11 @@ + + + + + @@ -194,6 +199,33 @@ android:value="android.intent.action.BOOT_COMPLETED" /> + + + + + + + + + + + + + + + + + \ No newline at end of file