summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlinus_lee <llee@cyngn.com>2014-09-22 18:38:29 -0700
committerlinus_lee <llee@cyngn.com>2014-11-20 12:03:04 -0800
commit78603d13a50f79b91ae9b9c945314a6a79ed4a04 (patch)
tree794df6dae1220403ddee5eb223a9238457bfa149
parent0e486adaf71cec5e75217a32a281f2eb6c799ba5 (diff)
downloadandroid_packages_apps_Eleven-78603d13a50f79b91ae9b9c945314a6a79ed4a04.tar.gz
android_packages_apps_Eleven-78603d13a50f79b91ae9b9c945314a6a79ed4a04.tar.bz2
android_packages_apps_Eleven-78603d13a50f79b91ae9b9c945314a6a79ed4a04.zip
Eleven: Add search history implementation. Fix padding and a small bug in Top tracks
Padding layouts were wrong in some layouts Fixed a bug in top tracks where we were looking at id and not loading time https://cyanogen.atlassian.net/browse/MUSIC-39 Change-Id: Ie304a9a7cc07770a2b699310b0ecbe94142863cf
-rw-r--r--res/drawable-hdpi/history_icon.pngbin0 -> 481 bytes
-rw-r--r--res/drawable-mdpi/history_icon.pngbin0 -> 360 bytes
-rw-r--r--res/drawable-xhdpi/history_icon.pngbin0 -> 575 bytes
-rw-r--r--res/drawable-xxhdpi/history_icon.pngbin0 -> 860 bytes
-rw-r--r--res/layout/activity_search.xml21
-rw-r--r--res/layout/list_item_common.xml3
-rw-r--r--res/layout/list_item_queue.xml3
-rw-r--r--res/layout/list_item_search_history.xml31
-rw-r--r--res/layout/list_item_top_tracks.xml2
-rw-r--r--res/values/dimens.xml1
-rw-r--r--src/com/cyngn/eleven/provider/MusicDB.java2
-rw-r--r--src/com/cyngn/eleven/provider/RecentStore.java2
-rw-r--r--src/com/cyngn/eleven/provider/SearchHistory.java155
-rw-r--r--src/com/cyngn/eleven/ui/activities/SearchActivity.java269
14 files changed, 447 insertions, 42 deletions
diff --git a/res/drawable-hdpi/history_icon.png b/res/drawable-hdpi/history_icon.png
new file mode 100644
index 0000000..ec76e2c
--- /dev/null
+++ b/res/drawable-hdpi/history_icon.png
Binary files differ
diff --git a/res/drawable-mdpi/history_icon.png b/res/drawable-mdpi/history_icon.png
new file mode 100644
index 0000000..808c72e
--- /dev/null
+++ b/res/drawable-mdpi/history_icon.png
Binary files differ
diff --git a/res/drawable-xhdpi/history_icon.png b/res/drawable-xhdpi/history_icon.png
new file mode 100644
index 0000000..700b172
--- /dev/null
+++ b/res/drawable-xhdpi/history_icon.png
Binary files differ
diff --git a/res/drawable-xxhdpi/history_icon.png b/res/drawable-xxhdpi/history_icon.png
new file mode 100644
index 0000000..6fef335
--- /dev/null
+++ b/res/drawable-xxhdpi/history_icon.png
Binary files differ
diff --git a/res/layout/activity_search.xml b/res/layout/activity_search.xml
new file mode 100644
index 0000000..5cd479c
--- /dev/null
+++ b/res/layout/activity_search.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 Cyanogen, Inc.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ListView
+ android:id="@+id/list_search_history"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:cacheColorHint="@color/transparent"
+ android:drawSelectorOnTop="false"
+ android:fastScrollEnabled="true"
+ android:dividerHeight="@dimen/divider_height"
+ android:divider="@drawable/inset_list_divider"/>
+
+ <include
+ layout="@layout/list_base" />
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout/list_item_common.xml b/res/layout/list_item_common.xml
index 6f025ce..4ba3914 100644
--- a/res/layout/list_item_common.xml
+++ b/res/layout/list_item_common.xml
@@ -3,8 +3,7 @@
android:id="@+id/image"
android:layout_width="@dimen/list_item_image_width"
android:layout_height="@dimen/list_item_image_height"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
+ android:layout_centerVertical="true"
android:scaleType="centerCrop" />
<RelativeLayout
diff --git a/res/layout/list_item_queue.xml b/res/layout/list_item_queue.xml
index 9cf1fc5..ddbc66f 100644
--- a/res/layout/list_item_queue.xml
+++ b/res/layout/list_item_queue.xml
@@ -13,8 +13,7 @@
android:id="@+id/image"
android:layout_width="@dimen/list_item_image_width"
android:layout_height="@dimen/list_item_image_height"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
+ android:layout_centerVertical="true"
android:scaleType="centerCrop" />
<!-- center the text views vertically -->
diff --git a/res/layout/list_item_search_history.xml b/res/layout/list_item_search_history.xml
new file mode 100644
index 0000000..2bb520e
--- /dev/null
+++ b/res/layout/list_item_search_history.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 Cyanogen, Inc.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/item_short_height"
+ android:gravity="center_vertical"
+ android:minHeight="@dimen/item_short_height"
+ android:paddingBottom="@dimen/list_item_padding_bottom"
+ android:paddingTop="@dimen/list_item_padding_top"
+ android:paddingLeft="@dimen/list_preferred_item_padding"
+ android:paddingRight="@dimen/list_preferred_item_padding">
+
+ <com.cyngn.eleven.widgets.SquareImageView
+ android:id="@+id/image"
+ android:layout_width="@dimen/list_item_image_width"
+ android:layout_height="@dimen/list_item_image_height"
+ android:layout_centerVertical="true"
+ android:scaleType="centerCrop"
+ android:src="@drawable/history_icon" />
+
+ <TextView
+ android:id="@+id/line_one"
+ style="@style/ListItemMainText.Single"
+ android:gravity="center_vertical"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@id/image"
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent" />
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/list_item_top_tracks.xml b/res/layout/list_item_top_tracks.xml
index 2b4a795..70fe32f 100644
--- a/res/layout/list_item_top_tracks.xml
+++ b/res/layout/list_item_top_tracks.xml
@@ -22,6 +22,8 @@
android:minHeight="@dimen/item_normal_height"
android:paddingTop="@dimen/list_item_padding_top"
android:paddingBottom="@dimen/list_item_padding_bottom"
+ android:paddingLeft="@dimen/list_preferred_item_padding"
+ android:paddingRight="@dimen/list_preferred_item_padding"
tools:ignore="ContentDescription" >
<FrameLayout
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 06189b5..5e4be37 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -61,6 +61,7 @@
<dimen name="grid_bottom_height">40dip</dimen>
<!-- Grid and list item normal height -->
<dimen name="item_normal_height">70.0dip</dimen>
+ <dimen name="item_short_height">60.0dip</dimen>
<dimen name="list_item_image_height">50.0dip</dimen>
<dimen name="list_item_image_width">50.0dip</dimen>
<dimen name="list_item_top_track_image_size">26.0dip</dimen>
diff --git a/src/com/cyngn/eleven/provider/MusicDB.java b/src/com/cyngn/eleven/provider/MusicDB.java
index d1cc348..794b661 100644
--- a/src/com/cyngn/eleven/provider/MusicDB.java
+++ b/src/com/cyngn/eleven/provider/MusicDB.java
@@ -41,6 +41,7 @@ public class MusicDB extends SQLiteOpenHelper {
PlaylistArtworkStore.getInstance(mContext).onCreate(db);
RecentStore.getInstance(mContext).onCreate(db);
SongPlayCount.getInstance(mContext).onCreate(db);
+ SearchHistory.getInstance(mContext).onCreate(db);
}
@Override
@@ -48,5 +49,6 @@ public class MusicDB extends SQLiteOpenHelper {
PlaylistArtworkStore.getInstance(mContext).onUpgrade(db, oldVersion, newVersion);
RecentStore.getInstance(mContext).onUpgrade(db, oldVersion, newVersion);
SongPlayCount.getInstance(mContext).onUpgrade(db, oldVersion, newVersion);
+ SearchHistory.getInstance(mContext).onUpgrade(db, oldVersion, newVersion);
}
}
diff --git a/src/com/cyngn/eleven/provider/RecentStore.java b/src/com/cyngn/eleven/provider/RecentStore.java
index b8ffbb4..285d53e 100644
--- a/src/com/cyngn/eleven/provider/RecentStore.java
+++ b/src/com/cyngn/eleven/provider/RecentStore.java
@@ -91,7 +91,7 @@ public class RecentStore {
Cursor oldest = null;
try {
database.query(RecentStoreColumns.NAME,
- new String[]{RecentStoreColumns.ID}, null, null, null, null,
+ new String[]{RecentStoreColumns.TIMEPLAYED}, null, null, null, null,
RecentStoreColumns.TIMEPLAYED + " ASC");
if (oldest != null && oldest.getCount() > MAX_ITEMS_IN_DB) {
diff --git a/src/com/cyngn/eleven/provider/SearchHistory.java b/src/com/cyngn/eleven/provider/SearchHistory.java
new file mode 100644
index 0000000..3f02c65
--- /dev/null
+++ b/src/com/cyngn/eleven/provider/SearchHistory.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014 Cyanogen, Inc.
+ */
+package com.cyngn.eleven.provider;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import java.util.ArrayList;
+
+public class SearchHistory {
+ /* Maximum # of items in the db */
+ private static final int MAX_ITEMS_IN_DB = 25;
+
+ private static SearchHistory sInstance = null;
+
+ private MusicDB mMusicDatabase = null;
+
+ public SearchHistory(final Context context) {
+ mMusicDatabase = MusicDB.getInstance(context);
+ }
+
+ public void onCreate(final SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE IF NOT EXISTS " + SearchHistoryColumns.NAME + " ("
+ + SearchHistoryColumns.SEARCHSTRING + " STRING NOT NULL,"
+ + SearchHistoryColumns.TIMESEARCHED + " LONG NOT NULL);");
+ }
+
+ public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
+ db.execSQL("DROP TABLE IF EXISTS " + SearchHistoryColumns.NAME);
+ onCreate(db);
+ }
+
+ /**
+ * @param context The {@link android.content.Context} to use
+ * @return A new instance of this class.
+ */
+ public static final synchronized SearchHistory getInstance(final Context context) {
+ if (sInstance == null) {
+ sInstance = new SearchHistory(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ /**
+ * Used to save a search request into the db. This function will remove any old
+ * searches for that string before inserting it into the db
+ * @param searchString The string that has been searched
+ */
+ public void addSearchString(final String searchString) {
+ if (searchString == null) {
+ return;
+ }
+
+ String trimmedString = searchString.trim();
+
+ if (trimmedString.isEmpty()) {
+ return;
+ }
+
+ final SQLiteDatabase database = mMusicDatabase.getWritableDatabase();
+ database.beginTransaction();
+
+ try {
+ // delete existing searches with the same search string
+ database.delete(SearchHistoryColumns.NAME,
+ SearchHistoryColumns.SEARCHSTRING + " = ? COLLATE NOCASE",
+ new String[] { trimmedString });
+
+ // add the entry
+ final ContentValues values = new ContentValues(2);
+ values.put(SearchHistoryColumns.SEARCHSTRING, trimmedString);
+ values.put(SearchHistoryColumns.TIMESEARCHED, System.currentTimeMillis());
+ database.insert(SearchHistoryColumns.NAME, null, values);
+
+ // if our db is too large, delete the extra items
+ Cursor oldest = null;
+ try {
+ database.query(SearchHistoryColumns.NAME,
+ new String[]{SearchHistoryColumns.TIMESEARCHED}, null, null, null, null,
+ SearchHistoryColumns.TIMESEARCHED + " ASC");
+
+ if (oldest != null && oldest.getCount() > MAX_ITEMS_IN_DB) {
+ oldest.moveToPosition(oldest.getCount() - MAX_ITEMS_IN_DB);
+ long timeOfRecordToKeep = oldest.getLong(0);
+
+ database.delete(SearchHistoryColumns.NAME,
+ SearchHistoryColumns.TIMESEARCHED + " < ?",
+ new String[] { String.valueOf(timeOfRecordToKeep) });
+
+ }
+ } finally {
+ if (oldest != null) {
+ oldest.close();
+ oldest = null;
+ }
+ }
+ } finally {
+ database.setTransactionSuccessful();
+ database.endTransaction();
+ }
+ }
+
+ /**
+ * Gets a cursor to the list of recently searched strings
+ * @param limit number of items to limit to
+ * @return cursor
+ */
+ public Cursor queryRecentSearches(final String limit) {
+ final SQLiteDatabase database = mMusicDatabase.getReadableDatabase();
+ return database.query(SearchHistoryColumns.NAME,
+ new String[]{SearchHistoryColumns.SEARCHSTRING}, null, null, null, null,
+ SearchHistoryColumns.TIMESEARCHED + " DESC", limit);
+ }
+
+ /**
+ * Gets the recently searched strings
+ * @return list of esarched strings
+ */
+ public ArrayList<String> getRecentSearches() {
+ Cursor searches = queryRecentSearches(String.valueOf(MAX_ITEMS_IN_DB));
+
+ ArrayList<String> results = new ArrayList<String>(MAX_ITEMS_IN_DB);
+
+ try {
+ if (searches != null && searches.moveToFirst()) {
+ int colIdx = searches.getColumnIndex(SearchHistoryColumns.SEARCHSTRING);
+
+ do {
+ results.add(searches.getString(colIdx));
+ } while (searches.moveToNext());
+ }
+ } finally {
+ if (searches != null) {
+ searches.close();
+ searches = null;
+ }
+ }
+
+ return results;
+ }
+
+ public interface SearchHistoryColumns {
+ /* Table name */
+ public static final String NAME = "searchhistory";
+
+ /* What was searched */
+ public static final String SEARCHSTRING = "searchstring";
+
+ /* Time of search */
+ public static final String TIMESEARCHED = "timesearched";
+ }
+}
diff --git a/src/com/cyngn/eleven/ui/activities/SearchActivity.java b/src/com/cyngn/eleven/ui/activities/SearchActivity.java
index aede621..48422cb 100644
--- a/src/com/cyngn/eleven/ui/activities/SearchActivity.java
+++ b/src/com/cyngn/eleven/ui/activities/SearchActivity.java
@@ -13,15 +13,14 @@ package com.cyngn.eleven.ui.activities;
import android.app.ActionBar;
import android.app.SearchManager;
-import android.app.SearchableInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.Cursor;
-import android.graphics.Bitmap;
import android.media.AudioManager;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.provider.BaseColumns;
import android.provider.MediaStore;
@@ -33,26 +32,27 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
-import android.widget.TextView;
import com.cyngn.eleven.Config;
import com.cyngn.eleven.IElevenService;
import com.cyngn.eleven.R;
import com.cyngn.eleven.adapters.SummarySearchAdapter;
+import com.cyngn.eleven.loaders.WrappedAsyncTaskLoader;
import com.cyngn.eleven.model.AlbumArtistDetails;
import com.cyngn.eleven.model.SearchResult;
import com.cyngn.eleven.model.SearchResult.ResultType;
+import com.cyngn.eleven.provider.SearchHistory;
import com.cyngn.eleven.recycler.RecycleHolder;
import com.cyngn.eleven.sectionadapter.SectionAdapter;
import com.cyngn.eleven.sectionadapter.SectionCreator;
@@ -78,10 +78,26 @@ import static com.cyngn.eleven.utils.MusicUtils.mService;
*
* @author Andrew Neal (andrewdneal@gmail.com)
*/
-public class SearchActivity extends FragmentActivity implements LoaderCallbacks<SectionListContainer<SearchResult>>,
+public class SearchActivity extends FragmentActivity implements
+ LoaderCallbacks<SectionListContainer<SearchResult>>,
OnScrollListener, OnQueryTextListener, OnItemClickListener, ServiceConnection,
OnTouchListener {
/**
+ * Loading delay of 500ms so we don't flash the screen too much when loading new searches
+ */
+ private static int LOADING_DELAY = 500;
+
+ /**
+ * Identifier for the search loader
+ */
+ private static int SEARCH_LOADER = 0;
+
+ /**
+ * Identifier for the search history loader
+ */
+ private static int HISTORY_LOADER = 1;
+
+ /**
* The service token
*/
private ServiceToken mToken;
@@ -128,6 +144,43 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
private ResultType mSearchType;
/**
+ * Search History loader callback
+ */
+ private SearchHistoryCallback mSearchHistoryCallback;
+
+ /**
+ * List view
+ */
+ private ListView mSearchHistoryListView;
+
+ /**
+ * This tracks our current visible state between the different views
+ */
+ enum VisibleState {
+ SearchHistory,
+ Empty,
+ SearchResults,
+ Loading,
+ }
+
+ private VisibleState mCurrentState;
+
+ /**
+ * Handler for posting runnables
+ */
+ private Handler mHandler;
+
+ /**
+ * A runnable to show the loading view that will be posted with a delay to prevent flashing
+ */
+ private Runnable mLoadingRunnable;
+
+ /**
+ * Flag used to track if we are quitting so we don't flash loaders while finishing the activity
+ */
+ private boolean mQuitting = false;
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -144,7 +197,7 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
mToken = MusicUtils.bindToService(this, this);
// Set the layout
- setContentView(R.layout.list_base);
+ setContentView(R.layout.activity_search);
// get the input method manager
mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -164,6 +217,15 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
initListView();
+ // setup handler and runnable
+ mHandler = new Handler();
+ mLoadingRunnable = new Runnable() {
+ @Override
+ public void run() {
+ setState(VisibleState.Loading);
+ }
+ };
+
// Theme the action bar
final ActionBar actionBar = getActionBar();
actionBar.setHomeButtonEnabled(true);
@@ -173,7 +235,7 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
mFilterString = getIntent().getStringExtra(SearchManager.QUERY);
// if we have a non-empty search string, this is a 2nd lvl search
- if (mFilterString != null && !mFilterString.isEmpty()) {
+ if (!TextUtils.isEmpty(mFilterString)) {
mTopLevelSearch = false;
// get the search type to filter by
@@ -203,11 +265,14 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
// Set the prefix
mAdapter.getUnderlyingAdapter().setPrefix(mFilterString);
- // Prepare the loader. Either re-connect with an existing one,
- // or start a new one.
- getSupportLoaderManager().initLoader(0, null, this);
+ // Start the loader for the query
+ getSupportLoaderManager().initLoader(SEARCH_LOADER, null, this);
} else {
mTopLevelSearch = true;
+ mSearchHistoryCallback = new SearchHistoryCallback();
+
+ // Start the loader for the search history
+ getSupportLoaderManager().initLoader(HISTORY_LOADER, null, mSearchHistoryCallback);
}
// set the background on the root view
@@ -235,15 +300,33 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
// when updating the search. For now let's manually toggle visibility and come back
// to this later
//mListView.setEmptyView(mNoResultsContainer);
+
+ // load the search history list view
+ mSearchHistoryListView = (ListView)findViewById(R.id.list_search_history);
+ mSearchHistoryListView.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ String searchItem = (String)mSearchHistoryListView.getAdapter().getItem(position);
+ mSearchView.setQuery(searchItem, true);
+ }
+ });
+ mSearchHistoryListView.setOnTouchListener(this);
}
/**
* {@inheritDoc}
*/
@Override
- public Loader<SectionListContainer<SearchResult>> onCreateLoader(final int id, final Bundle args) {
+ public Loader<SectionListContainer<SearchResult>> onCreateLoader(final int id,
+ final Bundle args) {
IItemCompare<SearchResult> comparator = null;
+ // prep the loader in case the query takes a long time
+ setLoading();
+
+ // set the no results string ahead of time in case the user changes the string whiel loading
+ mNoResultsContainer.setMainHighlightText("\"" + mFilterString + "\"");
+
// if we are at the top level, create a comparator to separate the different types into
// their own sections (artists, albums, etc)
if (mTopLevelSearch) {
@@ -291,8 +374,8 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
- finish();
- return true;
+ quit();
+ return false;
}
});
@@ -301,6 +384,11 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
return super.onCreateOptionsMenu(menu);
}
+ private void quit() {
+ mQuitting = true;
+ finish();
+ }
+
/**
* {@inheritDoc}
*/
@@ -339,7 +427,7 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
- finish();
+ quit();
return true;
default:
break;
@@ -357,13 +445,13 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
if (data.mListResults.isEmpty()) {
// clear the adapter
mAdapter.clear();
- mListView.setVisibility(View.INVISIBLE);
- mNoResultsContainer.setVisibility(View.VISIBLE);
+ // show the empty state
+ setState(VisibleState.Empty);
} else {
// Set the data
mAdapter.setData(data);
- mListView.setVisibility(View.VISIBLE);
- mNoResultsContainer.setVisibility(View.INVISIBLE);
+ // show the search results
+ setState(VisibleState.SearchResults);
}
}
@@ -372,8 +460,6 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
*/
@Override
public void onLoaderReset(final Loader<SectionListContainer<SearchResult>> loader) {
- // Clear the data in the adapter
- mAdapter.unload();
}
/**
@@ -396,11 +482,9 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
*/
@Override
public boolean onQueryTextSubmit(final String query) {
- if (TextUtils.isEmpty(query)) {
- mFilterString = "";
- return false;
- }
-
+ // simulate an on query text change
+ onQueryTextChange(query);
+ // hide the input manager
hideInputManager();
return true;
@@ -408,13 +492,64 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
public void hideInputManager() {
// When the search is "committed" by the user, then hide the keyboard so
- // the user can
- // more easily browse the list of results.
+ // the user can more easily browse the list of results.
if (mSearchView != null) {
if (mImm != null && mImm.isImeShowing()) {
mImm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0);
}
mSearchView.clearFocus();
+
+ // add our search string
+ SearchHistory.getInstance(this).addSearchString(mFilterString);
+ }
+ }
+
+ /**
+ * This posts a delayed for showing the loading screen. The reason for the delayed is we
+ * don't want to flash the loading icon very often since searches usually are pretty fast
+ */
+ public void setLoading() {
+ if (mCurrentState != VisibleState.Loading) {
+ if (!mHandler.hasCallbacks(mLoadingRunnable)) {
+ mHandler.postDelayed(mLoadingRunnable, LOADING_DELAY);
+ }
+ }
+ }
+
+ /**
+ * Sets the currently visible view
+ * @param state the current visible state
+ */
+ public void setState(VisibleState state) {
+ // remove any delayed runnables. This has to be before mCurrentState == state
+ // in case the state doesn't change but we've created a loading runnable
+ mHandler.removeCallbacks(mLoadingRunnable);
+
+ // if we are already looking at view already, just quit
+ if (mCurrentState == state) {
+ return;
+ }
+
+ mCurrentState = state;
+
+ mSearchHistoryListView.setVisibility(View.INVISIBLE);
+ mListView.setVisibility(View.INVISIBLE);
+ mNoResultsContainer.setVisibility(View.INVISIBLE);
+
+ switch (mCurrentState) {
+ case SearchHistory:
+ mSearchHistoryListView.setVisibility(View.VISIBLE);
+ break;
+ case SearchResults:
+ mListView.setVisibility(View.VISIBLE);
+ break;
+ case Empty:
+ mNoResultsContainer.setVisibility(View.VISIBLE);
+ break;
+ default:
+ // Don't show anything for now - we need a loading screen
+ // see bug: https://cyanogen.atlassian.net/browse/MUSIC-63
+ break;
}
}
@@ -423,19 +558,34 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
*/
@Override
public boolean onQueryTextChange(final String newText) {
+ if (mQuitting) {
+ return true;
+ }
+
if (TextUtils.isEmpty(newText)) {
- mListView.setVisibility(View.INVISIBLE);
- mNoResultsContainer.setVisibility(View.INVISIBLE);
- mFilterString = "";
- return false;
+ if (!TextUtils.isEmpty(mFilterString)) {
+ mFilterString = "";
+ getSupportLoaderManager().restartLoader(HISTORY_LOADER, null,
+ mSearchHistoryCallback);
+ getSupportLoaderManager().destroyLoader(SEARCH_LOADER);
+ }
+
+ return true;
+ }
+
+ // if the strings are the same, return
+ if (newText.equals(mFilterString)) {
+ return true;
}
+
// Called when the action bar search text has changed. Update
// the search filter, and restart the loader to do a new query
// with this filter.
- mFilterString = !TextUtils.isEmpty(newText) ? newText : null;
+ mFilterString = newText;
// Set the prefix
mAdapter.getUnderlyingAdapter().setPrefix(mFilterString);
- getSupportLoaderManager().restartLoader(0, null, this);
+ getSupportLoaderManager().restartLoader(SEARCH_LOADER, null, this);
+ getSupportLoaderManager().destroyLoader(HISTORY_LOADER);
return true;
}
@@ -523,7 +673,8 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
case Song:
item = SearchResult.createSearchResult(cursor);
if (item != null) {
- AlbumArtistDetails details = MusicUtils.getAlbumArtDetails(getContext(), item.mId);
+ AlbumArtistDetails details = MusicUtils.getAlbumArtDetails(getContext(),
+ item.mId);
if (details != null) {
item.mArtist = details.mArtistName;
item.mAlbum = details.mAlbumName;
@@ -681,14 +832,14 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
public static Cursor makePlaylistSearchCursor(final Context context,
final String searchTerms) {
- if (searchTerms == null || searchTerms.isEmpty()) {
+ if (TextUtils.isEmpty(searchTerms)) {
return null;
}
// trim out special characters like % or \ as well as things like "a" "and" etc
String trimmedSearchTerms = MusicUtils.getTrimmedName(searchTerms);
- if (trimmedSearchTerms == null || trimmedSearchTerms.isEmpty()) {
+ if (TextUtils.isEmpty(trimmedSearchTerms)) {
return null;
}
@@ -710,7 +861,8 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
}
}
- return context.getContentResolver().query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
+ return context.getContentResolver().query(
+ MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
new String[]{
/* 0 */
BaseColumns._ID,
@@ -720,7 +872,50 @@ public class SearchActivity extends FragmentActivity implements LoaderCallbacks<
}
}
+ /**
+ * Loads the search history in the background and creates an array adapter
+ */
+ public static class SearchHistoryLoader extends WrappedAsyncTaskLoader<ArrayAdapter<String>> {
+ public SearchHistoryLoader(Context context) {
+ super(context);
+ }
+
+ @Override
+ public ArrayAdapter<String> loadInBackground() {
+ ArrayList<String> strings = SearchHistory.getInstance(getContext()).getRecentSearches();
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(),
+ R.layout.list_item_search_history, R.id.line_one);
+ adapter.addAll(strings);
+ return adapter;
+ }
+ }
+ /**
+ * This handles the Loader callbacks for the search history
+ */
+ public class SearchHistoryCallback implements LoaderCallbacks<ArrayAdapter<String>> {
+ @Override
+ public Loader<ArrayAdapter<String>> onCreateLoader(int i, Bundle bundle) {
+ // prep the loader in case the query takes a long time
+ setLoading();
+
+ return new SearchHistoryLoader(SearchActivity.this);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<ArrayAdapter<String>> searchHistoryAdapterLoader,
+ ArrayAdapter<String> searchHistoryAdapter) {
+ // show the search history
+ setState(VisibleState.SearchHistory);
+
+ mSearchHistoryListView.setAdapter(searchHistoryAdapter);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<ArrayAdapter<String>> cursorAdapterLoader) {
+
+ }
+ }
/**
* {@inheritDoc}