Eric Kok
11 years ago
32 changed files with 1251 additions and 36 deletions
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout 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" |
||||
android:orientation="horizontal" |
||||
android:baselineAligned="false" |
||||
tools:context=".SearchActivity" > |
||||
|
||||
<com.actionbarsherlock.view.SherlockListView |
||||
android:id="@+id/searchsites_list" |
||||
android:layout_width="0dip" |
||||
android:layout_height="match_parent" |
||||
android:layout_weight="1" |
||||
android:choiceMode="singleChoice" |
||||
android:listSelector="?attr/selectable_background_transdroid" /> |
||||
|
||||
<fragment |
||||
android:id="@+id/searchresults_list" |
||||
android:layout_width="0dip" |
||||
android:layout_height="match_parent" |
||||
android:layout_weight="3" |
||||
class="org.transdroid.core.gui.search.SearchResultsFragment_" |
||||
tools:layout="@layout/fragment_searchresults" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/installmodule_text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:drawablePadding="8dip" |
||||
android:drawableTop="?attr/loading_progress" |
||||
android:gravity="center" |
||||
android:maxWidth="400dip" |
||||
android:padding="@dimen/margin_default" |
||||
android:text="@string/search_installsearch" |
||||
android:textIsSelectable="false" |
||||
android:visibility="gone" /> |
||||
|
||||
</LinearLayout> |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="match_parent" |
||||
android:orientation="vertical" |
||||
android:paddingRight="@dimen/margin_half" > |
||||
|
||||
<TextView |
||||
android:id="@+id/searchsite_text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:textColor="?attr/text_actionbar" |
||||
android:textIsSelectable="false" |
||||
android:textSize="@dimen/abs__action_bar_title_text_size" |
||||
android:fontFamily="sans-serif-condensed" /> |
||||
|
||||
</FrameLayout> |
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<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=".SearchActivity" > |
||||
|
||||
<fragment |
||||
android:id="@+id/searchresults_list" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
class="org.transdroid.core.gui.search.SearchResultsFragment_" |
||||
tools:layout="@layout/fragment_searchresults" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/installmodule_text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:drawablePadding="8dip" |
||||
android:drawableTop="?attr/loading_progress" |
||||
android:gravity="center" |
||||
android:maxWidth="400dip" |
||||
android:padding="@dimen/margin_default" |
||||
android:text="@string/search_installsearch" |
||||
android:textIsSelectable="false" |
||||
android:visibility="gone" /> |
||||
|
||||
</FrameLayout> |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" > |
||||
|
||||
<com.actionbarsherlock.view.SherlockListView |
||||
android:id="@+id/searchresults_list" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:choiceMode="multipleChoiceModal" |
||||
android:listSelector="?attr/selectable_background_transdroid" |
||||
android:visibility="gone" /> |
||||
|
||||
<ProgressBar |
||||
android:id="@+id/loading_progress" |
||||
android:layout_width="128dp" |
||||
android:layout_height="128dp" |
||||
android:layout_gravity="center" |
||||
android:indeterminate="true" |
||||
android:indeterminateDrawable="?attr/loading_progress" |
||||
android:indeterminateOnly="true" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/empty_text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:drawablePadding="8dip" |
||||
android:drawableTop="?attr/loading_progress" |
||||
android:gravity="center" |
||||
android:maxWidth="400dip" |
||||
android:padding="@dimen/margin_default" |
||||
android:text="@string/search_noresults" |
||||
android:textIsSelectable="false" |
||||
android:visibility="gone" /> |
||||
|
||||
</FrameLayout> |
@ -0,0 +1,58 @@
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<org.transdroid.core.gui.rss.RssitemStatusLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="wrap_content" |
||||
android:background="?attr/selectable_background_transdroid" |
||||
android:paddingBottom="@dimen/margin_half" |
||||
android:paddingLeft="@dimen/margin_default" |
||||
android:paddingRight="@dimen/margin_default" |
||||
android:paddingTop="@dimen/margin_half" > |
||||
|
||||
<TextView |
||||
android:id="@+id/name_text" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:fontFamily="sans-serif-condensed" |
||||
android:textColor="?attr/text_bright" |
||||
android:textIsSelectable="false" |
||||
android:textSize="@dimen/text_enlarged" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/leechers_text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_below="@id/name_text" |
||||
android:layout_alignParentRight="true" |
||||
android:layout_marginTop="4dip" |
||||
android:textIsSelectable="false" |
||||
android:textSize="@dimen/text_small" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/seeders_text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_alignBaseline="@id/leechers_text" |
||||
android:layout_toLeftOf="@id/leechers_text" |
||||
android:layout_marginRight="@dimen/margin_default" |
||||
android:textIsSelectable="false" |
||||
android:textSize="@dimen/text_small" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/size_text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_alignBaseline="@id/leechers_text" |
||||
android:textIsSelectable="false" |
||||
android:textSize="@dimen/text_small" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/date_text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_alignBaseline="@id/leechers_text" |
||||
android:layout_toRightOf="@id/size_text" |
||||
android:layout_marginRight="@dimen/margin_default" |
||||
android:textIsSelectable="false" |
||||
android:textSize="@dimen/text_small" /> |
||||
|
||||
</org.transdroid.core.gui.rss.RssitemStatusLayout> |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:paddingBottom="@dimen/margin_half" |
||||
android:paddingLeft="@dimen/margin_default" |
||||
android:paddingRight="@dimen/margin_default" |
||||
android:paddingTop="@dimen/margin_half" > |
||||
|
||||
<ImageView |
||||
android:id="@+id/favicon_image" |
||||
android:layout_width="24dip" |
||||
android:layout_height="24dip" |
||||
android:layout_marginRight="@dimen/margin_half" |
||||
android:scaleType="centerCrop" |
||||
tools:ignore="contentDescription" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/name_text" |
||||
android:layout_width="0dip" |
||||
android:layout_height="wrap_content" |
||||
android:layout_weight="1" |
||||
android:layout_marginTop="1dip" |
||||
android:textIsSelectable="false" /> |
||||
|
||||
</LinearLayout> |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
||||
|
||||
<item |
||||
android:id="@+id/action_refresh" |
||||
android:icon="?attr/ic_action_refresh" |
||||
android:showAsAction="always" |
||||
android:title="@string/action_refresh"/> |
||||
<item |
||||
android:id="@+id/action_search" |
||||
android:icon="?attr/ic_action_search" |
||||
android:showAsAction="collapseActionView|ifRoom" |
||||
android:title="@string/action_search"/> |
||||
<item |
||||
android:id="@+id/action_downloadsearch" |
||||
android:showAsAction="always" |
||||
android:title="@string/search_download"/> |
||||
|
||||
</menu> |
@ -1,9 +1,15 @@
@@ -1,9 +1,15 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
||||
|
||||
<item |
||||
android:id="@+id/action_showdetails" |
||||
android:icon="?attr/ic_action_new" |
||||
android:showAsAction="always" |
||||
android:title="@string/action_showdetails" /> |
||||
|
||||
<item |
||||
android:id="@+id/action_addall" |
||||
android:icon="?attr/ic_action_new" |
||||
android:showAsAction="always" |
||||
android:title="@string/action_addall" /> |
||||
|
||||
|
||||
</menu> |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
||||
|
||||
<item |
||||
android:id="@+id/action_addall" |
||||
android:icon="?attr/ic_action_new" |
||||
android:showAsAction="always" |
||||
android:title="@string/action_addall" /> |
||||
|
||||
</menu> |
@ -0,0 +1,248 @@
@@ -0,0 +1,248 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.androidannotations.annotations.AfterViews; |
||||
import org.androidannotations.annotations.Bean; |
||||
import org.androidannotations.annotations.EActivity; |
||||
import org.androidannotations.annotations.FragmentById; |
||||
import org.androidannotations.annotations.OptionsItem; |
||||
import org.androidannotations.annotations.OptionsMenu; |
||||
import org.androidannotations.annotations.SystemService; |
||||
import org.androidannotations.annotations.ViewById; |
||||
import org.transdroid.core.R; |
||||
import org.transdroid.core.app.search.SearchHelper; |
||||
import org.transdroid.core.app.search.SearchSite; |
||||
import org.transdroid.core.app.settings.ApplicationSettings; |
||||
import org.transdroid.core.app.settings.SystemSettings_; |
||||
import org.transdroid.core.app.settings.WebsearchSetting; |
||||
import org.transdroid.core.gui.TorrentsActivity_; |
||||
import org.transdroid.core.gui.navigation.NavigationHelper; |
||||
|
||||
import android.annotation.TargetApi; |
||||
import android.app.SearchManager; |
||||
import android.content.Intent; |
||||
import android.net.Uri; |
||||
import android.os.Build; |
||||
import android.os.Bundle; |
||||
import android.provider.SearchRecentSuggestions; |
||||
import android.view.View; |
||||
import android.widget.AdapterView; |
||||
import android.widget.AdapterView.OnItemClickListener; |
||||
import android.widget.TextView; |
||||
|
||||
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; |
||||
|
||||
/** |
||||
* An activity that shows search results to the user (after a query was supplied by the standard Android search manager) |
||||
* and either shows the list of search sites on the left (e.g. on tablets) or allows switching between search sites via |
||||
* the action bar spinner. |
||||
* @author Eric Kok |
||||
*/ |
||||
@EActivity(resName = "activity_search") |
||||
@OptionsMenu(resName = "activity_search") |
||||
public class SearchActivity extends SherlockFragmentActivity implements OnNavigationListener { |
||||
|
||||
@FragmentById(resName = "searchresults_list") |
||||
protected SearchResultsFragment fragmentResults; |
||||
@ViewById |
||||
protected SherlockListView searchsitesList; |
||||
@ViewById |
||||
protected TextView installmoduleText; |
||||
@Bean |
||||
protected ApplicationSettings applicationSettings; |
||||
@Bean |
||||
protected NavigationHelper navigationHelper; |
||||
@Bean |
||||
protected SearchHelper searchHelper; |
||||
@SystemService |
||||
protected SearchManager searchManager; |
||||
private SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, |
||||
TorrentSearchHistoryProvider.AUTHORITY, TorrentSearchHistoryProvider.MODE); |
||||
|
||||
private List<SearchSetting> searchSites; |
||||
private SearchSetting lastUsedSite; |
||||
private String lastUsedQuery; |
||||
|
||||
@Override |
||||
public void onCreate(Bundle savedInstanceState) { |
||||
// Set the theme according to the user preference
|
||||
if (SystemSettings_.getInstance_(this).useDarkTheme()) { |
||||
setTheme(R.style.TransdroidTheme_Dark); |
||||
getSupportActionBar().setIcon(R.drawable.ic_activity_torrents); |
||||
} |
||||
super.onCreate(savedInstanceState); |
||||
} |
||||
|
||||
@AfterViews |
||||
protected void init() { |
||||
|
||||
// Get the user query, as coming from the standard SearchManager
|
||||
handleIntent(getIntent()); |
||||
|
||||
if (!searchHelper.isTorrentSearchInstalled()) { |
||||
// The module install text will be shown instead (in onPrepareOptionsMenu)
|
||||
return; |
||||
} |
||||
|
||||
// Load sites and find the last used (or set as default) search site
|
||||
searchSites = applicationSettings.getSearchSettings(); |
||||
lastUsedSite = applicationSettings.getLastUsedSearchSite(); |
||||
int lastUsedPosition = -1; |
||||
if (lastUsedSite != null) { |
||||
for (int i = 0; i < searchSites.size(); i++) { |
||||
if (searchSites.get(i).getKey().equals(lastUsedSite.getKey())) |
||||
lastUsedPosition = i; |
||||
} |
||||
} |
||||
|
||||
// Allow site selection via list (on large screens) or action bar spinner
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
||||
if (searchsitesList != null) { |
||||
// The current layout has a dedicated list view to select the search site
|
||||
SearchSitesAdapter searchSitesAdapter = SearchSitesAdapter_.getInstance_(this); |
||||
searchSitesAdapter.update(searchSites); |
||||
searchsitesList.setAdapter(searchSitesAdapter); |
||||
searchsitesList.setOnItemClickListener(onSearchSiteClicked); |
||||
// Select the last used site; this also starts the search!
|
||||
if (lastUsedPosition >= 0) |
||||
searchsitesList.setItemChecked(lastUsedPosition, true); |
||||
} else { |
||||
// Use the action bar spinner to select sites
|
||||
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); |
||||
getSupportActionBar().setDisplayShowTitleEnabled(false); |
||||
getSupportActionBar().setListNavigationCallbacks(new SearchSettingsDropDownAdapter(this, searchSites), this); |
||||
// Select the last used site; this also starts the search!
|
||||
if (lastUsedPosition >= 0) |
||||
getSupportActionBar().setSelectedNavigationItem(lastUsedPosition); |
||||
} |
||||
|
||||
} |
||||
|
||||
@TargetApi(Build.VERSION_CODES.FROYO) |
||||
@Override |
||||
public boolean onCreateOptionsMenu(Menu menu) { |
||||
super.onCreateOptionsMenu(menu); |
||||
if (navigationHelper.enableSearchUi()) { |
||||
// 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; |
||||
} |
||||
|
||||
@Override |
||||
public boolean onPrepareOptionsMenu(Menu menu) { |
||||
super.onPrepareOptionsMenu(menu); |
||||
|
||||
boolean searchInstalled = searchHelper.isTorrentSearchInstalled(); |
||||
menu.findItem(R.id.action_search).setVisible(searchInstalled); |
||||
menu.findItem(R.id.action_refresh).setVisible(searchInstalled); |
||||
menu.findItem(R.id.action_downloadsearch).setVisible(!searchInstalled); |
||||
if (searchsitesList != null) |
||||
searchsitesList.setVisibility(searchInstalled ? View.VISIBLE : View.GONE); |
||||
if (searchInstalled) |
||||
getSupportFragmentManager().beginTransaction().show(fragmentResults).commit(); |
||||
else |
||||
getSupportFragmentManager().beginTransaction().hide(fragmentResults).commit(); |
||||
installmoduleText.setVisibility(searchInstalled ? View.GONE : View.VISIBLE); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
protected void onNewIntent(Intent intent) { |
||||
handleIntent(intent); |
||||
refreshSearch(); |
||||
} |
||||
|
||||
private void handleIntent(Intent intent) { |
||||
lastUsedQuery = getQuery(intent); |
||||
getSupportActionBar().setTitle(NavigationHelper.buildCondensedFontString(lastUsedQuery)); |
||||
|
||||
// Is this actually a full HTTP URL? Then redirect this request to add the URL directly
|
||||
if (lastUsedQuery != null |
||||
&& (lastUsedQuery.startsWith("http") || lastUsedQuery.startsWith("https") |
||||
|| lastUsedQuery.startsWith("magnet") || lastUsedQuery.startsWith("file"))) { |
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(lastUsedQuery))); |
||||
finish(); |
||||
return; |
||||
} |
||||
|
||||
} |
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB) |
||||
@OptionsItem(android.R.id.home) |
||||
protected void navigateUp() { |
||||
TorrentsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start(); |
||||
} |
||||
|
||||
private OnItemClickListener onSearchSiteClicked = new OnItemClickListener() { |
||||
|
||||
@Override |
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
||||
lastUsedSite = searchSites.get(position); |
||||
refreshSearch(); |
||||
} |
||||
}; |
||||
|
||||
@Override |
||||
public boolean onNavigationItemSelected(int itemPosition, long itemId) { |
||||
lastUsedSite = searchSites.get(itemPosition); |
||||
refreshSearch(); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Extracts the query string from the search {@link Intent} |
||||
* @return The query string that was entered by the user |
||||
*/ |
||||
private String getQuery(Intent intent) { |
||||
|
||||
String query = null; |
||||
if (intent.getAction().equals(Intent.ACTION_SEARCH)) { |
||||
query = intent.getStringExtra(SearchManager.QUERY); |
||||
} else if (intent.getAction().equals(Intent.ACTION_SEND)) { |
||||
query = SendIntentHelper.cleanUpText(intent); |
||||
} |
||||
if (query != null && query.length() > 0) { |
||||
|
||||
// Remember this search query to later show as a suggestion
|
||||
suggestions.saveRecentQuery(query, null); |
||||
return query; |
||||
|
||||
} |
||||
return null; |
||||
|
||||
} |
||||
|
||||
@OptionsItem(resName = "action_refresh") |
||||
protected void refreshSearch() { |
||||
if (lastUsedSite instanceof WebsearchSetting) { |
||||
// Start a browser page directly to the requested search results
|
||||
WebsearchSetting websearch = (WebsearchSetting) lastUsedSite; |
||||
startActivity(new Intent(Intent.ACTION_VIEW, |
||||
Uri.parse(String.format(websearch.getBaseUrl(), lastUsedQuery)))); |
||||
} else if (lastUsedSite instanceof SearchSite) { |
||||
// Ask the resutls fragment to start a search for the specified query
|
||||
fragmentResults.startSearch(lastUsedQuery, (SearchSite) lastUsedSite); |
||||
} |
||||
} |
||||
|
||||
@OptionsItem(resName = "action_downloadsearch") |
||||
protected void downloadSearchModule() { |
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.transdroid.org/latest-search"))); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import org.androidannotations.annotations.EViewGroup; |
||||
import org.androidannotations.annotations.ViewById; |
||||
import org.transdroid.core.R; |
||||
import org.transdroid.core.app.search.SearchResult; |
||||
|
||||
import android.content.Context; |
||||
import android.text.format.DateUtils; |
||||
import android.widget.TextView; |
||||
import fr.marvinlabs.widget.CheckableRelativeLayout; |
||||
|
||||
/** |
||||
* View that represents a {@link SearchResult} object from an in-app search |
||||
* @author Eric Kok |
||||
*/ |
||||
@EViewGroup(resName = "list_item_searchresult") |
||||
public class SearchResultView extends CheckableRelativeLayout { |
||||
|
||||
// Views
|
||||
@ViewById |
||||
protected TextView nameText, seedersText, leechersText, sizeText, dateText; |
||||
|
||||
public SearchResultView(Context context) { |
||||
super(context); |
||||
} |
||||
|
||||
public void bind(SearchResult result) { |
||||
|
||||
nameText.setText(result.getName()); |
||||
sizeText.setText(result.getSize()); |
||||
dateText.setText(result.getAddedOn() == null ? "" : DateUtils.getRelativeDateTimeString(getContext(), result |
||||
.getAddedOn().getTime(), DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, |
||||
DateUtils.FORMAT_ABBREV_MONTH)); |
||||
seedersText.setText(getContext().getString(R.string.search_seeders, result.getSeeders())); |
||||
leechersText.setText(getContext().getString(R.string.search_leechers, result.getLeechers())); |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.androidannotations.annotations.EBean; |
||||
import org.androidannotations.annotations.RootContext; |
||||
import org.transdroid.core.app.search.SearchResult; |
||||
|
||||
import android.content.Context; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
import android.widget.BaseAdapter; |
||||
|
||||
/** |
||||
* Adapter that contains a list of {@link SearchResult}s. |
||||
* @author Eric Kok |
||||
*/ |
||||
@EBean |
||||
public class SearchResultsAdapter extends BaseAdapter { |
||||
|
||||
private List<SearchResult> results = null; |
||||
|
||||
@RootContext |
||||
protected Context context; |
||||
|
||||
/** |
||||
* Allows updating the search results, replacing the old data |
||||
* @param newRssfeeds The new list of search results |
||||
*/ |
||||
public void update(List<SearchResult> results) { |
||||
this.results = results; |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean hasStableIds() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public int getCount() { |
||||
if (results == null) |
||||
return 0; |
||||
return results.size(); |
||||
} |
||||
|
||||
@Override |
||||
public SearchResult getItem(int position) { |
||||
if (results == null) |
||||
return null; |
||||
return results.get(position); |
||||
} |
||||
|
||||
@Override |
||||
public long getItemId(int position) { |
||||
return position; |
||||
} |
||||
|
||||
@Override |
||||
public View getView(int position, View convertView, ViewGroup parent) { |
||||
SearchResultView rssitemView; |
||||
if (convertView == null) { |
||||
rssitemView = SearchResultView_.build(context); |
||||
} else { |
||||
rssitemView = (SearchResultView) convertView; |
||||
} |
||||
rssitemView.bind(getItem(position)); |
||||
return rssitemView; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,168 @@
@@ -0,0 +1,168 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
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.EFragment; |
||||
import org.androidannotations.annotations.InstanceState; |
||||
import org.androidannotations.annotations.ItemClick; |
||||
import org.androidannotations.annotations.UiThread; |
||||
import org.androidannotations.annotations.ViewById; |
||||
import org.transdroid.core.R; |
||||
import org.transdroid.core.app.search.SearchHelper; |
||||
import org.transdroid.core.app.search.SearchHelper.SearchSortOrder; |
||||
import org.transdroid.core.app.search.SearchResult; |
||||
import org.transdroid.core.app.search.SearchSite; |
||||
import org.transdroid.core.gui.navigation.SelectionManagerMode; |
||||
|
||||
import android.content.Intent; |
||||
import android.net.Uri; |
||||
import android.view.View; |
||||
import android.widget.ProgressBar; |
||||
import android.widget.TextView; |
||||
import android.widget.Toast; |
||||
|
||||
import com.actionbarsherlock.app.SherlockFragment; |
||||
import com.actionbarsherlock.view.ActionMode; |
||||
import com.actionbarsherlock.view.Menu; |
||||
import com.actionbarsherlock.view.MenuItem; |
||||
import com.actionbarsherlock.view.SherlockListView; |
||||
import com.actionbarsherlock.view.SherlockListView.MultiChoiceModeListenerCompat; |
||||
|
||||
/** |
||||
* Fragment that lists the items in a specific RSS feed |
||||
* @author Eric Kok |
||||
*/ |
||||
@EFragment(resName = "fragment_searchresults") |
||||
public class SearchResultsFragment extends SherlockFragment { |
||||
|
||||
@InstanceState |
||||
protected ArrayList<SearchResult> results = null; |
||||
@Bean |
||||
protected SearchHelper searchHelper; |
||||
|
||||
// Views
|
||||
@ViewById(resName = "searchresults_list") |
||||
protected SherlockListView resultsList; |
||||
@Bean |
||||
protected SearchResultsAdapter resultsAdapter; |
||||
@ViewById |
||||
protected TextView emptyText; |
||||
@ViewById |
||||
protected ProgressBar loadingProgress; |
||||
|
||||
@AfterViews |
||||
protected void init() { |
||||
|
||||
// Set up the list adapter, which allows multi-select
|
||||
resultsList.setAdapter(resultsAdapter); |
||||
resultsList.setMultiChoiceModeListener(onItemsSelected); |
||||
if (results != null) |
||||
showResults(); |
||||
|
||||
} |
||||
|
||||
public void startSearch(String query, SearchSite site) { |
||||
loadingProgress.setVisibility(View.VISIBLE); |
||||
resultsList.setVisibility(View.GONE); |
||||
emptyText.setVisibility(View.GONE); |
||||
performSearch(query, site); |
||||
} |
||||
|
||||
@Background |
||||
protected void performSearch(String query, SearchSite site) { |
||||
results = searchHelper.search(query, site, SearchSortOrder.BySeeders); |
||||
showResults(); |
||||
} |
||||
|
||||
@UiThread |
||||
protected void showResults() { |
||||
loadingProgress.setVisibility(View.GONE); |
||||
if (results == null || results.size() == 0) { |
||||
resultsList.setVisibility(View.GONE); |
||||
emptyText.setVisibility(View.VISIBLE); |
||||
return; |
||||
} |
||||
resultsAdapter.update(results); |
||||
resultsList.setVisibility(View.VISIBLE); |
||||
emptyText.setVisibility(View.GONE); |
||||
} |
||||
|
||||
@ItemClick(resName = "searchresults_list") |
||||
protected void onItemClicked(SearchResult item) { |
||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(item.getTorrentUrl())); |
||||
i.putExtra("TORRENT_TITLE", item.getName()); |
||||
startActivity(i); |
||||
} |
||||
|
||||
private MultiChoiceModeListenerCompat onItemsSelected = new MultiChoiceModeListenerCompat() { |
||||
|
||||
SelectionManagerMode selectionManagerMode; |
||||
|
||||
@Override |
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) { |
||||
// Show contextual action bar to add items in batch mode
|
||||
mode.getMenuInflater().inflate(R.menu.fragment_searchresults_cab, menu); |
||||
selectionManagerMode = new SelectionManagerMode(resultsList, R.plurals.search_resutlsselected); |
||||
selectionManagerMode.onCreateActionMode(mode, menu); |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { |
||||
return selectionManagerMode.onPrepareActionMode(mode, menu); |
||||
} |
||||
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) { |
||||
|
||||
// Get checked torrents
|
||||
List<SearchResult> checked = new ArrayList<SearchResult>(); |
||||
for (int i = 0; i < resultsList.getCheckedItemPositions().size(); i++) { |
||||
if (resultsList.getCheckedItemPositions().valueAt(i)) |
||||
checked.add(resultsAdapter.getItem(resultsList.getCheckedItemPositions().keyAt(i))); |
||||
} |
||||
|
||||
int itemId = item.getItemId(); |
||||
if (itemId == R.id.action_addall) { |
||||
// Start an Intent that adds multiple items at once, by supplying the urls and titles as string array
|
||||
// extras and setting the Intent action to ADD_MULTIPLE
|
||||
Intent intent = new Intent("org.transdroid.ADD_MULTIPLE"); |
||||
String[] urls = new String[checked.size()]; |
||||
String[] titles = new String[checked.size()]; |
||||
for (int i = 0; i < checked.size(); i++) { |
||||
urls[i] = checked.get(i).getTorrentUrl(); |
||||
titles[i] = checked.get(i).getName(); |
||||
} |
||||
intent.putExtra("TORRENT_URLS", urls); |
||||
intent.putExtra("TORRENT_TITLES", titles); |
||||
startActivity(intent); |
||||
mode.finish(); |
||||
return true; |
||||
} else if (itemId == R.id.action_showdetails) { |
||||
SearchResult first = checked.get(0); |
||||
// Open the torrent's web page in the browser
|
||||
Toast.makeText(getActivity(), getString(R.string.search_openingdetails, first), Toast.LENGTH_LONG) |
||||
.show(); |
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(first.getDetailsUrl()))); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { |
||||
selectionManagerMode.onItemCheckedStateChanged(mode, position, id, checked); |
||||
} |
||||
|
||||
@Override |
||||
public void onDestroyActionMode(ActionMode mode) { |
||||
selectionManagerMode.onDestroyActionMode(mode); |
||||
} |
||||
|
||||
}; |
||||
|
||||
} |
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import org.transdroid.core.gui.lists.SimpleListItem; |
||||
|
||||
public interface SearchSetting extends SimpleListItem { |
||||
|
||||
/** |
||||
* Should return a unique key for this search setting, so that it can be compared (using equals()) to other settings. |
||||
* @return A unique string identifying this search setting |
||||
*/ |
||||
public String getKey(); |
||||
|
||||
/** |
||||
* Should return an URL (which may still be abstract and not the actual search URL) specific to the search site |
||||
* @return A clean URL directing to the search site, to, for example, get the favicon of the site |
||||
*/ |
||||
public String getBaseUrl(); |
||||
|
||||
} |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import org.androidannotations.annotations.EViewGroup; |
||||
import org.androidannotations.annotations.ViewById; |
||||
|
||||
import android.content.Context; |
||||
import android.widget.FrameLayout; |
||||
import android.widget.TextView; |
||||
|
||||
/** |
||||
* View that shows, as part of the action bar spinner, which {@link SearchSetting} is currently chosen. |
||||
* @author Eric Kok |
||||
*/ |
||||
@EViewGroup(resName = "actionbar_searchsite") |
||||
public class SearchSettingSelectionView extends FrameLayout { |
||||
|
||||
@ViewById |
||||
protected TextView searchsiteText; |
||||
|
||||
public SearchSettingSelectionView(Context context) { |
||||
super(context); |
||||
} |
||||
|
||||
public void bind(SearchSetting searchSettingItem) { |
||||
searchsiteText.setText(searchSettingItem.getName()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.transdroid.core.gui.lists.SimpleListItem; |
||||
import org.transdroid.core.gui.navigation.FilterListItemAdapter; |
||||
|
||||
import android.content.Context; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
|
||||
/** |
||||
* List adapter that holds search settings, that is, web searches and in-app search sites, displayed as content to a |
||||
* Spinner instead of a ListView. |
||||
* @author Eric Kok |
||||
*/ |
||||
public class SearchSettingsDropDownAdapter extends FilterListItemAdapter { |
||||
|
||||
private final Context context; |
||||
protected SearchSettingSelectionView searchSettingView = null; |
||||
|
||||
public SearchSettingsDropDownAdapter(Context context, List<? extends SimpleListItem> items) { |
||||
super(context, items); |
||||
this.context = context; |
||||
} |
||||
|
||||
@Override |
||||
public View getView(int position, View convertView, ViewGroup parent) { |
||||
// This returns the item to show in the action bar spinner
|
||||
if (searchSettingView == null) { |
||||
searchSettingView = SearchSettingSelectionView_.build(context); |
||||
} |
||||
searchSettingView.bind((SearchSetting) getItem(position)); |
||||
return searchSettingView; |
||||
} |
||||
|
||||
@Override |
||||
public View getDropDownView(int position, View convertView, ViewGroup parent) { |
||||
// This returns the item to show in the drop down list
|
||||
return super.getView(position, convertView, parent); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import org.androidannotations.annotations.Bean; |
||||
import org.androidannotations.annotations.EViewGroup; |
||||
import org.androidannotations.annotations.ViewById; |
||||
import org.transdroid.core.app.settings.RssfeedSetting; |
||||
import org.transdroid.core.gui.navigation.NavigationHelper; |
||||
|
||||
import android.content.Context; |
||||
import android.widget.ImageView; |
||||
import android.widget.LinearLayout; |
||||
import android.widget.TextView; |
||||
|
||||
/** |
||||
* View that represents some {@link RssfeedSetting} object and displays name as well as loads a favicon for the feed's |
||||
* site and can load how many new items are available. |
||||
* @author Eric Kok |
||||
*/ |
||||
@EViewGroup(resName = "list_item_searchsite") |
||||
public class SearchSiteView extends LinearLayout { |
||||
|
||||
private static final String GETFVO_URL = "http://g.etfv.co/%1$s"; |
||||
|
||||
@Bean |
||||
protected NavigationHelper navigationHelper; |
||||
|
||||
// Views
|
||||
@ViewById |
||||
protected ImageView faviconImage; |
||||
@ViewById |
||||
protected TextView nameText; |
||||
|
||||
public SearchSiteView(Context context) { |
||||
super(context); |
||||
} |
||||
|
||||
public void bind(SearchSetting rssfeedLoader) { |
||||
|
||||
// Show the RSS feed name and either a loading indicator or the number of new items
|
||||
nameText.setText(rssfeedLoader.getName()); |
||||
// Clear and then asynchronously load the site's favicon
|
||||
// Uses the g.etfv.co service to resolve the favicon of any URL
|
||||
faviconImage.setImageDrawable(null); |
||||
navigationHelper.getImageCache().displayImage(String.format(GETFVO_URL, rssfeedLoader.getBaseUrl()), |
||||
faviconImage); |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,72 @@
@@ -0,0 +1,72 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.androidannotations.annotations.EBean; |
||||
import org.androidannotations.annotations.RootContext; |
||||
import org.transdroid.core.app.search.SearchSite; |
||||
import org.transdroid.core.app.settings.WebsearchSetting; |
||||
|
||||
import android.content.Context; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
import android.widget.BaseAdapter; |
||||
|
||||
/** |
||||
* Adapter that contains a list of {@link SearchSetting}s, either {@link SearchSite} or {@link WebsearchSetting}. |
||||
* @author Eric Kok |
||||
*/ |
||||
@EBean |
||||
public class SearchSitesAdapter extends BaseAdapter { |
||||
|
||||
private List<SearchSetting> sites = null; |
||||
|
||||
@RootContext |
||||
protected Context context; |
||||
|
||||
/** |
||||
* Allows updating the full internal list of sites at once, replacing the old list |
||||
* @param sites The new list of search sites, either in-app or web search settings |
||||
*/ |
||||
public void update(List<SearchSetting> sites) { |
||||
this.sites = sites; |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean hasStableIds() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public int getCount() { |
||||
if (sites == null) |
||||
return 0; |
||||
return sites.size(); |
||||
} |
||||
|
||||
@Override |
||||
public SearchSetting getItem(int position) { |
||||
if (sites == null) |
||||
return null; |
||||
return sites.get(position); |
||||
} |
||||
|
||||
@Override |
||||
public long getItemId(int position) { |
||||
return position; |
||||
} |
||||
|
||||
@Override |
||||
public View getView(int position, View convertView, ViewGroup parent) { |
||||
SearchSiteView rssfeedView; |
||||
if (convertView == null) { |
||||
rssfeedView = SearchSiteView_.build(context); |
||||
} else { |
||||
rssfeedView = (SearchSiteView) convertView; |
||||
} |
||||
rssfeedView.bind(getItem(position)); |
||||
return rssfeedView; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import android.content.Intent; |
||||
|
||||
/** |
||||
* Used to clean up text as received from a generic ACTION_SEND intent. This class is highly custom-based for known |
||||
* applications, i.e. the EXTRA_TEXT send by some known applications. |
||||
* @author Eric Kok |
||||
*/ |
||||
public class SendIntentHelper { |
||||
|
||||
private static final String SOUNDHOUND1 = "Just used #SoundHound to find "; |
||||
private static final String SOUNDHOUND1_END = " http://"; |
||||
private static final String SHAZAM = "I just used Shazam to discover "; |
||||
private static final String SHAZAM_END = ". http://"; |
||||
private static final String YOUTUBE_ID = "Watch \""; |
||||
private static final String YOUTUBE_START = "\""; |
||||
private static final String YOUTUBE_END = "\""; |
||||
|
||||
/** |
||||
* Cleans a SEND intent text string by removing irrelevant parts, so that the remaining text can be used as search |
||||
* string. Typically deals with specific known applications such as Shazam and YouTube's SEND intents. |
||||
* @param intent The original SEND intent that was received |
||||
* @return A cleaned string to be used as search query |
||||
*/ |
||||
public static String cleanUpText(Intent intent) { |
||||
|
||||
if (intent == null || !intent.hasExtra(Intent.EXTRA_TEXT)) { |
||||
return null; |
||||
} |
||||
String text = intent.getStringExtra(Intent.EXTRA_TEXT); |
||||
try { |
||||
|
||||
// Soundhound song/artist share
|
||||
if (text.startsWith(SOUNDHOUND1)) { |
||||
return cutOut(text, SOUNDHOUND1, SOUNDHOUND1_END).replace(" by ", " "); |
||||
} |
||||
// Shazam song share
|
||||
if (text.startsWith(SHAZAM)) { |
||||
return cutOut(text, SHAZAM, SHAZAM_END).replace(" by ", " "); |
||||
} |
||||
// YouTube app share (stores title in EXTRA_SUBJECT)
|
||||
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) { |
||||
String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT); |
||||
if (subject.startsWith(YOUTUBE_ID)) { |
||||
return cutOut(subject, YOUTUBE_START, YOUTUBE_END); |
||||
} |
||||
} |
||||
|
||||
} catch (Exception e) { |
||||
// Ignore any errors in parsing; just return the raw text
|
||||
} |
||||
return text; |
||||
} |
||||
|
||||
private static String cutOut(String text, String start, String end) { |
||||
int startAt = text.indexOf(start) + start.length(); |
||||
return text.substring(startAt, text.indexOf(end, startAt)); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
package org.transdroid.core.gui.search; |
||||
|
||||
import android.content.Context; |
||||
import android.content.SearchRecentSuggestionsProvider; |
||||
import android.provider.SearchRecentSuggestions; |
||||
|
||||
/** |
||||
* Provides a wrapper for the {@link SearchRecentSuggestionsProvider} to show the last torrent searches to the user. |
||||
* @author Eric Kok |
||||
*/ |
||||
public class TorrentSearchHistoryProvider extends SearchRecentSuggestionsProvider { |
||||
|
||||
public static final String AUTHORITY = "org.transdroid.core.gui.search.TorrentSearchHistoryProvider"; |
||||
public static final int MODE = DATABASE_MODE_QUERIES; |
||||
|
||||
public TorrentSearchHistoryProvider() { |
||||
super(); |
||||
setupSuggestions(AUTHORITY, MODE); |
||||
} |
||||
|
||||
public static void clearHistory(Context context) { |
||||
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(context, |
||||
TorrentSearchHistoryProvider.AUTHORITY, TorrentSearchHistoryProvider.MODE); |
||||
suggestions.clearHistory(); |
||||
} |
||||
} |
Loading…
Reference in new issue