From d287dca85423931fb0c486879d831e06fafd8f3e Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Thu, 24 Apr 2014 16:48:50 +0930 Subject: [PATCH] Refactored SearchView into Activity + ListFragment (Fixes #11) This allowed for the use of LoaderCallbacks which seem like a better way at managing the lifecycle of the cursors which our ContentProviders return. --- src/org/fdroid/fdroid/SearchResults.java | 118 +++------------- .../fragments/SearchResultsFragment.java | 133 ++++++++++++++++++ 2 files changed, 156 insertions(+), 95 deletions(-) create mode 100644 src/org/fdroid/fdroid/views/fragments/SearchResultsFragment.java diff --git a/src/org/fdroid/fdroid/SearchResults.java b/src/org/fdroid/fdroid/SearchResults.java index 8f62100ba..53f475692 100644 --- a/src/org/fdroid/fdroid/SearchResults.java +++ b/src/org/fdroid/fdroid/SearchResults.java @@ -18,56 +18,22 @@ package org.fdroid.fdroid; -import android.app.ListActivity; -import android.app.SearchManager; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.ListView; -import android.widget.TextView; - +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; import android.support.v4.app.NavUtils; import android.support.v4.view.MenuItemCompat; - +import android.view.Menu; +import android.view.MenuItem; +import android.widget.LinearLayout; import org.fdroid.fdroid.compat.ActionBarCompat; -import org.fdroid.fdroid.data.App; -import org.fdroid.fdroid.data.AppProvider; -import org.fdroid.fdroid.views.AppListAdapter; -import org.fdroid.fdroid.views.AvailableAppListAdapter; -import org.fdroid.fdroid.views.fragments.AppListFragment; +import org.fdroid.fdroid.views.fragments.SearchResultsFragment; -public class SearchResults extends ListActivity { - - private static final int REQUEST_APPDETAILS = 0; +public class SearchResults extends FragmentActivity { private static final int SEARCH = Menu.FIRST; - private Cursor cursor; - private AppListAdapter adapter; - - protected String getQuery() { - Intent intent = getIntent(); - String query; - if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - query = intent.getStringExtra(SearchManager.QUERY); - } else { - Uri data = intent.getData(); - if (data != null && data.isHierarchical()) { - query = data.getQueryParameter("q"); - if (query != null && query.startsWith("pname:")) - query = query.substring(6); - } else { - query = data.getEncodedSchemeSpecificPart(); - } - } - return query; - } - @Override public void onCreate(Bundle savedInstanceState) { @@ -75,21 +41,28 @@ public class SearchResults extends ListActivity { super.onCreate(savedInstanceState); - setContentView(R.layout.searchresults); + // Start a search by just typing + setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); + + FragmentManager fm = getSupportFragmentManager(); + if (fm.findFragmentById(android.R.id.content) == null) { + + // Need to set a dummy view (which will get overridden by the fragment manager + // below) so that we can call setContentView(). This is a work around for + // a (bug?) thing in 3.0, 3.1 which requires setContentView to be invoked before + // the actionbar is played with: + // http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html + setContentView( new LinearLayout(this) ); + + SearchResultsFragment fragment = new SearchResultsFragment(); + fm.beginTransaction().add(android.R.id.content, fragment).commit(); + } // Actionbar cannot be accessed until after setContentView (on 3.0 and 3.1 devices) // see: http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html // for reason why. ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); - // Start a search by just typing - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - } - - @Override - protected void onResume() { - super.onResume(); - updateView(); } @Override @@ -98,51 +71,6 @@ public class SearchResults extends ListActivity { setIntent(intent); } - private void updateView() { - - String query = getQuery(); - - if (query != null) - query = query.trim(); - - if (query == null || query.length() == 0) - finish(); - - if (cursor != null) cursor.close(); - cursor = managedQuery( - AppProvider.getSearchUri(query), AppListFragment.APP_PROJECTION, - null, null, AppListFragment.APP_SORT); - - - TextView tv = (TextView) findViewById(R.id.description); - String headertext; - int count = cursor != null ? cursor.getCount() : 0; - if (count == 0) { - headertext = getString(R.string.searchres_noapps, query); - } else if (count == 1) { - headertext = getString(R.string.searchres_oneapp, query); - } else { - headertext = getString(R.string.searchres_napps, count, query); - } - tv.setText(headertext); - Log.d("FDroid", "Search for '" + query + "' returned " + count + " results"); - - adapter = new AvailableAppListAdapter(this, cursor); - getListView().setFastScrollEnabled(true); - setListAdapter(adapter); - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - final App app; - app = new App((Cursor) adapter.getItem(position)); - - Intent intent = new Intent(this, AppDetails.class); - intent.putExtra(AppDetails.EXTRA_APPID, app.id); - startActivityForResult(intent, REQUEST_APPDETAILS); - super.onListItemClick(l, v, position, id); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { diff --git a/src/org/fdroid/fdroid/views/fragments/SearchResultsFragment.java b/src/org/fdroid/fdroid/views/fragments/SearchResultsFragment.java new file mode 100644 index 000000000..a2f017be8 --- /dev/null +++ b/src/org/fdroid/fdroid/views/fragments/SearchResultsFragment.java @@ -0,0 +1,133 @@ +package org.fdroid.fdroid.views.fragments; + +import android.app.SearchManager; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; +import android.widget.TextView; +import org.fdroid.fdroid.AppDetails; +import org.fdroid.fdroid.R; +import org.fdroid.fdroid.data.App; +import org.fdroid.fdroid.data.AppProvider; +import org.fdroid.fdroid.views.AppListAdapter; +import org.fdroid.fdroid.views.AvailableAppListAdapter; + +public class SearchResultsFragment extends ListFragment implements LoaderManager.LoaderCallbacks { + + private static final String TAG = "org.fdroid.fdroid.views.fragments.SearchResultsFragment"; + + private static final int REQUEST_APPDETAILS = 0; + + private AppListAdapter adapter; + + protected String getQuery() { + Intent intent = getActivity().getIntent(); + String query = null; + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + query = intent.getStringExtra(SearchManager.QUERY); + } else { + Uri data = intent.getData(); + if (data != null && data.isHierarchical()) { + query = data.getQueryParameter("q"); + if (query != null && query.startsWith("pname:")) + query = query.substring(6); + } else if (data!= null ) { + query = data.getEncodedSchemeSpecificPart(); + } + } + return query == null ? "" : query; + } + + @Override + public void onResume() { + super.onResume(); + + //Starts a new or restarts an existing Loader in this manager + getLoaderManager().restartLoader(0, null, this); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup root, Bundle data) { + + adapter = new AvailableAppListAdapter(getActivity(), null); + setListAdapter(adapter); + + View view = inflater.inflate(R.layout.searchresults, null); + updateSummary(view); + + return view; + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + Uri uri = AppProvider.getSearchUri(getQuery()); + return new CursorLoader( + getActivity(), + uri, + AppListFragment.APP_PROJECTION, + null, + null, + AppListFragment.APP_SORT + ); + } + + private void updateSummary() { + updateSummary(getView()); + } + + private void updateSummary(View view) { + + String query = getQuery(); + + if (query != null) + query = query.trim(); + + if (query == null || query.length() == 0) + getActivity().finish(); + + TextView tv = (TextView) view.findViewById(R.id.description); + String headerText; + int count = adapter.getCount(); + if (count == 0) { + headerText = getString(R.string.searchres_noapps, query); + } else if (count == 1) { + headerText = getString(R.string.searchres_oneapp, query); + } else { + headerText = getString(R.string.searchres_napps, count, query); + } + tv.setText(headerText); + Log.d(TAG, "Search for '" + query + "' returned " + count + " results"); + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + final App app; + app = new App((Cursor) adapter.getItem(position)); + + Intent intent = new Intent(getActivity(), AppDetails.class); + intent.putExtra(AppDetails.EXTRA_APPID, app.id); + startActivityForResult(intent, REQUEST_APPDETAILS); + super.onListItemClick(l, v, position, id); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + adapter.swapCursor(data); + updateSummary(); + } + + @Override + public void onLoaderReset(Loader loader) { + adapter.swapCursor(null); + } +}