diff --git a/core/res/drawable-hdpi/widget_preview.png b/core/res/drawable-hdpi/widget_preview.png new file mode 100644 index 00000000..e6fcab62 Binary files /dev/null and b/core/res/drawable-hdpi/widget_preview.png differ diff --git a/core/res/layout-v11/activity_widgetconfig.xml b/core/res/layout-v11/activity_widgetconfig.xml index 7f1169aa..9cf2564f 100644 --- a/core/res/layout-v11/activity_widgetconfig.xml +++ b/core/res/layout-v11/activity_widgetconfig.xml @@ -62,13 +62,21 @@ android:id="@+id/filter_spinner" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/margin_default" /> + android:layout_marginTop="@dimen/margin_half" /> + + + android:layout_marginTop="@dimen/margin_half" /> + android:paddingRight="@dimen/widget_list_item_padding" > - - + android:layout_width="6dp" + android:layout_height="@dimen/widget_list_item_height" /> + @@ -58,9 +59,10 @@ android:layout_height="wrap_content" android:layout_alignBaseline="@id/ratio_text" android:layout_alignParentLeft="true" - android:layout_marginLeft="@dimen/margin_torrentlistleft" - android:layout_toLeftOf="@id/priority_image" + android:layout_marginLeft="@dimen/widget_list_item_padding_left" + android:layout_toLeftOf="@id/ratio_text" + android:maxLines="1" android:textIsSelectable="false" android:textSize="@dimen/text_small" /> - + \ No newline at end of file diff --git a/core/res/layout-v11/list_item_widget_light.xml b/core/res/layout-v11/list_item_widget_light.xml index 860ef687..3e7fdd21 100644 --- a/core/res/layout-v11/list_item_widget_light.xml +++ b/core/res/layout-v11/list_item_widget_light.xml @@ -18,26 +18,26 @@ + android:paddingRight="@dimen/widget_list_item_padding" > - - + android:layout_width="6dp" + android:layout_height="@dimen/widget_list_item_height" /> + @@ -58,9 +59,10 @@ android:layout_height="wrap_content" android:layout_alignBaseline="@id/ratio_text" android:layout_alignParentLeft="true" - android:layout_marginLeft="@dimen/margin_torrentlistleft" - android:layout_toLeftOf="@id/priority_image" + android:layout_marginLeft="@dimen/widget_list_item_padding_left" + android:layout_toLeftOf="@id/ratio_text" + android:maxLines="1" android:textIsSelectable="false" android:textSize="@dimen/text_small" /> - + \ No newline at end of file diff --git a/core/res/layout-v11/widget_torrents_dark.xml b/core/res/layout-v11/widget_torrents_dark.xml index 3959a922..31b51077 100644 --- a/core/res/layout-v11/widget_torrents_dark.xml +++ b/core/res/layout-v11/widget_torrents_dark.xml @@ -1,4 +1,20 @@ + + + + android:paddingRight="@dimen/margin_half" + android:paddingTop="3dp" > @@ -40,20 +70,22 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="-4dip" + android:ellipsize="marquee" android:fontFamily="sans-serif-light" + android:maxLines="1" android:textColor="@color/text_actionbar_dark" android:textIsSelectable="false" android:textSize="@dimen/ui_navigation_server" /> - - + + + + android:paddingRight="@dimen/margin_half" + android:paddingTop="3dp" > @@ -40,20 +70,22 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="-4dip" + android:ellipsize="marquee" android:fontFamily="sans-serif-light" + android:maxLines="1" android:textColor="@color/text_actionbar_light" android:textIsSelectable="false" android:textSize="@dimen/ui_navigation_server" /> - - diff --git a/core/res/values/dimens.xml b/core/res/values/dimens.xml index 52c36924..a7e05008 100644 --- a/core/res/values/dimens.xml +++ b/core/res/values/dimens.xml @@ -46,5 +46,10 @@ 20dp 200dp -1dp + 46dp + 6dp + 54dp + 6dp + 12dp diff --git a/core/res/values/strings.xml b/core/res/values/strings.xml index b5856e1e..0d90b8cb 100644 --- a/core/res/values/strings.xml +++ b/core/res/values/strings.xml @@ -190,11 +190,13 @@ New torrents for %1$s + Loading… Open Transdroid SERVER VIEW LOOK & FEEL - Reverse sort order - Use dark theme + SORT ORDER + Reversed sort order + Use dark theme (no preview) DONE Servers diff --git a/core/res/xml/appwidget_info.xml b/core/res/xml/appwidget_info.xml index a236ffa0..7846f411 100644 --- a/core/res/xml/appwidget_info.xml +++ b/core/res/xml/appwidget_info.xml @@ -23,7 +23,7 @@ android:minResizeHeight="110dip" android:minResizeWidth="110dp" android:minWidth="180dp" - android:previewImage="@drawable/ic_launcher" + android:previewImage="@drawable/widget_preview" android:resizeMode="horizontal|vertical" android:updatePeriodMillis="3600000" android:widgetCategory="home_screen|keyguard" /> diff --git a/core/src/org/transdroid/core/gui/lists/SimpleListItemSpinnerAdapter.java b/core/src/org/transdroid/core/gui/lists/SimpleListItemSpinnerAdapter.java new file mode 100644 index 00000000..0d33e654 --- /dev/null +++ b/core/src/org/transdroid/core/gui/lists/SimpleListItemSpinnerAdapter.java @@ -0,0 +1,66 @@ +/* + * 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.gui.lists; + +import java.util.List; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +/** + * A wrapper around {@link ArrayAdapter} that contains {@link SimpleListItem}s which simply show their name in the + * Spinner. The standard Android spinner resources are used for the layout. + * @author Eric Kok + */ +public class SimpleListItemSpinnerAdapter extends ArrayAdapter { + + /** + * Constructs the adapter, supplying the {@link SimpleListItem}s to show in the spinner. The given resource will be + * ignored as the standard Android Spinner layout is used instead. + * @param context The UI context to inflate the layout in + * @param resource This is ignored; android.R.layout.simple_spinner_item is always used instead + * @param objects The items to show in the spinner, which can simply display some name + */ + public SimpleListItemSpinnerAdapter(Context context, int resource, List objects) { + super(context, android.R.layout.simple_spinner_item, objects); + setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + // This relies on the ArrayAdapter implementation and the used standard xml layouts that simply return a + // TextView; this can then be filled with the SimpleListItem's name instead of the standard toString() + // implementation + TextView text = (TextView) super.getView(position, convertView, parent); + text.setText(getItem(position).getName()); + return text; + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + // This relies on the ArrayAdapter implementation and the used standard xml layouts that simply return a + // TextView; this can then be filled with the SimpleListItem's name instead of the standard toString() + // implementation + TextView text = (TextView) super.getDropDownView(position, convertView, parent); + text.setText(getItem(position).getName()); + return text; + } + +} diff --git a/core/src/org/transdroid/core/gui/lists/SortByListItem.java b/core/src/org/transdroid/core/gui/lists/SortByListItem.java index d05c6aef..90e6110d 100644 --- a/core/src/org/transdroid/core/gui/lists/SortByListItem.java +++ b/core/src/org/transdroid/core/gui/lists/SortByListItem.java @@ -16,18 +16,42 @@ */ package org.transdroid.core.gui.lists; +import org.transdroid.core.R; import org.transdroid.daemon.TorrentsSortBy; +import android.content.Context; + /** * Represents a way in which a torrents list can be sorted. * @author Eric Kok */ public class SortByListItem implements SimpleListItem { - private TorrentsSortBy sortBy; + private final TorrentsSortBy sortBy; + private final String name; - public SortByListItem(TorrentsSortBy sortBy) { + public SortByListItem(Context context, TorrentsSortBy sortBy) { this.sortBy = sortBy; + switch (sortBy) { + case DateAdded: + this.name = context.getString(R.string.action_sort_added); + break; + case DateDone: + this.name = context.getString(R.string.action_sort_done); + break; + case Ratio: + this.name = context.getString(R.string.action_sort_ratio); + break; + case Status: + this.name = context.getString(R.string.action_sort_status); + break; + case UploadSpeed: + this.name = context.getString(R.string.action_sort_upspeed); + break; + default: + this.name = context.getString(R.string.action_sort_alpha); + break; + } } /** @@ -40,7 +64,7 @@ public class SortByListItem implements SimpleListItem { @Override public String getName() { - return sortBy.name(); + return name; } } diff --git a/core/src/org/transdroid/core/widget/WidgetConfigActivity.java b/core/src/org/transdroid/core/widget/WidgetConfigActivity.java index 2478623e..01d8276f 100644 --- a/core/src/org/transdroid/core/widget/WidgetConfigActivity.java +++ b/core/src/org/transdroid/core/widget/WidgetConfigActivity.java @@ -30,10 +30,9 @@ 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.lists.SimpleListItem; +import org.transdroid.core.gui.lists.SimpleListItemSpinnerAdapter; import org.transdroid.core.gui.lists.SortByListItem; import org.transdroid.core.gui.lists.TorrentsAdapter; -import org.transdroid.core.gui.navigation.FilterListItemAdapter; import org.transdroid.core.gui.navigation.StatusType; import org.transdroid.core.gui.navigation.StatusType.StatusTypeFilter; import org.transdroid.core.service.ConnectivityHelper; @@ -111,18 +110,20 @@ public class WidgetConfigActivity extends SherlockActivity { @AfterViews protected void init() { - // Populate the selection spinners - List sortOrders = new ArrayList(); + // Populate the selection spinners with custom array adapters + List sortOrders = new ArrayList(); for (TorrentsSortBy order : TorrentsSortBy.values()) { - sortOrders.add(new SortByListItem(order)); + sortOrders.add(new SortByListItem(this, order)); } - - serverSpinner.setAdapter(new FilterListItemAdapter(this, applicationSettings.getServerSettings())); - filterSpinner.setAdapter(new FilterListItemAdapter(this, StatusType.getAllStatusTypes(this))); - sortSpinner.setAdapter(new FilterListItemAdapter(this, sortOrders)); + serverSpinner.setAdapter(new SimpleListItemSpinnerAdapter(this, 0, applicationSettings + .getServerSettings())); + filterSpinner.setAdapter(new SimpleListItemSpinnerAdapter(this, 0, StatusType + .getAllStatusTypes(this))); + sortSpinner.setAdapter(new SimpleListItemSpinnerAdapter(this, 0, sortOrders)); // TODO: Update to AndroidAnnotations 3.0 and use @CheckedChanged reverseorderCheckBox.setOnCheckedChangeListener(reverseorderCheckedChanged); torrentsList.setAdapter(previewTorrentsAdapter); + torrentsList.setEmptyView(errorText); // Set up action bar with a done button // Inspired by NoNonsenseNotes's ListWidgetConfig.java (Apache License, Version 2.0) @@ -257,4 +258,5 @@ public class WidgetConfigActivity extends SherlockActivity { } }; + } diff --git a/core/src/org/transdroid/core/widget/WidgetProvider.java b/core/src/org/transdroid/core/widget/WidgetProvider.java index e599d7ab..c16165c4 100644 --- a/core/src/org/transdroid/core/widget/WidgetProvider.java +++ b/core/src/org/transdroid/core/widget/WidgetProvider.java @@ -20,7 +20,7 @@ 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.TorrentsActivity_; import org.transdroid.core.gui.log.Log; import android.annotation.TargetApi; @@ -40,46 +40,40 @@ 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"; + public static final String EXTRA_REFRESH = "extra_refresh"; @Bean protected ApplicationSettings applicationSettings; + @Override + public void onReceive(Context context, Intent intent) { + if (intent != null && 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); + } + @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { for (int appWidgetId : appWidgetIds) { appWidgetManager.updateAppWidget(appWidgetId, buildRemoteViews(context, appWidgetId, applicationSettings.getWidgetConfig(appWidgetId))); + appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.torrents_list); } } @Override public void onDeleted(Context context, int[] appWidgetIds) { - 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}. @@ -103,26 +97,44 @@ public class WidgetProvider extends AppWidgetProvider { // 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); + 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 - // 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); + Intent data = new Intent(context, WidgetService_.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); + Intent open = new Intent(context, TorrentsActivity_.class); + open.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); rv.setPendingIntentTemplate(R.id.torrents_list, - PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); + PendingIntent.getActivity(context, appWidgetId, open, 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)); + rv.setTextViewText(R.id.error_text, context.getString(R.string.widget_loading)); - // Show the server and status type filter from the widget configuration + // Show the server and status type filter from the widget configuration in the 'action bar' ServerSetting server = appSettings.getServerSetting(config.getServerId()); rv.setTextViewText(R.id.server_text, server.getName()); rv.setTextViewText(R.id.filter_text, config.getStatusType().getFilterItem(context).getName()); + + // Set up the START_SERVER intent for 'action bar' clicks to start Transdroid normally + Intent start = new Intent(context, TorrentsActivity_.class); + //start.setData(Uri.parse("intent://widget/" + appWidgetId + "/start/" + config.getServerId())); + start.setAction(INTENT_STARTSERVER); + start.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + start.putExtra(EXTRA_SERVER, config.getServerId()); rv.setOnClickPendingIntent(R.id.icon_image, - PendingIntent.getActivity(context, 0, intent.cloneFilter(), PendingIntent.FLAG_UPDATE_CURRENT)); + PendingIntent.getActivity(context, appWidgetId, start, PendingIntent.FLAG_UPDATE_CURRENT)); + rv.setOnClickPendingIntent(R.id.navigation_view, + PendingIntent.getActivity(context, appWidgetId, start, PendingIntent.FLAG_UPDATE_CURRENT)); + + // 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); + 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)); return rv; diff --git a/core/src/org/transdroid/core/widget/WidgetService.java b/core/src/org/transdroid/core/widget/WidgetService.java index 915bebd9..7f3ca7e3 100644 --- a/core/src/org/transdroid/core/widget/WidgetService.java +++ b/core/src/org/transdroid/core/widget/WidgetService.java @@ -39,7 +39,6 @@ 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; @@ -131,29 +130,28 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { // 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)); + rv.setTextViewText(R.id.progress_text, local.getProgressSizeText(context.getResources(), false)); + rv.setTextViewText(R.id.ratio_text, local.getProgressEtaRatioText(context.getResources())); int statusColour; switch (torrent.getStatusCode()) { case Downloading: - statusColour = r.getColor(r.getColor(R.color.torrent_downloading)); + statusColour = R.color.torrent_downloading; break; case Paused: - statusColour = r.getColor(r.getColor(R.color.torrent_paused)); + statusColour = R.color.torrent_paused; break; case Seeding: - statusColour = r.getColor(r.getColor(R.color.torrent_seeding)); + statusColour = R.color.torrent_seeding; break; case Error: - statusColour = r.getColor(r.getColor(R.color.torrent_error)); + statusColour = R.color.torrent_error; break; default: // Checking, Waiting, Queued, Unknown - statusColour = r.getColor(r.getColor(R.color.torrent_other)); + statusColour = R.color.torrent_other; break; } - rv.setInt(R.id.status_view, "setBackgroundColor", r.getColor(statusColour)); + 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); @@ -170,7 +168,9 @@ class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { @Override public void onDestroy() { - torrents.clear(); + if (torrents != null) + torrents.clear(); + torrents = null; } @Override