Eric Kok
12 years ago
69 changed files with 1850 additions and 422 deletions
@ -0,0 +1,16 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<!-- This layout is for phones in portrait and shows only the torrents list. --> |
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
tools:context=".DetailsActivity" > |
||||||
|
|
||||||
|
<fragment |
||||||
|
android:id="@+id/torrent_details" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
class="org.transdroid.core.gui.DetailsFragment_" |
||||||
|
tools:layout="@layout/fragment_details" /> |
||||||
|
|
||||||
|
</FrameLayout> |
@ -0,0 +1,58 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<fr.marvinlabs.widget.CheckableRelativeLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:orientation="vertical" |
||||||
|
android:layout_marginTop="@dimen/margin_half" |
||||||
|
android:layout_marginBottom="@dimen/margin_half" |
||||||
|
android:layout_marginLeft="@dimen/margin_half" |
||||||
|
android:layout_marginRight="@dimen/margin_default"> |
||||||
|
|
||||||
|
<fr.marvinlabs.widget.InertCheckBox |
||||||
|
android:id="@+id/file_checkbox" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" /> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/name_text" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_alignParentTop="true" |
||||||
|
android:layout_toRightOf="@+id/file_checkbox" |
||||||
|
android:textColor="#fff" |
||||||
|
android:textIsSelectable="true" |
||||||
|
android:textSize="15sp" /> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/progress_text" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_below="@id/name_text" |
||||||
|
android:layout_alignParentRight="true" |
||||||
|
android:layout_marginLeft="@dimen/margin_half" |
||||||
|
android:layout_marginTop="4dip" |
||||||
|
android:textSize="12sp" |
||||||
|
android:textIsSelectable="false" /> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/sizes_text" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_alignBaseline="@+id/progress_text" |
||||||
|
android:layout_alignBottom="@+id/progress_text" |
||||||
|
android:layout_toRightOf="@+id/priority_image" |
||||||
|
android:textIsSelectable="false" |
||||||
|
android:textSize="12sp" /> |
||||||
|
|
||||||
|
<ImageView |
||||||
|
android:id="@+id/priority_image" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_alignLeft="@+id/name_text" |
||||||
|
android:layout_below="@+id/name_text" |
||||||
|
android:layout_marginTop="4dip" |
||||||
|
android:layout_marginRight="@dimen/margin_half" |
||||||
|
android:contentDescription="@string/status_priority_normal" /> |
||||||
|
|
||||||
|
</fr.marvinlabs.widget.CheckableRelativeLayout> |
@ -0,0 +1,9 @@ |
|||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_refresh" |
||||||
|
android:icon="@drawable/ic_action_refresh" |
||||||
|
android:showAsAction="always" |
||||||
|
android:title="@string/action_refresh"/> |
||||||
|
|
||||||
|
</menu> |
@ -0,0 +1,8 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
|
||||||
|
<searchable xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:label="@string/search_torrentsearch" |
||||||
|
android:hint="@string/search_hint" |
||||||
|
android:searchSuggestAuthority="org.transdroid.core.gui.SearchHistoryProvider" |
||||||
|
android:searchSuggestSelection=" ? " /> |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package org.transdroid.lite.app.search; |
package org.transdroid.core.app.search; |
||||||
|
|
||||||
import java.util.ArrayList; |
import java.util.ArrayList; |
||||||
import java.util.List; |
import java.util.List; |
@ -1,4 +1,4 @@ |
|||||||
package org.transdroid.lite.app.search; |
package org.transdroid.core.app.search; |
||||||
|
|
||||||
import java.util.Date; |
import java.util.Date; |
||||||
|
|
@ -1,12 +1,12 @@ |
|||||||
package org.transdroid.lite.app.search; |
package org.transdroid.core.app.search; |
||||||
|
|
||||||
import org.transdroid.lite.gui.navigation.FilterItem; |
import org.transdroid.core.gui.lists.SimpleListItem; |
||||||
|
|
||||||
/** |
/** |
||||||
* Represents an available torrent site that can be searched using the Torrent Search package. |
* Represents an available torrent site that can be searched using the Torrent Search package. |
||||||
* @author Eric Kok |
* @author Eric Kok |
||||||
*/ |
*/ |
||||||
public class SearchSite implements FilterItem { |
public class SearchSite implements SimpleListItem { |
||||||
|
|
||||||
private final int id; |
private final int id; |
||||||
private final String key; |
private final String key; |
@ -1,4 +1,4 @@ |
|||||||
package org.transdroid.lite.app.settings; |
package org.transdroid.core.app.settings; |
||||||
|
|
||||||
import org.androidannotations.annotations.EBean; |
import org.androidannotations.annotations.EBean; |
||||||
import org.androidannotations.annotations.RootContext; |
import org.androidannotations.annotations.RootContext; |
@ -1,4 +1,4 @@ |
|||||||
package org.transdroid.lite.app.settings; |
package org.transdroid.core.app.settings; |
||||||
|
|
||||||
import org.androidannotations.annotations.EBean; |
import org.androidannotations.annotations.EBean; |
||||||
import org.androidannotations.annotations.EBean.Scope; |
import org.androidannotations.annotations.EBean.Scope; |
@ -0,0 +1,132 @@ |
|||||||
|
package org.transdroid.core.gui; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.AfterViews; |
||||||
|
import org.androidannotations.annotations.Background; |
||||||
|
import org.androidannotations.annotations.Bean; |
||||||
|
import org.androidannotations.annotations.EActivity; |
||||||
|
import org.androidannotations.annotations.Extra; |
||||||
|
import org.androidannotations.annotations.FragmentById; |
||||||
|
import org.androidannotations.annotations.InstanceState; |
||||||
|
import org.androidannotations.annotations.OptionsItem; |
||||||
|
import org.androidannotations.annotations.OptionsMenu; |
||||||
|
import org.androidannotations.annotations.UiThread; |
||||||
|
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.LocalTorrent; |
||||||
|
import org.transdroid.daemon.IDaemonAdapter; |
||||||
|
import org.transdroid.daemon.Torrent; |
||||||
|
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.GetFileListTask; |
||||||
|
import org.transdroid.daemon.task.GetFileListTaskSuccessResult; |
||||||
|
import org.transdroid.daemon.task.GetTorrentDetailsTask; |
||||||
|
import org.transdroid.daemon.task.GetTorrentDetailsTaskSuccessResult; |
||||||
|
import org.transdroid.daemon.task.RetrieveTask; |
||||||
|
import org.transdroid.daemon.task.RetrieveTaskSuccessResult; |
||||||
|
|
||||||
|
import android.widget.Toast; |
||||||
|
|
||||||
|
import com.actionbarsherlock.app.SherlockFragmentActivity; |
||||||
|
|
||||||
|
@EActivity(R.layout.activity_details) |
||||||
|
@OptionsMenu(R.menu.activity_details) |
||||||
|
public class DetailsActivity extends SherlockFragmentActivity { |
||||||
|
|
||||||
|
@Extra |
||||||
|
@InstanceState |
||||||
|
protected Torrent torrent; |
||||||
|
|
||||||
|
// Settings
|
||||||
|
@Bean |
||||||
|
protected ApplicationSettings applicationSettings; |
||||||
|
private IDaemonAdapter currentConnection = null; |
||||||
|
|
||||||
|
// Details view components
|
||||||
|
@FragmentById(R.id.torrent_details) |
||||||
|
protected DetailsFagment fragmentDetails; |
||||||
|
|
||||||
|
@AfterViews |
||||||
|
protected void init() { |
||||||
|
|
||||||
|
// Simple action bar with up, torrent name as title and refresh button
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
||||||
|
getSupportActionBar().setTitle(torrent.getName()); |
||||||
|
|
||||||
|
// Connect to the last used server
|
||||||
|
ServerSetting lastUsed = applicationSettings.getLastUsedServer(); |
||||||
|
currentConnection = lastUsed.createServerAdapter(); |
||||||
|
|
||||||
|
// Load fine details and torrent files
|
||||||
|
refreshTorrentDetails(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@OptionsItem(R.id.action_refresh) |
||||||
|
protected void refreshScreen() { |
||||||
|
refreshTorrent(); |
||||||
|
refreshTorrentDetails(); |
||||||
|
refreshTorrentFiles(); |
||||||
|
} |
||||||
|
|
||||||
|
@Background |
||||||
|
protected void refreshTorrent() { |
||||||
|
DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(); |
||||||
|
if (result instanceof RetrieveTaskSuccessResult) { |
||||||
|
onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels()); |
||||||
|
} else { |
||||||
|
onCommunicationError((DaemonTaskFailureResult)result); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
protected void onTorrentsRetrieved(List<Torrent> torrents, List<org.transdroid.daemon.Label> labels) { |
||||||
|
// Update the details fragment
|
||||||
|
fragmentDetails.perhapsUpdateTorrent(torrents); |
||||||
|
} |
||||||
|
|
||||||
|
@Background |
||||||
|
protected void refreshTorrentDetails() { |
||||||
|
DaemonTaskResult result = GetTorrentDetailsTask.create(currentConnection, torrent).execute(); |
||||||
|
if (result instanceof GetTorrentDetailsTaskSuccessResult) { |
||||||
|
onTorrentDetailsRetrieved(((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails()); |
||||||
|
} else { |
||||||
|
onCommunicationError((DaemonTaskFailureResult)result); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@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() { |
||||||
|
DaemonTaskResult result = GetFileListTask.create(currentConnection, torrent).execute(); |
||||||
|
if (result instanceof GetFileListTaskSuccessResult) { |
||||||
|
onTorrentFilesRetrieved(((GetFileListTaskSuccessResult) result).getFiles()); |
||||||
|
} else { |
||||||
|
onCommunicationError((DaemonTaskFailureResult)result); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
protected void onTorrentFilesRetrieved(List<TorrentFile> torrentFiles) { |
||||||
|
// Update the details fragment with the newly retrieved list of files
|
||||||
|
fragmentDetails.updateTorrentFiles(new ArrayList<TorrentFile>(torrentFiles)); |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
protected void onCommunicationError(DaemonTaskFailureResult result) { |
||||||
|
// TODO: Properly report this error
|
||||||
|
Toast.makeText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), |
||||||
|
Toast.LENGTH_LONG).show(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
package org.transdroid.core.gui; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.AfterViews; |
||||||
|
import org.androidannotations.annotations.EFragment; |
||||||
|
import org.androidannotations.annotations.InstanceState; |
||||||
|
import org.androidannotations.annotations.ViewById; |
||||||
|
import org.transdroid.core.R; |
||||||
|
import org.transdroid.core.gui.lists.DetailsAdapter; |
||||||
|
import org.transdroid.core.gui.lists.SimpleListItemAdapter; |
||||||
|
import org.transdroid.daemon.Torrent; |
||||||
|
import org.transdroid.daemon.TorrentDetails; |
||||||
|
import org.transdroid.daemon.TorrentFile; |
||||||
|
|
||||||
|
import android.widget.TextView; |
||||||
|
|
||||||
|
import com.actionbarsherlock.app.SherlockFragment; |
||||||
|
import com.actionbarsherlock.view.SherlockListView; |
||||||
|
|
||||||
|
/** |
||||||
|
* Fragment that shown detailed statistics about some torrent. These come from some already fetched {@link Torrent} |
||||||
|
* object, but it also retrieves further detailed statistics. |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
@EFragment(R.layout.fragment_details) |
||||||
|
public class DetailsFagment extends SherlockFragment { |
||||||
|
|
||||||
|
// Local data
|
||||||
|
@InstanceState |
||||||
|
protected Torrent torrent = null; |
||||||
|
@InstanceState |
||||||
|
protected TorrentDetails torrentDetails = null; |
||||||
|
@InstanceState |
||||||
|
protected ArrayList<TorrentFile> torrentFiles = null; |
||||||
|
|
||||||
|
// Views
|
||||||
|
@ViewById(R.id.details_list) |
||||||
|
protected SherlockListView detailsList; |
||||||
|
@ViewById |
||||||
|
protected TextView emptyText; |
||||||
|
|
||||||
|
@AfterViews |
||||||
|
protected void init() { |
||||||
|
|
||||||
|
detailsList.setAdapter(new DetailsAdapter()); |
||||||
|
detailsList.setEmptyView(emptyText); // Shows a text that no torrent was selected yet
|
||||||
|
if (torrent != null) |
||||||
|
updateTorrent(torrent); |
||||||
|
if (torrentDetails != null) |
||||||
|
updateTorrentDetails(torrentDetails); |
||||||
|
if (torrentFiles != null) |
||||||
|
updateTorrentFiles(torrentFiles); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the details adapter header to show the new torrent data |
||||||
|
* @param newTorrent The new torrent object |
||||||
|
*/ |
||||||
|
public void updateTorrent(Torrent newTorrent) { |
||||||
|
this.torrent = newTorrent; |
||||||
|
((DetailsAdapter) detailsList.getAdapter()).updateTorrent(newTorrent); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the details adapter to show the list of trackers and tracker errors |
||||||
|
* @param newTorrentDetails The new fine details object of some torrent |
||||||
|
*/ |
||||||
|
public void updateTorrentDetails(TorrentDetails newTorrentDetails) { |
||||||
|
this.torrentDetails = newTorrentDetails; |
||||||
|
((DetailsAdapter) detailsList.getAdapter()).updateTrackers(SimpleListItemAdapter.SimpleStringItem |
||||||
|
.wrapStringsList(newTorrentDetails.getTrackers())); |
||||||
|
((DetailsAdapter) detailsList.getAdapter()).updateErrors(SimpleListItemAdapter.SimpleStringItem |
||||||
|
.wrapStringsList(newTorrentDetails.getErrors())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the list adapter to show a new list of torrent files, replacing the old files list |
||||||
|
* @param newTorrents The new, updated list of torrent file objects |
||||||
|
*/ |
||||||
|
public void updateTorrentFiles(ArrayList<TorrentFile> newTorrentFiles) { |
||||||
|
this.torrentFiles = newTorrentFiles; |
||||||
|
((DetailsAdapter) detailsList.getAdapter()).updateTorrentFiles(newTorrentFiles); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Can be called if some outside activity returned new torrents, so we can perhaps piggyback on this by update our |
||||||
|
* data as well |
||||||
|
* @param torrents The last of retrieved torrents |
||||||
|
*/ |
||||||
|
public void perhapsUpdateTorrent(List<Torrent> torrents) { |
||||||
|
for (Torrent newTorrent : torrents) { |
||||||
|
if (newTorrent.getUniqueID().equals(this.torrent.getUniqueID())) { |
||||||
|
// Found, so we can update our data as well
|
||||||
|
updateTorrent(newTorrent); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clear the screen by fully clearing the internal merge list (with header and other lists) |
||||||
|
*/ |
||||||
|
public void clear() { |
||||||
|
detailsList.setAdapter(new DetailsAdapter()); |
||||||
|
torrent = null; |
||||||
|
torrentDetails = null; |
||||||
|
torrentFiles = null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package org.transdroid.core.gui; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.SearchRecentSuggestionsProvider; |
||||||
|
import android.provider.SearchRecentSuggestions; |
||||||
|
|
||||||
|
public class SearchHistoryProvider extends SearchRecentSuggestionsProvider { |
||||||
|
|
||||||
|
public final static String AUTHORITY = "org.transdroid.core.gui.SearchHistoryProvider"; |
||||||
|
public final static int MODE = DATABASE_MODE_QUERIES; |
||||||
|
|
||||||
|
public SearchHistoryProvider() { |
||||||
|
super(); |
||||||
|
setupSuggestions(AUTHORITY, MODE); |
||||||
|
} |
||||||
|
|
||||||
|
public static void clearHistory(Context context) { |
||||||
|
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(context, |
||||||
|
SearchHistoryProvider.AUTHORITY, SearchHistoryProvider.MODE); |
||||||
|
suggestions.clearHistory(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,253 @@ |
|||||||
|
package org.transdroid.core.gui; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.AfterViews; |
||||||
|
import org.androidannotations.annotations.Background; |
||||||
|
import org.androidannotations.annotations.Bean; |
||||||
|
import org.androidannotations.annotations.EActivity; |
||||||
|
import org.androidannotations.annotations.FragmentById; |
||||||
|
import org.androidannotations.annotations.InstanceState; |
||||||
|
import org.androidannotations.annotations.OptionsItem; |
||||||
|
import org.androidannotations.annotations.OptionsMenu; |
||||||
|
import org.androidannotations.annotations.SystemService; |
||||||
|
import org.androidannotations.annotations.UiThread; |
||||||
|
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.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.NavigationHelper; |
||||||
|
import org.transdroid.core.gui.navigation.StatusType; |
||||||
|
import org.transdroid.core.gui.navigation.StatusType.StatusTypeFilter; |
||||||
|
import org.transdroid.daemon.IDaemonAdapter; |
||||||
|
import org.transdroid.daemon.Torrent; |
||||||
|
import org.transdroid.daemon.task.DaemonTaskFailureResult; |
||||||
|
import org.transdroid.daemon.task.DaemonTaskResult; |
||||||
|
import org.transdroid.daemon.task.RetrieveTask; |
||||||
|
import org.transdroid.daemon.task.RetrieveTaskSuccessResult; |
||||||
|
|
||||||
|
import android.annotation.TargetApi; |
||||||
|
import android.app.SearchManager; |
||||||
|
import android.os.Build; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.AdapterView; |
||||||
|
import android.widget.AdapterView.OnItemSelectedListener; |
||||||
|
import android.widget.Toast; |
||||||
|
|
||||||
|
import com.actionbarsherlock.app.ActionBar; |
||||||
|
import com.actionbarsherlock.app.ActionBar.OnNavigationListener; |
||||||
|
import com.actionbarsherlock.app.SherlockFragmentActivity; |
||||||
|
import com.actionbarsherlock.view.Menu; |
||||||
|
import com.actionbarsherlock.view.MenuItem; |
||||||
|
import com.actionbarsherlock.view.SherlockListView; |
||||||
|
import com.actionbarsherlock.widget.SearchView; |
||||||
|
|
||||||
|
@EActivity(R.layout.activity_torrents) |
||||||
|
@OptionsMenu(R.menu.activity_torrents) |
||||||
|
public class TorrentsActivity extends SherlockFragmentActivity implements OnNavigationListener { |
||||||
|
|
||||||
|
// Navigation components
|
||||||
|
@Bean |
||||||
|
protected NavigationHelper navigationHelper; |
||||||
|
@ViewById |
||||||
|
protected SherlockListView filtersList; |
||||||
|
protected FilterListAdapter navigationListAdapter = null; |
||||||
|
protected FilterListAdapter navigationSpinnerAdapter = null; |
||||||
|
@SystemService |
||||||
|
protected SearchManager searchManager; |
||||||
|
|
||||||
|
// Settings
|
||||||
|
@Bean |
||||||
|
protected ApplicationSettings applicationSettings; |
||||||
|
@InstanceState |
||||||
|
boolean firstStart = true; |
||||||
|
private IDaemonAdapter currentConnection = null; |
||||||
|
|
||||||
|
// Torrents list components
|
||||||
|
@FragmentById(R.id.torrent_list) |
||||||
|
protected TorrentsFragment fragmentTorrents; |
||||||
|
|
||||||
|
// Details view components
|
||||||
|
@FragmentById(R.id.torrent_details) |
||||||
|
protected DetailsFagment fragmentDetails; |
||||||
|
|
||||||
|
@AfterViews |
||||||
|
protected void init() { |
||||||
|
|
||||||
|
// 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); |
||||||
|
// Servers are always added to the action bar spinner
|
||||||
|
navigationSpinnerAdapter.updateServers(applicationSettings.getServerSettings()); |
||||||
|
getSupportActionBar().setListNavigationCallbacks(navigationSpinnerAdapter, this); |
||||||
|
if (filtersList != null) { |
||||||
|
// There was room for a dedicated filter list; add the status types
|
||||||
|
navigationListAdapter = FilterListAdapter_.getInstance_(this); |
||||||
|
filtersList.setAdapter(navigationListAdapter); |
||||||
|
navigationListAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); |
||||||
|
filtersList.setOnItemSelectedListener(onFilterListItemSelected); |
||||||
|
} else { |
||||||
|
// Add status types directly to the action bar spinner
|
||||||
|
navigationSpinnerAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); |
||||||
|
} |
||||||
|
|
||||||
|
// Connect to the last used server
|
||||||
|
ServerSetting lastUsed = applicationSettings.getLastUsedServer(); |
||||||
|
if (lastUsed == null) { |
||||||
|
// No server settings yet;
|
||||||
|
return; |
||||||
|
} |
||||||
|
// Set this as selection in the action bar spinner; we can use the server setting key since we have stable ids
|
||||||
|
// TODO: Does this call the action bar item selection callback?
|
||||||
|
getSupportActionBar().setSelectedNavigationItem(lastUsed.getOrder()); |
||||||
|
|
||||||
|
// Handle any start up intents or instead just refresh the torrents list
|
||||||
|
if (firstStart) { |
||||||
|
handleStartIntent(); |
||||||
|
} else { |
||||||
|
refreshTorrents(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onResume() { |
||||||
|
super.onResume(); |
||||||
|
refreshTorrents(); |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.FROYO) |
||||||
|
@Override |
||||||
|
public boolean onCreateOptionsMenu(Menu menu) { |
||||||
|
super.onCreateOptionsMenu(menu); |
||||||
|
// For Android 2.1+, add an expandable SearchView to the action bar
|
||||||
|
MenuItem item = menu.findItem(R.id.action_search); |
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 8) { |
||||||
|
final SearchView searchView = new SearchView(this); |
||||||
|
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); |
||||||
|
searchView.setQueryRefinementEnabled(true); |
||||||
|
item.setActionView(searchView); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Called when an item in the action bar navigation spinner was selected |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean onNavigationItemSelected(int itemPosition, long itemId) { |
||||||
|
Object item = navigationSpinnerAdapter.getItem(itemPosition); |
||||||
|
if (item instanceof SimpleListItem) { |
||||||
|
// A filter item was selected form the navigation spinner
|
||||||
|
filterSelected((SimpleListItem) item); |
||||||
|
return true; |
||||||
|
} |
||||||
|
// A header was selected; no action
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// Handles clicks (selections) on the dedicated list of filter items (if it exists)
|
||||||
|
// NOTE: Unfortunately we cannot use the @ItemSelect(R.id.filters_list) annotation as it throws NPE exceptions when
|
||||||
|
// the list doesn't exist (read: on small screens)
|
||||||
|
protected OnItemSelectedListener onFilterListItemSelected = new OnItemSelectedListener() { |
||||||
|
@Override |
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { |
||||||
|
filterSelected((SimpleListItem) filtersList.getAdapter().getItem(position)); |
||||||
|
} |
||||||
|
@Override |
||||||
|
public void onNothingSelected(AdapterView<?> parent) { |
||||||
|
// TODO: Check if this happens
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* A new filter was selected; update the view over the current data |
||||||
|
* @param selected True if the filter item was selected, false if it was deselected |
||||||
|
* @param item The touched filter item |
||||||
|
*/ |
||||||
|
protected void filterSelected(SimpleListItem item) { |
||||||
|
|
||||||
|
// Server selection
|
||||||
|
if (item instanceof ServerSetting) { |
||||||
|
ServerSetting server = (ServerSetting) item; |
||||||
|
|
||||||
|
if (currentConnection != null && server.equals(currentConnection.getSettings())) { |
||||||
|
// Already connected to this server; just ask for a refresh instead
|
||||||
|
refreshTorrents(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Update connection to the newly selected server and refresh
|
||||||
|
currentConnection = server.createServerAdapter(); |
||||||
|
clearScreens(); |
||||||
|
refreshTorrents(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if (item instanceof StatusTypeFilter) { |
||||||
|
// TODO: Update the torrent list view
|
||||||
|
} |
||||||
|
|
||||||
|
if (item instanceof Label) { |
||||||
|
// TODO: Update the torrent list view
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* If required, add torrents, switch to a specific server, etc. |
||||||
|
*/ |
||||||
|
protected void handleStartIntent() { |
||||||
|
// TODO: Handle start intent
|
||||||
|
} |
||||||
|
|
||||||
|
@OptionsItem(R.id.action_refresh) |
||||||
|
protected void refreshScreen() { |
||||||
|
refreshTorrents(); |
||||||
|
// TODO: Refresh TorentDetails and TorrentFiles as well
|
||||||
|
} |
||||||
|
|
||||||
|
private void clearScreens() { |
||||||
|
// Clear the currently shown list of torrent and perhaps the details
|
||||||
|
fragmentTorrents.clear(); |
||||||
|
if (fragmentDetails != null) { |
||||||
|
fragmentDetails.clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Background |
||||||
|
protected void refreshTorrents() { |
||||||
|
DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(); |
||||||
|
if (result instanceof RetrieveTaskSuccessResult) { |
||||||
|
onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels()); |
||||||
|
} else { |
||||||
|
onCommunicationError((DaemonTaskFailureResult)result); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@UiThread |
||||||
|
protected void onTorrentsRetrieved(List<Torrent> torrents, List<org.transdroid.daemon.Label> labels) { |
||||||
|
// Report the newly retrieved list of torrents to the torrents fragment
|
||||||
|
fragmentTorrents.updateTorrents(new ArrayList<Torrent>(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 onCommunicationError(DaemonTaskFailureResult result) { |
||||||
|
// TODO: Properly report this error
|
||||||
|
Toast.makeText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), |
||||||
|
Toast.LENGTH_LONG).show(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package org.transdroid.core.gui; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.AfterViews; |
||||||
|
import org.androidannotations.annotations.EFragment; |
||||||
|
import org.androidannotations.annotations.InstanceState; |
||||||
|
import org.androidannotations.annotations.ItemClick; |
||||||
|
import org.androidannotations.annotations.ViewById; |
||||||
|
import org.transdroid.core.R; |
||||||
|
import org.transdroid.core.gui.lists.TorrentsAdapter; |
||||||
|
import org.transdroid.daemon.Torrent; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
import android.widget.TextView; |
||||||
|
|
||||||
|
import com.actionbarsherlock.app.SherlockFragment; |
||||||
|
import com.actionbarsherlock.view.SherlockListView; |
||||||
|
|
||||||
|
@EFragment(R.layout.fragment_torrents) |
||||||
|
public class TorrentsFragment extends SherlockFragment { |
||||||
|
|
||||||
|
// Local data
|
||||||
|
@InstanceState |
||||||
|
protected ArrayList<Torrent> torrents = null; |
||||||
|
|
||||||
|
// Views
|
||||||
|
@ViewById(R.id.torrent_list) |
||||||
|
protected SherlockListView torrentsList; |
||||||
|
@ViewById |
||||||
|
protected TextView emptyText; |
||||||
|
|
||||||
|
@AfterViews |
||||||
|
protected void init() { |
||||||
|
torrentsList.setAdapter(new TorrentsAdapter()); |
||||||
|
torrentsList.setEmptyView(emptyText); |
||||||
|
if (torrents != null) |
||||||
|
updateTorrents(torrents); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the list adapter to show a new list of torrent objects, replacing the old torrents completely |
||||||
|
* @param newTorrents The new, updated list of torrents |
||||||
|
*/ |
||||||
|
public void updateTorrents(ArrayList<Torrent> 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
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clear currently visible list of torrents |
||||||
|
*/ |
||||||
|
public void clear() { |
||||||
|
updateTorrents(null); |
||||||
|
} |
||||||
|
|
||||||
|
@ItemClick(R.id.torrent_list) |
||||||
|
protected void torrentsListClicked(Torrent torrent) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,141 @@ |
|||||||
|
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.EBean; |
||||||
|
import org.androidannotations.annotations.RootContext; |
||||||
|
import org.transdroid.core.R; |
||||||
|
import org.transdroid.core.gui.navigation.FilterSeparatorView_; |
||||||
|
import org.transdroid.daemon.Torrent; |
||||||
|
import org.transdroid.daemon.TorrentFile; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.widget.BaseAdapter; |
||||||
|
|
||||||
|
import com.commonsware.cwac.merge.MergeAdapter; |
||||||
|
|
||||||
|
/** |
||||||
|
* List adapter that holds a header view showing torrent details and show the list list contained by the torrent. |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
@EBean |
||||||
|
public class DetailsAdapter extends MergeAdapter { |
||||||
|
|
||||||
|
@RootContext |
||||||
|
protected Context context; |
||||||
|
private TorrentDetailsView torrentDetailsView = null; |
||||||
|
private TorrentFilesAdapter torrentFilesAdapter = null; |
||||||
|
private SimpleListItemAdapter trackersAdapter = null; |
||||||
|
private SimpleListItemAdapter errorsAdapter = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Update the torrent data in the details header of this merge adapter |
||||||
|
* @param torrent The torrent for which detailed data is shown |
||||||
|
*/ |
||||||
|
public void updateTorrent(Torrent torrent) { |
||||||
|
if (this.torrentDetailsView == null) { |
||||||
|
torrentDetailsView = TorrentDetailsView_.build(context); |
||||||
|
addView(torrentDetailsView, false); |
||||||
|
} |
||||||
|
torrentDetailsView.update(torrent); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Update the list of files contained in this torrent |
||||||
|
* @param torrentFiles The new list of files |
||||||
|
*/ |
||||||
|
public void updateTorrentFiles(List<TorrentFile> torrentFiles) { |
||||||
|
if (this.torrentFilesAdapter == null && torrentFiles != null) { |
||||||
|
addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.status_files)), false); |
||||||
|
this.torrentFilesAdapter = new TorrentFilesAdapter(context, torrentFiles); |
||||||
|
addAdapter(torrentFilesAdapter); |
||||||
|
} else if (this.torrentFilesAdapter != null && torrentFiles != null) { |
||||||
|
this.torrentFilesAdapter.update(torrentFiles); |
||||||
|
} else { |
||||||
|
this.torrentFilesAdapter = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Update the list of trackers |
||||||
|
* @param trackers The new list of trackers known for this torrent |
||||||
|
*/ |
||||||
|
public void updateTrackers(List<? extends SimpleListItem> trackers) { |
||||||
|
if (this.trackersAdapter == null && trackers != null) { |
||||||
|
addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.status_trackers)), false); |
||||||
|
this.trackersAdapter = new SimpleListItemAdapter(context, trackers); |
||||||
|
addAdapter(trackersAdapter); |
||||||
|
} else if (this.trackersAdapter != null && trackers != null) { |
||||||
|
this.trackersAdapter.update(trackers); |
||||||
|
} else { |
||||||
|
this.trackersAdapter = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Update the list of errors |
||||||
|
* @param errors The new list of errors known for this torrent |
||||||
|
*/ |
||||||
|
public void updateErrors(List<? extends SimpleListItem> errors) { |
||||||
|
if (this.errorsAdapter == null && errors != null) { |
||||||
|
addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.status_errors)), false); |
||||||
|
this.errorsAdapter = new SimpleListItemAdapter(context, errors); |
||||||
|
addAdapter(errorsAdapter); |
||||||
|
} else if (this.errorsAdapter != null && errors != null) { |
||||||
|
this.errorsAdapter.update(errors); |
||||||
|
} else { |
||||||
|
this.errorsAdapter = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected class TorrentFilesAdapter extends BaseAdapter { |
||||||
|
|
||||||
|
private final Context context; |
||||||
|
private List<TorrentFile> items; |
||||||
|
|
||||||
|
public TorrentFilesAdapter(Context context, List<TorrentFile> items) { |
||||||
|
this.context = context; |
||||||
|
this.items = items; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Allows updating of the full data list underlying this adapter, replacing all items |
||||||
|
* @param newItems The new list of files to display |
||||||
|
*/ |
||||||
|
public void update(List<TorrentFile> newItems) { |
||||||
|
this.items = newItems; |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getCount() { |
||||||
|
return items.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public TorrentFile getItem(int position) { |
||||||
|
return items.get(position); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getItemId(int position) { |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public View getView(int position, View convertView, ViewGroup parent) { |
||||||
|
TorrentFileView torrentFileView; |
||||||
|
if (convertView == null) { |
||||||
|
torrentFileView = TorrentFileView_.build(context); |
||||||
|
} else { |
||||||
|
torrentFileView = (TorrentFileView) convertView; |
||||||
|
} |
||||||
|
torrentFileView.bind(getItem(position)); |
||||||
|
return torrentFileView; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,233 @@ |
|||||||
|
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
|
import java.util.Locale; |
||||||
|
|
||||||
|
import org.transdroid.core.R; |
||||||
|
import org.transdroid.daemon.DaemonException; |
||||||
|
import org.transdroid.daemon.Torrent; |
||||||
|
import org.transdroid.daemon.TorrentStatus; |
||||||
|
import org.transdroid.daemon.util.FileSizeConverter; |
||||||
|
import org.transdroid.daemon.util.TimespanConverter; |
||||||
|
|
||||||
|
import android.content.res.Resources; |
||||||
|
|
||||||
|
/** |
||||||
|
* Wrapper around Torrent to provide some addition getters that give translatable or otherwise formatted Strings of |
||||||
|
* torrent statistics. |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
public class LocalTorrent { |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates the LocalTorrent object so that the translatable/formattable version of a Torrent can be used. |
||||||
|
* @param torrent The Torrent object |
||||||
|
* @return The torrent wrapped as LocalTorrent object |
||||||
|
*/ |
||||||
|
public static LocalTorrent fromTorrent(Torrent torrent) { |
||||||
|
return new LocalTorrent(torrent); |
||||||
|
} |
||||||
|
|
||||||
|
private final Torrent t; |
||||||
|
|
||||||
|
private LocalTorrent(Torrent torrent) { |
||||||
|
this.t = torrent; |
||||||
|
} |
||||||
|
|
||||||
|
private static final String DECIMAL_FORMATTER = "%.1f"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Builds a string showing the upload/download seed ratio. If not downloading, it will base the ratio on the total |
||||||
|
* size; so if you created the torrent yourself you will have downloaded 0 bytes, but the ratio will pretend you |
||||||
|
* have 100%. |
||||||
|
* @return A nicely formatted string containing the upload/download seed ratio |
||||||
|
*/ |
||||||
|
public String getRatioString() { |
||||||
|
long baseSize = t.getTotalSize(); |
||||||
|
if (t.getStatusCode() == TorrentStatus.Downloading) { |
||||||
|
baseSize = t.getDownloadedEver(); |
||||||
|
} |
||||||
|
if (baseSize <= 0) { |
||||||
|
return String.format(Locale.getDefault(), DECIMAL_FORMATTER, 0d); |
||||||
|
} else if (t.getRatio() == Double.POSITIVE_INFINITY) { |
||||||
|
return "\u221E"; |
||||||
|
} else { |
||||||
|
return String.format(Locale.getDefault(), DECIMAL_FORMATTER, t.getRatio()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a formatted string indicating the current progress in terms of transferred bytes |
||||||
|
* @param r The context resources, to access translations |
||||||
|
* @param withAvailability Whether to show file availability in-line |
||||||
|
* @return A nicely formatted string indicating torrent status and, if applicable, progress in bytes |
||||||
|
*/ |
||||||
|
public String getProgressSizeText(Resources r, boolean withAvailability) { |
||||||
|
|
||||||
|
switch (t.getStatusCode()) { |
||||||
|
case Waiting: |
||||||
|
case Checking: |
||||||
|
case Error: |
||||||
|
// Not downloading yet
|
||||||
|
return r.getString(R.string.status_waitingtodl, FileSizeConverter.getSize(t.getTotalSize())); |
||||||
|
case Downloading: |
||||||
|
// Downloading
|
||||||
|
return r.getString( |
||||||
|
R.string.status_size1, |
||||||
|
FileSizeConverter.getSize(t.getDownloadedEver()), |
||||||
|
FileSizeConverter.getSize(t.getTotalSize()), |
||||||
|
String.format(DECIMAL_FORMATTER, t.getDownloadedPercentage() * 100) |
||||||
|
+ "%" |
||||||
|
+ (!withAvailability ? "" : "/" |
||||||
|
+ String.format(DECIMAL_FORMATTER, t.getAvailability() * 100) + "%")); |
||||||
|
case Seeding: |
||||||
|
case Paused: |
||||||
|
case Queued: |
||||||
|
// Seeding or paused
|
||||||
|
return r.getString(R.string.status_size2, FileSizeConverter.getSize(t.getTotalSize()), |
||||||
|
FileSizeConverter.getSize(t.getUploadedEver())); |
||||||
|
default: |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a formatted string indicating either the expected time to download (ETA) or, when seeding, the ratio |
||||||
|
* @param r The context resources, to access translations |
||||||
|
* @return A string like '~ 34 seconds', or 'RATIO 8.2' or an empty string |
||||||
|
*/ |
||||||
|
public String getProgressEtaRatioText(Resources r) { |
||||||
|
switch (t.getStatusCode()) { |
||||||
|
case Downloading: |
||||||
|
// Downloading
|
||||||
|
return getRemainingTimeString(r, true, false); |
||||||
|
case Seeding: |
||||||
|
case Paused: |
||||||
|
case Queued: |
||||||
|
// Seeding or paused
|
||||||
|
return r.getString(R.string.status_ratio, getRatioString()); |
||||||
|
case Waiting: |
||||||
|
case Checking: |
||||||
|
case Error: |
||||||
|
default: |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a formatted string indicating the torrent status and connected peers |
||||||
|
* @param r The context resources, to access translations |
||||||
|
* @return A string like 'Queued' or, when seeding or leeching, '2 OF 28 PEERS' |
||||||
|
*/ |
||||||
|
public String getProgressConnectionText(Resources r) { |
||||||
|
|
||||||
|
switch (t.getStatusCode()) { |
||||||
|
case Waiting: |
||||||
|
return r.getString(R.string.status_waiting); |
||||||
|
case Checking: |
||||||
|
return r.getString(R.string.status_checking); |
||||||
|
case Downloading: |
||||||
|
return r.getString(R.string.status_peers, t.getPeersSendingToUs(), t.getPeersConnected()); |
||||||
|
case Seeding: |
||||||
|
return r.getString(R.string.status_peers, t.getPeersGettingFromUs(), t.getPeersConnected()); |
||||||
|
case Paused: |
||||||
|
return r.getString(R.string.status_paused); |
||||||
|
case Queued: |
||||||
|
return r.getString(R.string.status_stopped); |
||||||
|
case Error: |
||||||
|
return r.getString(R.string.status_error); |
||||||
|
default: |
||||||
|
return r.getString(R.string.status_unknown); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a formatted string indicating current transfer speeds for the torrent |
||||||
|
* @param r The context resources, to access translations |
||||||
|
* @return A string like '↓ 28KB/s ↑ 1.8MB/s', or an empty string when not transferrring |
||||||
|
*/ |
||||||
|
public String getProgressSpeedText(Resources r) { |
||||||
|
|
||||||
|
switch (t.getStatusCode()) { |
||||||
|
case Waiting: |
||||||
|
case Checking: |
||||||
|
case Paused: |
||||||
|
case Queued: |
||||||
|
return ""; |
||||||
|
case Downloading: |
||||||
|
return r.getString(R.string.status_speed_down, FileSizeConverter.getSize(t.getRateDownload()) + "/s") + " " |
||||||
|
+ r.getString(R.string.status_speed_up, FileSizeConverter.getSize(t.getRateUpload()) + "/s"); |
||||||
|
case Seeding: |
||||||
|
return r.getString(R.string.status_speed_up, FileSizeConverter.getSize(t.getRateUpload()) + "/s"); |
||||||
|
default: |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public String getProgressStatusEta(Resources r) { |
||||||
|
switch (t.getStatusCode()) { |
||||||
|
case Waiting: |
||||||
|
return r.getString(R.string.status_waiting).toUpperCase(Locale.getDefault()); |
||||||
|
case Checking: |
||||||
|
return r.getString(R.string.status_checking).toUpperCase(Locale.getDefault()); |
||||||
|
case Error: |
||||||
|
return r.getString(R.string.status_error).toUpperCase(Locale.getDefault()); |
||||||
|
case Downloading: |
||||||
|
// Downloading
|
||||||
|
return r.getString(R.string.status_downloading).toUpperCase(Locale.getDefault()) + " (" |
||||||
|
+ String.format(DECIMAL_FORMATTER, t.getDownloadedPercentage() * 100) + "%), " |
||||||
|
+ getRemainingTimeString(r, false, true); |
||||||
|
case Seeding: |
||||||
|
return r.getString(R.string.status_seeding).toUpperCase(Locale.getDefault()); |
||||||
|
case Paused: |
||||||
|
return r.getString(R.string.status_paused).toUpperCase(Locale.getDefault()); |
||||||
|
case Queued: |
||||||
|
return r.getString(R.string.status_queued).toUpperCase(Locale.getDefault()); |
||||||
|
default: |
||||||
|
return r.getString(R.string.status_unknown).toUpperCase(Locale.getDefault()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a formatted string indicating the remaining download time |
||||||
|
* @param r The context resources, to access translations |
||||||
|
* @param inDays Whether to show days or use hours for > 24 hours left instead |
||||||
|
* @return A string like '4d 8h 34m 5s' or '2m 3s' |
||||||
|
*/ |
||||||
|
public String getRemainingTimeString(Resources r, boolean abbreviate, boolean inDays) { |
||||||
|
if (t.getEta() == -1 || t.getEta() == -2) { |
||||||
|
return r.getString(R.string.status_unknowneta); |
||||||
|
} |
||||||
|
return r.getString(abbreviate ? R.string.status_eta : R.string.status_etalong, |
||||||
|
TimespanConverter.getTime(t.getEta(), inDays)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert a DaemonException to a translatable human-readable error message |
||||||
|
* @param e The exception that was thrown by the server |
||||||
|
* @return A string resource ID to show to the user |
||||||
|
*/ |
||||||
|
public static int getResourceForDaemonException(DaemonException e) { |
||||||
|
switch (e.getType()) { |
||||||
|
case MethodUnsupported: |
||||||
|
return R.string.error_jsonrequesterror; |
||||||
|
case ConnectionError: |
||||||
|
return R.string.error_httperror; |
||||||
|
case UnexpectedResponse: |
||||||
|
return R.string.error_jsonresponseerror; |
||||||
|
case ParsingFailed: |
||||||
|
return R.string.error_jsonrequesterror; |
||||||
|
case NotConnected: |
||||||
|
return R.string.error_daemonnotconnected; |
||||||
|
case AuthenticationFailure: |
||||||
|
return R.string.error_401; |
||||||
|
case FileAccessError: |
||||||
|
return R.string.error_torrentfile; |
||||||
|
default: |
||||||
|
return R.string.error_httperror; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,11 +1,11 @@ |
|||||||
package org.transdroid.lite.gui.navigation; |
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
/** |
/** |
||||||
* Represents a filter item as shown in the navigation list or spinner. |
* Represents a filter item as shown in the navigation list or spinner. |
||||||
* |
* |
||||||
* @author Eric Kok |
* @author Eric Kok |
||||||
*/ |
*/ |
||||||
public interface FilterItem { |
public interface SimpleListItem { |
||||||
|
|
||||||
public String getName(); |
public String getName(); |
||||||
|
|
@ -0,0 +1,91 @@ |
|||||||
|
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.widget.BaseAdapter; |
||||||
|
|
||||||
|
public class SimpleListItemAdapter extends BaseAdapter { |
||||||
|
|
||||||
|
private final Context context; |
||||||
|
private List<? extends SimpleListItem> items; |
||||||
|
|
||||||
|
public SimpleListItemAdapter(Context context, List<? extends SimpleListItem> items) { |
||||||
|
this.context = context; |
||||||
|
this.items = items; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Allows updating of the full data list underlying this adapter, replacing all items |
||||||
|
* @param newItems The new list of filter items to display |
||||||
|
*/ |
||||||
|
public void update(List<? extends SimpleListItem> newItems) { |
||||||
|
this.items = newItems; |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getCount() { |
||||||
|
return items.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public SimpleListItem getItem(int position) { |
||||||
|
return items.get(position); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getItemId(int position) { |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public View getView(int position, View convertView, ViewGroup parent) { |
||||||
|
SimpleListItemView filterItemView; |
||||||
|
if (convertView == null) { |
||||||
|
filterItemView = SimpleListItemView_.build(context); |
||||||
|
} else { |
||||||
|
filterItemView = (SimpleListItemView) convertView; |
||||||
|
} |
||||||
|
filterItemView.bind(getItem(position)); |
||||||
|
return filterItemView; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Represents a very simple list item that only contains a single string to show in the list. Use wrapStringsList to |
||||||
|
* wrap an existing list of strings into a list of {@link SimpleListItem}s. |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
public static class SimpleStringItem implements SimpleListItem { |
||||||
|
|
||||||
|
/** |
||||||
|
* Wraps a simple string of strings into a list of SimpleStringItem to add as data to a |
||||||
|
* {@link SimpleListItemAdapter} |
||||||
|
* @param errorStrings A list of string |
||||||
|
* @return A list of SimpleStringItem objects representing the input strings |
||||||
|
*/ |
||||||
|
public static List<SimpleStringItem> wrapStringsList(List<String> errorStrings) { |
||||||
|
ArrayList<SimpleStringItem> errors = new ArrayList<SimpleStringItem>(); |
||||||
|
for (String errorString : errorStrings) { |
||||||
|
errors.add(new SimpleStringItem(errorString)); |
||||||
|
} |
||||||
|
return errors; |
||||||
|
} |
||||||
|
|
||||||
|
private final String string; |
||||||
|
|
||||||
|
public SimpleStringItem(String string) { |
||||||
|
this.string = string; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return this.string; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.EViewGroup; |
||||||
|
import org.androidannotations.annotations.ViewById; |
||||||
|
import org.transdroid.core.R; |
||||||
|
import org.transdroid.daemon.Daemon; |
||||||
|
import org.transdroid.daemon.Torrent; |
||||||
|
import org.transdroid.daemon.util.FileSizeConverter; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.text.TextUtils; |
||||||
|
import android.text.format.DateUtils; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.RelativeLayout; |
||||||
|
import android.widget.TextView; |
||||||
|
|
||||||
|
/** |
||||||
|
* Represents a group of views that show torrent status, sizes, speeds and other details. |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
@EViewGroup(R.layout.fragment_details_header) |
||||||
|
public class TorrentDetailsView extends RelativeLayout { |
||||||
|
|
||||||
|
@ViewById |
||||||
|
protected TextView labelText, dateaddedText, uploadedText, uploadedunitText, ratioText, upspeedText, seedersText, |
||||||
|
downloadedunitText, downloadedText, totalsizeText, downspeedText, leechersText, statusText; |
||||||
|
|
||||||
|
public TorrentDetailsView(Context context) { |
||||||
|
super(context); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Update the text fields with new/updated torrent details |
||||||
|
* @param torrent The torrent for which to show details |
||||||
|
*/ |
||||||
|
public void update(Torrent torrent) { |
||||||
|
|
||||||
|
LocalTorrent local = LocalTorrent.fromTorrent(torrent); |
||||||
|
|
||||||
|
// Set label text
|
||||||
|
if (Daemon.supportsLabels(torrent.getDaemon())) { |
||||||
|
if (TextUtils.isEmpty(torrent.getLabelName())) { |
||||||
|
labelText.setText(getResources().getString(R.string.labels_unlabeled)); |
||||||
|
} else { |
||||||
|
labelText.setText(torrent.getLabelName()); |
||||||
|
} |
||||||
|
labelText.setVisibility(View.VISIBLE); |
||||||
|
} else { |
||||||
|
labelText.setVisibility(View.INVISIBLE); |
||||||
|
} |
||||||
|
|
||||||
|
// Set status texts
|
||||||
|
if (torrent.getDateAdded() != null) { |
||||||
|
dateaddedText.setText(getResources().getString( |
||||||
|
R.string.status_sincedate, |
||||||
|
DateUtils.getRelativeDateTimeString(getContext(), torrent.getDateAdded().getTime(), |
||||||
|
DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_ABBREV_MONTH))); |
||||||
|
dateaddedText.setVisibility(View.VISIBLE); |
||||||
|
} else { |
||||||
|
dateaddedText.setVisibility(View.INVISIBLE); |
||||||
|
} |
||||||
|
statusText.setText(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(), |
||||||
|
torrent.getPeersConnected())); |
||||||
|
leechersText.setText(getResources().getString(R.string.status_peers, torrent.getPeersSendingToUs(), |
||||||
|
torrent.getPeersConnected())); |
||||||
|
// TODO: Add field that displays torrent errors (as opposed to tracker errors)
|
||||||
|
// TODO: Add field that displays availability
|
||||||
|
|
||||||
|
// Sizes and speeds texts
|
||||||
|
totalsizeText.setText(FileSizeConverter.getSize(torrent.getTotalSize())); |
||||||
|
downloadedText.setText(FileSizeConverter.getSize(torrent.getDownloadedEver(), false)); |
||||||
|
downloadedunitText.setText(FileSizeConverter.getSizeUnit(torrent.getDownloadedEver()).toString()); |
||||||
|
uploadedText.setText(FileSizeConverter.getSize(torrent.getUploadedEver(), false)); |
||||||
|
uploadedunitText.setText(FileSizeConverter.getSizeUnit(torrent.getUploadedEver()).toString()); |
||||||
|
downspeedText.setText(getResources().getString(R.string.status_speed_down, |
||||||
|
FileSizeConverter.getSize(torrent.getRateDownload()) + "/s")); |
||||||
|
upspeedText.setText(getResources().getString(R.string.status_speed_up, |
||||||
|
FileSizeConverter.getSize(torrent.getRateUpload()) + "/s")); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.EViewGroup; |
||||||
|
import org.androidannotations.annotations.ViewById; |
||||||
|
import org.transdroid.core.R; |
||||||
|
import org.transdroid.daemon.TorrentFile; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.widget.ImageView; |
||||||
|
import android.widget.TextView; |
||||||
|
import fr.marvinlabs.widget.CheckableRelativeLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* View that represents some {@link TorrentFile} object and show the file's name, status and priority |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
@EViewGroup(R.layout.list_item_torrentfile) |
||||||
|
public class TorrentFileView extends CheckableRelativeLayout { |
||||||
|
|
||||||
|
@ViewById |
||||||
|
protected TextView nameText, progressText, sizesText; |
||||||
|
@ViewById |
||||||
|
protected ImageView priorityImage; |
||||||
|
|
||||||
|
public TorrentFileView(Context context) { |
||||||
|
super(context, null); |
||||||
|
} |
||||||
|
|
||||||
|
public void bind(TorrentFile torrentFile) { |
||||||
|
nameText.setText(torrentFile.getName()); |
||||||
|
sizesText.setText(torrentFile.getDownloadedAndTotalSizeText()); |
||||||
|
progressText.setText(torrentFile.getProgressText()); |
||||||
|
switch (torrentFile.getPriority()) { |
||||||
|
case Off: |
||||||
|
priorityImage.setImageResource(R.drawable.ic_priority_off); |
||||||
|
priorityImage.setContentDescription(getResources().getString(R.string.status_priority_low)); |
||||||
|
break; |
||||||
|
case Low: |
||||||
|
priorityImage.setImageResource(R.drawable.ic_priority_low); |
||||||
|
priorityImage.setContentDescription(getResources().getString(R.string.status_priority_normal)); |
||||||
|
break; |
||||||
|
case Normal: |
||||||
|
priorityImage.setImageResource(R.drawable.ic_priority_normal); |
||||||
|
priorityImage.setContentDescription(getResources().getString(R.string.status_priority_normal)); |
||||||
|
break; |
||||||
|
case High: |
||||||
|
priorityImage.setImageResource(R.drawable.ic_priority_high); |
||||||
|
priorityImage.setContentDescription(getResources().getString(R.string.status_priority_high)); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package org.transdroid.lite.gui.lists; |
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
import org.transdroid.core.R; |
import org.transdroid.core.R; |
||||||
|
|
@ -0,0 +1,47 @@ |
|||||||
|
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.EViewGroup; |
||||||
|
import org.androidannotations.annotations.ViewById; |
||||||
|
import org.transdroid.core.R; |
||||||
|
import org.transdroid.daemon.Torrent; |
||||||
|
import org.transdroid.daemon.TorrentStatus; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.ImageView; |
||||||
|
import android.widget.TextView; |
||||||
|
import fr.marvinlabs.widget.CheckableRelativeLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* View that represents some {@link Torrent} object and displays progress, status, speeds, etc. |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
@EViewGroup(R.layout.list_item_torrent) |
||||||
|
public class TorrentView extends CheckableRelativeLayout { |
||||||
|
|
||||||
|
@ViewById |
||||||
|
protected ImageView priorityImage; |
||||||
|
@ViewById |
||||||
|
protected TextView nameText, ratioText, progressText, speedText, peersText; |
||||||
|
@ViewById |
||||||
|
protected TorrentProgressBar torrentProgressbar; |
||||||
|
|
||||||
|
public TorrentView(Context context) { |
||||||
|
super(context, null); |
||||||
|
} |
||||||
|
|
||||||
|
public void bind(Torrent torrent) { |
||||||
|
LocalTorrent local = LocalTorrent.fromTorrent(torrent); |
||||||
|
nameText.setText(torrent.getName()); |
||||||
|
ratioText.setText(local.getProgressEtaRatioText(getResources())); |
||||||
|
progressText.setText(local.getProgressSizeText(getResources(), false)); |
||||||
|
speedText.setText(local.getProgressSpeedText(getResources())); |
||||||
|
peersText.setText(local.getProgressConnectionText(getResources())); |
||||||
|
torrentProgressbar.setProgress((int) (torrent.getDownloadedPercentage() * 100)); |
||||||
|
torrentProgressbar.setActive(torrent.canPause());; |
||||||
|
torrentProgressbar.setError(torrent.getStatusCode() == TorrentStatus.Error); |
||||||
|
// TODO: Implement per-torrent priority and set priorityImage
|
||||||
|
priorityImage.setVisibility(View.INVISIBLE); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.EBean; |
||||||
|
import org.androidannotations.annotations.RootContext; |
||||||
|
import org.transdroid.core.gui.lists.TorrentView_; |
||||||
|
import org.transdroid.daemon.Torrent; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.widget.BaseAdapter; |
||||||
|
|
||||||
|
/** |
||||||
|
* Adapter that contains a list of torrent objects to show. |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
@EBean |
||||||
|
public class TorrentsAdapter extends BaseAdapter { |
||||||
|
|
||||||
|
private ArrayList<Torrent> torrents = null; |
||||||
|
|
||||||
|
@RootContext |
||||||
|
protected Context context; |
||||||
|
|
||||||
|
/** |
||||||
|
* Allows updating the full internal list of torrents at once, replacing the old list |
||||||
|
* @param newTorrents The new list of torrent objects |
||||||
|
*/ |
||||||
|
public void update(ArrayList<Torrent> newTorrents) { |
||||||
|
this.torrents = newTorrents; |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getCount() { |
||||||
|
if (torrents == null) |
||||||
|
return 0; |
||||||
|
return torrents.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Torrent getItem(int position) { |
||||||
|
if (torrents == null) |
||||||
|
return null; |
||||||
|
return torrents.get(position); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getItemId(int position) { |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public View getView(int position, View convertView, ViewGroup parent) { |
||||||
|
TorrentView torrentView; |
||||||
|
if (convertView == null) { |
||||||
|
torrentView = TorrentView_.build(context); |
||||||
|
} else { |
||||||
|
torrentView = (TorrentView) convertView; |
||||||
|
} |
||||||
|
torrentView.bind(getItem(position)); |
||||||
|
return torrentView; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
package org.transdroid.core.gui.navigation; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.androidannotations.annotations.EBean; |
||||||
|
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 android.content.Context; |
||||||
|
|
||||||
|
import com.commonsware.cwac.merge.MergeAdapter; |
||||||
|
|
||||||
|
/** |
||||||
|
* List adapter that holds filter items, that is, servers, view types and labels. A header item is inserted where |
||||||
|
* appropriate. |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
@EBean |
||||||
|
public class FilterListAdapter extends MergeAdapter { |
||||||
|
|
||||||
|
@RootContext |
||||||
|
protected Context context; |
||||||
|
private SimpleListItemAdapter serverItems = null; |
||||||
|
private SimpleListItemAdapter statusTypeItems = null; |
||||||
|
private SimpleListItemAdapter labelItems = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Update the list of available servers |
||||||
|
* @param servers The new list of available servers |
||||||
|
*/ |
||||||
|
public void updateServers(List<? extends SimpleListItem> servers) { |
||||||
|
if (this.serverItems == null && servers != null) { |
||||||
|
addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_servers)), false); |
||||||
|
this.serverItems = new SimpleListItemAdapter(context, servers); |
||||||
|
addAdapter(serverItems); |
||||||
|
} else if (this.serverItems != null && servers != null) { |
||||||
|
this.serverItems.update(servers); |
||||||
|
} else { |
||||||
|
this.serverItems = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Update the list of available status types |
||||||
|
* @param statusTypes The new list of available status types |
||||||
|
*/ |
||||||
|
public void updateStatusTypes(List<? extends SimpleListItem> statusTypes) { |
||||||
|
if (this.statusTypeItems == null && statusTypes != null) { |
||||||
|
addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_status)), false); |
||||||
|
this.statusTypeItems = new SimpleListItemAdapter(context, statusTypes); |
||||||
|
addAdapter(statusTypeItems); |
||||||
|
} else if (this.statusTypeItems != null && statusTypes != null) { |
||||||
|
this.statusTypeItems.update(statusTypes); |
||||||
|
} else { |
||||||
|
this.statusTypeItems = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Update the list of available labels |
||||||
|
* @param labels The new list of available labels |
||||||
|
*/ |
||||||
|
public void updateLabels(List<? extends SimpleListItem> labels) { |
||||||
|
if (this.labelItems == null && labels != null) { |
||||||
|
addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_labels)), false); |
||||||
|
this.labelItems = new SimpleListItemAdapter(context, labels); |
||||||
|
addAdapter(labelItems); |
||||||
|
} else if (this.serverItems != null && labels != null) { |
||||||
|
this.labelItems.update(labels); |
||||||
|
} else { |
||||||
|
this.labelItems = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,10 +1,12 @@ |
|||||||
package org.transdroid.lite.gui.navigation; |
package org.transdroid.core.gui.navigation; |
||||||
|
|
||||||
|
import org.transdroid.core.gui.lists.SimpleListItem; |
||||||
|
|
||||||
/** |
/** |
||||||
* Represents some label that is active or available on the server. |
* Represents some label that is active or available on the server. |
||||||
* @author Eric Kok |
* @author Eric Kok |
||||||
*/ |
*/ |
||||||
public class Label implements FilterItem { |
public class Label implements SimpleListItem { |
||||||
|
|
||||||
private final String name; |
private final String name; |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package org.transdroid.lite.gui.navigation; |
package org.transdroid.core.gui.navigation; |
||||||
|
|
||||||
import org.androidannotations.annotations.EBean; |
import org.androidannotations.annotations.EBean; |
||||||
import org.androidannotations.annotations.RootContext; |
import org.androidannotations.annotations.RootContext; |
@ -1,9 +1,9 @@ |
|||||||
package org.transdroid.lite.gui.settings; |
package org.transdroid.core.gui.settings; |
||||||
|
|
||||||
import org.androidannotations.annotations.Bean; |
import org.androidannotations.annotations.Bean; |
||||||
import org.androidannotations.annotations.EActivity; |
import org.androidannotations.annotations.EActivity; |
||||||
import org.androidannotations.annotations.Extra; |
import org.androidannotations.annotations.Extra; |
||||||
import org.transdroid.lite.app.settings.ApplicationSettings; |
import org.transdroid.core.app.settings.ApplicationSettings; |
||||||
|
|
||||||
import android.os.Bundle; |
import android.os.Bundle; |
||||||
|
|
@ -1,6 +1,6 @@ |
|||||||
package org.transdroid.lite.gui.settings; |
package org.transdroid.core.gui.settings; |
||||||
|
|
||||||
import org.transdroid.lite.app.settings.RssfeedSetting; |
import org.transdroid.core.app.settings.RssfeedSetting; |
||||||
|
|
||||||
import android.content.Context; |
import android.content.Context; |
||||||
import android.preference.Preference; |
import android.preference.Preference; |
@ -1,10 +1,10 @@ |
|||||||
package org.transdroid.lite.gui.settings; |
package org.transdroid.core.gui.settings; |
||||||
|
|
||||||
import org.androidannotations.annotations.Bean; |
import org.androidannotations.annotations.Bean; |
||||||
import org.androidannotations.annotations.EActivity; |
import org.androidannotations.annotations.EActivity; |
||||||
import org.androidannotations.annotations.Extra; |
import org.androidannotations.annotations.Extra; |
||||||
import org.transdroid.core.R; |
import org.transdroid.core.R; |
||||||
import org.transdroid.lite.app.settings.ApplicationSettings; |
import org.transdroid.core.app.settings.ApplicationSettings; |
||||||
|
|
||||||
import android.os.Bundle; |
import android.os.Bundle; |
||||||
|
|
@ -1,6 +1,6 @@ |
|||||||
package org.transdroid.lite.gui.settings; |
package org.transdroid.core.gui.settings; |
||||||
|
|
||||||
import org.transdroid.lite.app.settings.ServerSetting; |
import org.transdroid.core.app.settings.ServerSetting; |
||||||
|
|
||||||
import android.content.Context; |
import android.content.Context; |
||||||
import android.preference.Preference; |
import android.preference.Preference; |
@ -1,11 +1,11 @@ |
|||||||
package org.transdroid.lite.gui.settings; |
package org.transdroid.core.gui.settings; |
||||||
|
|
||||||
import org.androidannotations.annotations.Bean; |
import org.androidannotations.annotations.Bean; |
||||||
import org.androidannotations.annotations.EActivity; |
import org.androidannotations.annotations.EActivity; |
||||||
import org.androidannotations.annotations.Extra; |
import org.androidannotations.annotations.Extra; |
||||||
import org.transdroid.daemon.Daemon; |
import org.transdroid.daemon.Daemon; |
||||||
import org.transdroid.core.R; |
import org.transdroid.core.R; |
||||||
import org.transdroid.lite.app.settings.ApplicationSettings; |
import org.transdroid.core.app.settings.ApplicationSettings; |
||||||
|
|
||||||
import android.content.SharedPreferences; |
import android.content.SharedPreferences; |
||||||
import android.os.Bundle; |
import android.os.Bundle; |
@ -1,6 +1,6 @@ |
|||||||
package org.transdroid.lite.gui.settings; |
package org.transdroid.core.gui.settings; |
||||||
|
|
||||||
import org.transdroid.lite.app.settings.WebsearchSetting; |
import org.transdroid.core.app.settings.WebsearchSetting; |
||||||
|
|
||||||
import android.content.Context; |
import android.content.Context; |
||||||
import android.preference.Preference; |
import android.preference.Preference; |
@ -1,10 +1,10 @@ |
|||||||
package org.transdroid.lite.gui.settings; |
package org.transdroid.core.gui.settings; |
||||||
|
|
||||||
import org.androidannotations.annotations.Bean; |
import org.androidannotations.annotations.Bean; |
||||||
import org.androidannotations.annotations.EActivity; |
import org.androidannotations.annotations.EActivity; |
||||||
import org.androidannotations.annotations.Extra; |
import org.androidannotations.annotations.Extra; |
||||||
import org.transdroid.core.R; |
import org.transdroid.core.R; |
||||||
import org.transdroid.lite.app.settings.ApplicationSettings; |
import org.transdroid.core.app.settings.ApplicationSettings; |
||||||
|
|
||||||
import android.os.Bundle; |
import android.os.Bundle; |
||||||
|
|
@ -1,47 +0,0 @@ |
|||||||
package org.transdroid.lite.gui; |
|
||||||
|
|
||||||
import org.androidannotations.annotations.AfterViews; |
|
||||||
import org.androidannotations.annotations.EFragment; |
|
||||||
import org.androidannotations.annotations.FragmentArg; |
|
||||||
import org.androidannotations.annotations.InstanceState; |
|
||||||
import org.androidannotations.annotations.ViewById; |
|
||||||
import org.transdroid.daemon.Torrent; |
|
||||||
import org.transdroid.daemon.TorrentDetails; |
|
||||||
import org.transdroid.core.R; |
|
||||||
|
|
||||||
import android.view.View; |
|
||||||
import android.widget.TextView; |
|
||||||
|
|
||||||
import com.actionbarsherlock.app.SherlockFragment; |
|
||||||
|
|
||||||
/** |
|
||||||
* Fragment that shown detailed statistics about some torrent. These come from some already fetched {@link Torrent} |
|
||||||
* object, but it also retrieves further detailed statistics. |
|
||||||
* |
|
||||||
* @author Eric Kok |
|
||||||
*/ |
|
||||||
@EFragment(R.layout.fragment_details) |
|
||||||
public class DetailsFagment extends SherlockFragment { |
|
||||||
|
|
||||||
@FragmentArg |
|
||||||
@InstanceState |
|
||||||
protected Torrent torrent = null; |
|
||||||
@InstanceState |
|
||||||
protected TorrentDetails torrentDetails; |
|
||||||
|
|
||||||
@ViewById |
|
||||||
protected TextView emptyText; |
|
||||||
|
|
||||||
@AfterViews |
|
||||||
protected void init() { |
|
||||||
|
|
||||||
if (torrent == null) { |
|
||||||
// No torrent specified; show the placeholder layout only
|
|
||||||
emptyText.setVisibility(View.VISIBLE); |
|
||||||
} |
|
||||||
|
|
||||||
// TODO: Show the torrent details and load the advanced statistics
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,94 +0,0 @@ |
|||||||
package org.transdroid.lite.gui; |
|
||||||
|
|
||||||
import org.androidannotations.annotations.AfterViews; |
|
||||||
import org.androidannotations.annotations.Bean; |
|
||||||
import org.androidannotations.annotations.EActivity; |
|
||||||
import org.androidannotations.annotations.FragmentById; |
|
||||||
import org.androidannotations.annotations.ItemSelect; |
|
||||||
import org.androidannotations.annotations.OptionsMenu; |
|
||||||
import org.androidannotations.annotations.ViewById; |
|
||||||
import org.transdroid.core.R; |
|
||||||
import org.transdroid.lite.app.settings.ApplicationSettings; |
|
||||||
import org.transdroid.lite.gui.navigation.FilterAdapter; |
|
||||||
import org.transdroid.lite.gui.navigation.FilterItem; |
|
||||||
import org.transdroid.lite.gui.navigation.NavigationHelper; |
|
||||||
import org.transdroid.lite.gui.navigation.StatusType; |
|
||||||
|
|
||||||
import com.actionbarsherlock.app.ActionBar; |
|
||||||
import com.actionbarsherlock.app.ActionBar.OnNavigationListener; |
|
||||||
import com.actionbarsherlock.app.SherlockFragmentActivity; |
|
||||||
import com.actionbarsherlock.view.SherlockListView; |
|
||||||
|
|
||||||
@EActivity(R.layout.activity_torrents) |
|
||||||
@OptionsMenu(R.menu.activity_torrents) |
|
||||||
public class TorrentsActivity extends SherlockFragmentActivity implements OnNavigationListener { |
|
||||||
|
|
||||||
// Navigation components
|
|
||||||
@Bean |
|
||||||
protected NavigationHelper navigationHelper; |
|
||||||
@ViewById |
|
||||||
protected SherlockListView filtersList; |
|
||||||
protected FilterAdapter navigationListAdapter = null; |
|
||||||
protected FilterAdapter navigationSpinnerAdapter = null; |
|
||||||
|
|
||||||
// Settings
|
|
||||||
@Bean |
|
||||||
protected ApplicationSettings applicationSettings; |
|
||||||
|
|
||||||
// Torrents list components
|
|
||||||
@FragmentById(R.id.torrent_list) |
|
||||||
protected TorrentsFragment fragmentTorrents; |
|
||||||
|
|
||||||
// Details view components
|
|
||||||
@FragmentById(R.id.torrent_details) |
|
||||||
protected DetailsFagment fragmentDetails; |
|
||||||
|
|
||||||
@AfterViews |
|
||||||
protected void init() { |
|
||||||
|
|
||||||
// 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 = new FilterAdapter(this); |
|
||||||
// Servers are always added to the action bar spinner
|
|
||||||
navigationSpinnerAdapter.updateServers(applicationSettings.getServerSettings()); |
|
||||||
getSupportActionBar().setListNavigationCallbacks(navigationSpinnerAdapter, this); |
|
||||||
if (filtersList != null) { |
|
||||||
// There was room for a dedicated filter list; add the status types
|
|
||||||
navigationListAdapter = new FilterAdapter(this); |
|
||||||
filtersList.setAdapter(navigationListAdapter); |
|
||||||
navigationListAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); |
|
||||||
} else { |
|
||||||
// Add status types directly to the action bar spinner
|
|
||||||
navigationSpinnerAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Called when an item in the action bar navigation spinner was selected |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public boolean onNavigationItemSelected(int itemPosition, long itemId) { |
|
||||||
Object item = navigationSpinnerAdapter.getItem(itemPosition); |
|
||||||
if (item instanceof FilterItem) { |
|
||||||
// A filter item was selected form the navigation spinner
|
|
||||||
filterSelected(true, (FilterItem) item); |
|
||||||
return true; |
|
||||||
} |
|
||||||
// A header was selected; no action
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* A new filter was selected; update the view over the current data |
|
||||||
* @param selected True if the filter item was selected, false if it was deselected |
|
||||||
* @param item The touched filter item |
|
||||||
*/ |
|
||||||
@ItemSelect(R.id.filters_list) |
|
||||||
protected void filterSelected(boolean selected, FilterItem item) { |
|
||||||
// TODO: Update the torrent list view
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,11 +0,0 @@ |
|||||||
package org.transdroid.lite.gui; |
|
||||||
|
|
||||||
import org.androidannotations.annotations.EFragment; |
|
||||||
import org.transdroid.core.R; |
|
||||||
|
|
||||||
import com.actionbarsherlock.app.SherlockFragment; |
|
||||||
|
|
||||||
@EFragment(R.layout.fragment_torrents) |
|
||||||
public class TorrentsFragment extends SherlockFragment { |
|
||||||
|
|
||||||
} |
|
@ -1,125 +0,0 @@ |
|||||||
package org.transdroid.lite.gui.navigation; |
|
||||||
|
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
import org.transdroid.core.R; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.view.View; |
|
||||||
import android.view.ViewGroup; |
|
||||||
import android.widget.BaseAdapter; |
|
||||||
|
|
||||||
import com.commonsware.cwac.merge.MergeAdapter; |
|
||||||
|
|
||||||
/** |
|
||||||
* List adapter that holds filter items, that is, servers, view types and labels. A header item is intersted where |
|
||||||
* appropriate. |
|
||||||
* @author Eric Kok |
|
||||||
*/ |
|
||||||
public class FilterAdapter extends MergeAdapter { |
|
||||||
|
|
||||||
private Context context; |
|
||||||
private FilterItemAdapter serverItems = null; |
|
||||||
private FilterItemAdapter statusTypeItems = null; |
|
||||||
private FilterItemAdapter labelItems = null; |
|
||||||
|
|
||||||
public FilterAdapter(Context context) { |
|
||||||
this.context = context; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Update the list of available servers. |
|
||||||
* @param servers The new list of available servers |
|
||||||
*/ |
|
||||||
public void updateServers(List<? extends FilterItem> servers) { |
|
||||||
if (this.serverItems == null && servers != null) { |
|
||||||
addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_servers)), false); |
|
||||||
this.serverItems = new FilterItemAdapter(context, servers); |
|
||||||
addAdapter(serverItems); |
|
||||||
} else if (this.serverItems != null && servers != null) { |
|
||||||
this.serverItems.update(servers); |
|
||||||
} else { |
|
||||||
this.serverItems = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Update the list of available status types. |
|
||||||
* @param statusTypes The new list of available status types |
|
||||||
*/ |
|
||||||
public void updateStatusTypes(List<? extends FilterItem> statusTypes) { |
|
||||||
if (this.statusTypeItems == null && statusTypes != null) { |
|
||||||
addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_status)), false); |
|
||||||
this.statusTypeItems = new FilterItemAdapter(context, statusTypes); |
|
||||||
addAdapter(statusTypeItems); |
|
||||||
} else if (this.statusTypeItems != null && statusTypes != null) { |
|
||||||
this.statusTypeItems.update(statusTypes); |
|
||||||
} else { |
|
||||||
this.statusTypeItems = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Update the list of available labels. |
|
||||||
* @param labels The new list of available labels |
|
||||||
*/ |
|
||||||
public void updateLabels(List<? extends FilterItem> labels) { |
|
||||||
if (this.labelItems == null && labels != null) { |
|
||||||
addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_labels)), false); |
|
||||||
this.labelItems = new FilterItemAdapter(context, labels); |
|
||||||
addAdapter(labelItems); |
|
||||||
} else if (this.serverItems != null && labels != null) { |
|
||||||
this.labelItems.update(labels); |
|
||||||
} else { |
|
||||||
this.labelItems = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
protected class FilterItemAdapter extends BaseAdapter { |
|
||||||
|
|
||||||
private final Context context; |
|
||||||
private List<? extends FilterItem> items; |
|
||||||
|
|
||||||
public FilterItemAdapter(Context context, List<? extends FilterItem> items) { |
|
||||||
this.context = context; |
|
||||||
this.items = items; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Allows updating of the full data list underlying this adapter, replacing all items |
|
||||||
* @param newItems The new list of filter items to display |
|
||||||
*/ |
|
||||||
public void update(List<? extends FilterItem> newItems) { |
|
||||||
this.items = newItems; |
|
||||||
notifyDataSetChanged(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getCount() { |
|
||||||
return items.size(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public FilterItem getItem(int position) { |
|
||||||
return items.get(position); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public long getItemId(int position) { |
|
||||||
return position; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public View getView(int position, View convertView, ViewGroup parent) { |
|
||||||
FilterItemView filterItemView; |
|
||||||
if (convertView == null) { |
|
||||||
filterItemView = FilterItemView_.build(context); |
|
||||||
} else { |
|
||||||
filterItemView = (FilterItemView) convertView; |
|
||||||
} |
|
||||||
filterItemView.bind(getItem(position)); |
|
||||||
return filterItemView; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue