From 1497dadfe0456958a76727c3b1daea53c7b030a4 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli Date: Fri, 29 Apr 2016 16:10:13 +0200 Subject: [PATCH 01/44] Use a Timer, and a TimerTask, to correctly track application `background` state. --- .../java/org/wordpress/android/WordPress.java | 70 ++++++++++++------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/WordPress.java b/WordPress/src/main/java/org/wordpress/android/WordPress.java index 41fc5c1b98c3..f1d92d83429a 100644 --- a/WordPress/src/main/java/org/wordpress/android/WordPress.java +++ b/WordPress/src/main/java/org/wordpress/android/WordPress.java @@ -82,6 +82,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; import de.greenrobot.event.EventBus; import io.fabric.sdk.android.Fabric; @@ -698,8 +700,11 @@ private class ApplicationLifecycleMonitor implements Application.ActivityLifecyc private final int DEFAULT_TIMEOUT = 2 * 60; // 2 minutes private Date mLastPingDate; private Date mApplicationOpenedDate; - boolean mIsInBackground = true; boolean mFirstActivityResumed = true; + private Timer mActivityTransitionTimer; + private TimerTask mActivityTransitionTimerTask; + private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000; + boolean mIsInBackground = true; @Override public void onConfigurationChanged(final Configuration newConfig) { @@ -713,25 +718,6 @@ public void onLowMemory() { @Override public void onTrimMemory(final int level) { - if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { - // We're in the Background - mIsInBackground = true; - String lastActivityString = AppPrefs.getLastActivityStr(); - ActivityId lastActivity = ActivityId.getActivityIdFromName(lastActivityString); - Map properties = new HashMap(); - properties.put("last_visible_screen", lastActivity.toString()); - if (mApplicationOpenedDate != null) { - Date now = new Date(); - properties.put("time_in_app", DateTimeUtils.secondsBetween(now, mApplicationOpenedDate)); - mApplicationOpenedDate = null; - } - AnalyticsTracker.track(AnalyticsTracker.Stat.APPLICATION_CLOSED, properties); - AnalyticsTracker.endSession(false); - onAppGoesToBackground(); - } else { - mIsInBackground = false; - } - boolean evictBitmaps = false; switch (level) { case TRIM_MEMORY_COMPLETE: @@ -776,9 +762,42 @@ private void updatePushNotificationTokenIfNotLimited() { } } - public void onAppGoesToBackground() { - AppLog.i(T.UTILS, "App goes to background"); - ConnectionChangeReceiver.setEnabled(WordPress.this, false); + private void startActivityTransitionTimer() { + this.mActivityTransitionTimer = new Timer(); + this.mActivityTransitionTimerTask = new TimerTask() { + public void run() { + AppLog.i(T.UTILS, "App goes to background"); + // We're in the Background + mIsInBackground = true; + String lastActivityString = AppPrefs.getLastActivityStr(); + ActivityId lastActivity = ActivityId.getActivityIdFromName(lastActivityString); + Map properties = new HashMap(); + properties.put("last_visible_screen", lastActivity.toString()); + if (mApplicationOpenedDate != null) { + Date now = new Date(); + properties.put("time_in_app", DateTimeUtils.secondsBetween(now, mApplicationOpenedDate)); + mApplicationOpenedDate = null; + } + AnalyticsTracker.track(AnalyticsTracker.Stat.APPLICATION_CLOSED, properties); + AnalyticsTracker.endSession(false); + ConnectionChangeReceiver.setEnabled(WordPress.this, false); + } + }; + + this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask, + MAX_ACTIVITY_TRANSITION_TIME_MS); + } + + private void stopActivityTransitionTimer() { + if (this.mActivityTransitionTimerTask != null) { + this.mActivityTransitionTimerTask.cancel(); + } + + if (this.mActivityTransitionTimer != null) { + this.mActivityTransitionTimer.cancel(); + } + + mIsInBackground = false; } /** @@ -786,7 +805,7 @@ public void onAppGoesToBackground() { * 1. the app starts (but it's not opened by a service or a broadcast receiver, i.e. an activity is resumed) * 2. the app was in background and is now foreground */ - public void onAppComesFromBackground() { + private void onAppComesFromBackground() { AppLog.i(T.UTILS, "App comes from background"); ConnectionChangeReceiver.setEnabled(WordPress.this, true); AnalyticsUtils.refreshMetadata(); @@ -811,6 +830,8 @@ public void onActivityResumed(Activity activity) { // was in background before onAppComesFromBackground(); } + stopActivityTransitionTimer(); + mIsInBackground = false; if (mFirstActivityResumed) { deferredInit(activity); @@ -829,6 +850,7 @@ public void onActivityDestroyed(Activity arg0) { @Override public void onActivityPaused(Activity arg0) { mLastPingDate = new Date(); + startActivityTransitionTimer(); } @Override From 1d9c4e06b1a05f4edc26b59a259ed7b51a178e9a Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 09:31:33 -0400 Subject: [PATCH 02/44] First pass at reader search service --- WordPress/src/main/AndroidManifest.xml | 4 + .../android/ui/reader/ReaderEvents.java | 28 +++++ .../ui/reader/ReaderPostListFragment.java | 36 ++++-- .../ui/reader/adapters/ReaderPostAdapter.java | 13 +- .../reader/services/ReaderSearchService.java | 117 ++++++++++++++++++ 5 files changed, 184 insertions(+), 14 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index 429fd210a423..a011eae15895 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -389,6 +389,10 @@ android:name=".ui.reader.services.ReaderPostService" android:exported="false" android:label="Reader Post Service" /> + created"); + } + + @Override + public void onDestroy() { + AppLog.i(AppLog.T.READER, "reader search service > destroyed"); + super.onDestroy(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (intent == null) { + return START_NOT_STICKY; + } + + String query = intent.getStringExtra(ARG_QUERY); + int offset = intent.getIntExtra(ARG_OFFSET, 0); + startSearch(query, offset); + + return START_NOT_STICKY; + } + + private void startSearch(final String query, int offset) { + // TODO: verify that &meta param is honored + String path = "read/search?q=" + + UrlUtils.urlEncode(query) + + "&number=" + ReaderConstants.READER_MAX_POSTS_TO_REQUEST + + "&meta=site,likes"; + if (offset > 0) { + path += "&offset=" + offset; + } + + RestRequest.Listener listener = new RestRequest.Listener() { + @Override + public void onResponse(JSONObject jsonObject) { + if (jsonObject != null) { + handleSearchResponse(query, jsonObject); + } else { + EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, null)); + } + } + }; + RestRequest.ErrorListener errorListener = new RestRequest.ErrorListener() { + @Override + public void onErrorResponse(VolleyError volleyError) { + AppLog.e(AppLog.T.READER, volleyError); + EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, null)); + } + }; + + AppLog.d(AppLog.T.READER, "starting search for " + query); + WordPress.getRestClientUtilsV1_2().get(path, null, null, listener, errorListener); + } + + private static void handleSearchResponse(final String query, final JSONObject jsonObject) { + new Thread() { + @Override + public void run() { + ReaderPostList serverPosts = ReaderPostList.fromJson(jsonObject); + ReaderPostTable.addOrUpdatePosts(null, serverPosts); + EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, serverPosts)); + } + }.start(); + } +} From 24a4bf5e69c0784a8f30808d79454448f0836526 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 09:38:18 -0400 Subject: [PATCH 03/44] Submit search query on suggestion click --- .../wordpress/android/ui/reader/ReaderPostListFragment.java | 6 +++++- .../android/ui/reader/adapters/ReaderPostAdapter.java | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index a252ed0ef813..39056f3a6a2d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -650,7 +650,7 @@ public boolean onSuggestionSelect(int position) { public boolean onSuggestionClick(int position) { String query = mSearchSuggestionAdapter.getSuggestion(position); if (!TextUtils.isEmpty(query)) { - mSearchView.setQuery(query, false); + mSearchView.setQuery(query, true); } return true; } @@ -1344,6 +1344,10 @@ public void onPostSelected(ReaderPost post) { post.blogId, post.postId); break; + case SEARCH_RESULTS: + // TODO: track analytics + ReaderActivityLauncher.showReaderPostDetail(getActivity(), post.blogId, post.postId); + break; } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java index 2a1007072bf6..4e62a7d37fe6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java @@ -2,7 +2,6 @@ import android.content.Context; import android.os.AsyncTask; -import android.support.annotation.NonNull; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; @@ -53,7 +52,6 @@ public class ReaderPostAdapter extends RecyclerView.Adapter Date: Fri, 13 May 2016 09:45:40 -0400 Subject: [PATCH 04/44] Empty view for no results now works --- .../org/wordpress/android/ui/reader/ReaderPostListFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 39056f3a6a2d..f00d02395742 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -680,7 +680,7 @@ public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { if (event.hasResults()) { getPostAdapter().setSearchResults(event.getResults()); } else { - setEmptyTitleAndDescription(false); + mDataLoadedListener.onDataLoaded(true); } } From f9a5b4b72ce9157d2b4f827f920271139daa33dd Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 10:06:06 -0400 Subject: [PATCH 05/44] Added "Searching..." message while search is active --- .../ui/reader/ReaderPostListFragment.java | 59 +++++++++++-------- .../reader/services/ReaderSearchService.java | 6 +- .../res/layout/reader_fragment_post_cards.xml | 2 +- WordPress/src/main/res/values/strings.xml | 1 + 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index f00d02395742..dae170dd9122 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -5,6 +5,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.StringRes; import android.support.design.widget.Snackbar; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.ListPopupWindow; @@ -583,8 +584,9 @@ public boolean onQueryTextChange(String newText) { private void showSearchUI() { if (!isAdded()) return; - // hide settings icon + // hide settings icon and show message letting user know what they're searching mSettingsMenuItem.setVisible(false); + showSearchMessage(R.string.reader_label_post_search_explainer); // create the suggestion adapter if it doesn't already exist, otherwise repopulate it // so the latest suggestions appear @@ -594,18 +596,6 @@ private void showSearchUI() { mSearchSuggestionAdapter.populate(); } - // show message letting user know what they're querying, but only if the user is in - // portrait mode or the device is a tablet (since there's not enough space for the - // message when the virtual keyboard is visible) - boolean isLandscape = DisplayUtils.isLandscape(getActivity()); - boolean isTablet = DisplayUtils.isXLarge(getActivity()); - if (isTablet || !isLandscape) { - TextView txtSearchExplainer = (TextView) getView().findViewById(R.id.text_search_explainer); - if (txtSearchExplainer.getVisibility() != View.VISIBLE) { - AniUtils.fadeIn(txtSearchExplainer, AniUtils.Duration.LONG); - } - } - // hide the recycler (post list) RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); if (recycler != null && recycler.getVisibility() == View.VISIBLE) { @@ -616,23 +606,44 @@ private void showSearchUI() { private void hideSearchUI() { if (!isAdded()) return; - // redisplay settings icon mSettingsMenuItem.setVisible(true); + hideSearchMessage(); - // hide the explainer - TextView txtSearchExplainer = (TextView) getView().findViewById(R.id.text_search_explainer); - if (txtSearchExplainer.getVisibility() == View.VISIBLE) { - AniUtils.fadeOut(txtSearchExplainer, AniUtils.Duration.LONG); - } - txtSearchExplainer.setVisibility(View.GONE); - - // show the recycler + // show the recycler again RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); if (recycler != null && recycler.getVisibility() != View.VISIBLE) { AniUtils.fadeIn(recycler, AniUtils.Duration.LONG); } } + /* + * show message letting user know what they're querying, but only if the user is in + * portrait mode or the device is a tablet (since there's not enough space for the + * message when the virtual keyboard is visible) + */ + private void showSearchMessage(@StringRes int stringResId) { + if (!isAdded()) return; + + boolean isLandscape = DisplayUtils.isLandscape(getActivity()); + boolean isTablet = DisplayUtils.isXLarge(getActivity()); + if (isLandscape && !isTablet) return; + + TextView txtSearchMsg = (TextView) getView().findViewById(R.id.text_search_message); + txtSearchMsg.setText(stringResId); + if (txtSearchMsg.getVisibility() != View.VISIBLE) { + AniUtils.fadeIn(txtSearchMsg, AniUtils.Duration.LONG); + } + } + + private void hideSearchMessage() { + if (!isAdded()) return; + + TextView txtSearchMsg = (TextView) getView().findViewById(R.id.text_search_message); + if (txtSearchMsg.getVisibility() == View.VISIBLE) { + AniUtils.fadeOut(txtSearchMsg, AniUtils.Duration.LONG); + } + } + /* * create and assign the suggestion adapter for the search view */ @@ -669,7 +680,7 @@ public void onEventMainThread(ReaderEvents.SearchPostsStarted event) { if (!isAdded()) return; setIsUpdating(true); - setEmptyTitleAndDescription(false); + showSearchMessage(R.string.reader_label_post_search_running); } @SuppressWarnings("unused") @@ -677,6 +688,8 @@ public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { if (!isAdded() || getPostListType() != ReaderPostListType.SEARCH_RESULTS) return; setIsUpdating(false); + hideSearchMessage(); + if (event.hasResults()) { getPostAdapter().setSearchResults(event.getResults()); } else { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index fbe461f527fb..37d675ec6beb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -29,9 +29,6 @@ public class ReaderSearchService extends Service { private static final String ARG_QUERY = "query"; private static final String ARG_OFFSET = "offset"; - /* - * search for posts matching the passed query - */ public static void startService(Context context, @NonNull String query) { startService(context, query, 0); } @@ -100,7 +97,8 @@ public void onErrorResponse(VolleyError volleyError) { } }; - AppLog.d(AppLog.T.READER, "starting search for " + query); + AppLog.d(AppLog.T.READER, "reader search service > starting search for " + query); + EventBus.getDefault().post(new ReaderEvents.SearchPostsStarted(query)); WordPress.getRestClientUtilsV1_2().get(path, null, null, listener, errorListener); } diff --git a/WordPress/src/main/res/layout/reader_fragment_post_cards.xml b/WordPress/src/main/res/layout/reader_fragment_post_cards.xml index c8acb14a5097..8e6144d76657 100644 --- a/WordPress/src/main/res/layout/reader_fragment_post_cards.xml +++ b/WordPress/src/main/res/layout/reader_fragment_post_cards.xml @@ -58,7 +58,7 @@ tools:visibility="visible" /> SEND Load more posts Search all public WordPress.com blogs + Searching… Like From ca18d728c8fe3f496b868e696d85c00220666534 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 10:15:08 -0400 Subject: [PATCH 06/44] Search results are now stored in main post table --- .../android/ui/reader/ReaderEvents.java | 20 +++++++++---------- .../ui/reader/ReaderPostListFragment.java | 2 +- .../ui/reader/adapters/ReaderPostAdapter.java | 18 +++++++++-------- .../reader/services/ReaderSearchService.java | 15 ++++++++++---- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java index fc1c7c945a5a..4fc06241897c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java @@ -2,7 +2,6 @@ import android.support.annotation.NonNull; -import org.wordpress.android.models.ReaderPostList; import org.wordpress.android.models.ReaderTag; import org.wordpress.android.ui.reader.actions.ReaderActions; import org.wordpress.android.ui.reader.services.ReaderPostService; @@ -71,23 +70,22 @@ public static class SearchPostsStarted { public SearchPostsStarted(@NonNull String query) { mQuery = query; } + public String getQuery() { + return mQuery; + } } public static class SearchPostsEnded { private final String mQuery; - private final ReaderPostList mSearchResults; - public SearchPostsEnded(@NonNull String query, ReaderPostList searchResults) { + private final int mNumResults; + public SearchPostsEnded(@NonNull String query, int numResults) { mQuery = query; - if (searchResults != null) { - mSearchResults = (ReaderPostList) searchResults.clone(); - } else { - mSearchResults = new ReaderPostList(); - } + mNumResults = numResults; } public boolean hasResults() { - return !mSearchResults.isEmpty(); + return mNumResults > 0; } - public ReaderPostList getResults() { - return mSearchResults; + public String getQuery() { + return mQuery; } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index dae170dd9122..5dfdf5286668 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -691,7 +691,7 @@ public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { hideSearchMessage(); if (event.hasResults()) { - getPostAdapter().setSearchResults(event.getResults()); + getPostAdapter().setCurrentSearchQuery(event.getQuery()); } else { mDataLoadedListener.onDataLoaded(true); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java index 4e62a7d37fe6..a102b8087176 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java @@ -2,6 +2,7 @@ import android.content.Context; import android.os.AsyncTask; +import android.support.annotation.NonNull; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; @@ -27,6 +28,7 @@ import org.wordpress.android.ui.reader.actions.ReaderActions; import org.wordpress.android.ui.reader.actions.ReaderPostActions; import org.wordpress.android.ui.reader.models.ReaderBlogIdPostId; +import org.wordpress.android.ui.reader.services.ReaderSearchService; import org.wordpress.android.ui.reader.utils.ReaderUtils; import org.wordpress.android.ui.reader.utils.ReaderXPostUtils; import org.wordpress.android.ui.reader.views.ReaderBlogInfoView; @@ -47,6 +49,7 @@ public class ReaderPostAdapter extends RecyclerView.Adapter Date: Fri, 13 May 2016 10:19:09 -0400 Subject: [PATCH 07/44] Updated getTagForSearchQuery --- .../android/ui/reader/services/ReaderSearchService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index 50eb25c618ac..0a4363526db1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -115,8 +115,11 @@ public void run() { }.start(); } + /* + * we use a special tag name when storing search results in the reader post table + */ public static ReaderTag getTagForSearchQuery(@NonNull String query) { - String slug = "search:" + query; + String slug = ":search:" + query; return new ReaderTag(slug, query, query, null, ReaderTagType.FOLLOWED); } } From 41477b56ff746e2380483531e8c2420128c70bfe Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 13:12:34 -0400 Subject: [PATCH 08/44] Added ReaderPostListType.SEARCH_RESULTS --- .../org/wordpress/android/models/ReaderTagType.java | 8 +++++++- .../android/ui/reader/ReaderPostListFragment.java | 2 ++ .../ui/reader/services/ReaderSearchService.java | 10 +++++++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderTagType.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderTagType.java index f20ef28da3cd..5075e81d35f9 100644 --- a/WordPress/src/main/java/org/wordpress/android/models/ReaderTagType.java +++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderTagType.java @@ -4,12 +4,14 @@ public enum ReaderTagType { FOLLOWED, DEFAULT, RECOMMENDED, - CUSTOM_LIST; + CUSTOM_LIST, + SEARCH; private static final int INT_DEFAULT = 0; private static final int INT_FOLLOWED = 1; private static final int INT_RECOMMENDED = 2; private static final int INT_CUSTOM_LIST = 3; + private static final int INT_SEARCH = 4; public static ReaderTagType fromInt(int value) { switch (value) { @@ -19,6 +21,8 @@ public static ReaderTagType fromInt(int value) { return FOLLOWED; case INT_CUSTOM_LIST: return CUSTOM_LIST; + case INT_SEARCH: + return SEARCH; default : return DEFAULT; } @@ -32,6 +36,8 @@ public int toInt() { return INT_RECOMMENDED; case CUSTOM_LIST: return INT_CUSTOM_LIST; + case SEARCH: + return INT_SEARCH; default : return INT_DEFAULT; } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 5dfdf5286668..f190601ffa56 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -928,6 +928,8 @@ private ReaderPostAdapter getPostAdapter() { mPostAdapter.setCurrentTag(getCurrentTag()); } else if (getPostListType() == ReaderPostListType.BLOG_PREVIEW) { mPostAdapter.setCurrentBlogAndFeed(mCurrentBlogId, mCurrentFeedId); + } else if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { + mPostAdapter.setCurrentSearchQuery(mCurrentSearchQuery); } } return mPostAdapter; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index 0a4363526db1..7e455ca9d1ab 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -17,6 +17,7 @@ import org.wordpress.android.models.ReaderTagType; import org.wordpress.android.ui.reader.ReaderConstants; import org.wordpress.android.ui.reader.ReaderEvents; +import org.wordpress.android.ui.reader.utils.ReaderUtils; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.UrlUtils; @@ -79,6 +80,9 @@ private void startSearch(final String query, int offset) { + "&meta=site,likes"; if (offset > 0) { path += "&offset=" + offset; + } else { + // delete existing posts for this query if there's no offset + ReaderPostTable.deletePostsWithTag(getTagForSearchQuery(query)); } RestRequest.Listener listener = new RestRequest.Listener() { @@ -116,10 +120,10 @@ public void run() { } /* - * we use a special tag name when storing search results in the reader post table + * used when storing search results in the reader post table */ public static ReaderTag getTagForSearchQuery(@NonNull String query) { - String slug = ":search:" + query; - return new ReaderTag(slug, query, query, null, ReaderTagType.FOLLOWED); + String slug = ReaderUtils.sanitizeWithDashes(query); + return new ReaderTag(slug, query, query, null, ReaderTagType.SEARCH); } } From 39f2a1f4c8589ff012116b31974ee05545ca0004 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 15:14:36 -0400 Subject: [PATCH 09/44] Search results are now correctly displayed when search finishes --- .../ui/reader/ReaderPostListFragment.java | 16 ++++++++-------- .../ui/reader/adapters/ReaderPostAdapter.java | 6 ++---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index f190601ffa56..4d16fae4800c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -5,7 +5,6 @@ import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.annotation.StringRes; import android.support.design.widget.Snackbar; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.ListPopupWindow; @@ -586,7 +585,7 @@ private void showSearchUI() { // hide settings icon and show message letting user know what they're searching mSettingsMenuItem.setVisible(false); - showSearchMessage(R.string.reader_label_post_search_explainer); + showSearchMessage(); // create the suggestion adapter if it doesn't already exist, otherwise repopulate it // so the latest suggestions appear @@ -621,7 +620,7 @@ private void hideSearchUI() { * portrait mode or the device is a tablet (since there's not enough space for the * message when the virtual keyboard is visible) */ - private void showSearchMessage(@StringRes int stringResId) { + private void showSearchMessage() { if (!isAdded()) return; boolean isLandscape = DisplayUtils.isLandscape(getActivity()); @@ -629,7 +628,6 @@ private void showSearchMessage(@StringRes int stringResId) { if (isLandscape && !isTablet) return; TextView txtSearchMsg = (TextView) getView().findViewById(R.id.text_search_message); - txtSearchMsg.setText(stringResId); if (txtSearchMsg.getVisibility() != View.VISIBLE) { AniUtils.fadeIn(txtSearchMsg, AniUtils.Duration.LONG); } @@ -680,15 +678,13 @@ public void onEventMainThread(ReaderEvents.SearchPostsStarted event) { if (!isAdded()) return; setIsUpdating(true); - showSearchMessage(R.string.reader_label_post_search_running); } @SuppressWarnings("unused") public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { - if (!isAdded() || getPostListType() != ReaderPostListType.SEARCH_RESULTS) return; + if (!isAdded()) return; setIsUpdating(false); - hideSearchMessage(); if (event.hasResults()) { getPostAdapter().setCurrentSearchQuery(event.getQuery()); @@ -801,7 +797,11 @@ private void setEmptyTitleAndDescription(boolean requestFailed) { } else if (requestFailed) { title = getString(R.string.reader_empty_posts_request_failed); } else if (isUpdating()) { - title = getString(R.string.reader_empty_posts_in_tag_updating); + if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { + title = getString(R.string.reader_label_post_search_running); + } else { + title = getString(R.string.reader_empty_posts_in_tag_updating); + } } else if (getPostListType() == ReaderPostListType.BLOG_PREVIEW) { title = getString(R.string.reader_empty_posts_in_blog); } else if (getPostListType() == ReaderPostListType.TAG_FOLLOWED && hasCurrentTag()) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java index a102b8087176..2131e1afaf65 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java @@ -584,10 +584,8 @@ public void setCurrentBlogAndFeed(long blogId, long feedId) { } public void setCurrentSearchQuery(@NonNull String query) { - if (!query.equals(mCurrentSearchQuery)) { - mCurrentSearchQuery = query; - reload(); - } + mCurrentSearchQuery = query; + reload(); } public void clear() { From 89190a46f48fa31014ddb8915f45a99b88230c26 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 15:29:23 -0400 Subject: [PATCH 10/44] Clear existing results before performing search --- .../android/ui/reader/ReaderActivityLauncher.java | 5 +++++ .../android/ui/reader/ReaderPostListFragment.java | 7 +++++++ .../android/ui/reader/services/ReaderSearchService.java | 3 --- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java index aaae767806e5..c59aab5248b2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java @@ -14,6 +14,7 @@ import org.wordpress.android.R; import org.wordpress.android.analytics.AnalyticsTracker; +import org.wordpress.android.datasets.ReaderPostTable; import org.wordpress.android.datasets.ReaderSearchTable; import org.wordpress.android.models.AccountHelper; import org.wordpress.android.models.ReaderComment; @@ -22,6 +23,7 @@ import org.wordpress.android.ui.ActivityLauncher; import org.wordpress.android.ui.WPWebViewActivity; import org.wordpress.android.ui.reader.ReaderTypes.ReaderPostListType; +import org.wordpress.android.ui.reader.services.ReaderSearchService; import org.wordpress.android.util.AnalyticsUtils; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.UrlUtils; @@ -141,6 +143,9 @@ public static void showReaderSearchResults(Context context, String query) { // record this search query ReaderSearchTable.addOrUpdateQueryString(query); + // delete existing results for this query + ReaderPostTable.deletePostsWithTag(ReaderSearchService.getTagForSearchQuery(query)); + // TODO: track analytics //AnalyticsTracker.track(AnalyticsTracker.Stat.???); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 4d16fae4800c..29b49c983c75 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -684,9 +684,16 @@ public void onEventMainThread(ReaderEvents.SearchPostsStarted event) { public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { if (!isAdded()) return; + // make sure this is for the current search + if (getPostListType() != ReaderPostListType.SEARCH_RESULTS + || !event.getQuery().equals(mCurrentSearchQuery)) { + return; + } + setIsUpdating(false); if (event.hasResults()) { + // this will load the search results getPostAdapter().setCurrentSearchQuery(event.getQuery()); } else { mDataLoadedListener.onDataLoaded(true); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index 7e455ca9d1ab..14637f186c9f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -80,9 +80,6 @@ private void startSearch(final String query, int offset) { + "&meta=site,likes"; if (offset > 0) { path += "&offset=" + offset; - } else { - // delete existing posts for this query if there's no offset - ReaderPostTable.deletePostsWithTag(getTagForSearchQuery(query)); } RestRequest.Listener listener = new RestRequest.Listener() { From b366ecccf67858fe6bb9573d8e3f2305ca2412c4 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 15:36:22 -0400 Subject: [PATCH 11/44] Return max 10 results --- .../android/datasets/ReaderSearchTable.java | 3 --- .../android/ui/reader/ReaderConstants.java | 17 +++++++++-------- .../ui/reader/services/ReaderSearchService.java | 2 +- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java index d921060cb5b5..c8b6f10c7013 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java @@ -54,9 +54,6 @@ private static int getCounterForQueryString(@NonNull String query) { "SELECT counter FROM tbl_search_history WHERE query_string=?", args); } - public static List getQueryStrings() { - return getQueryStrings(null); - } public static List getQueryStrings(String filter) { List queries = new ArrayList<>(); Cursor cursor; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java index f79b9d1d0628..45440db5fd73 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java @@ -1,16 +1,17 @@ package org.wordpress.android.ui.reader; public class ReaderConstants { - public static final int READER_MAX_POSTS_TO_REQUEST = 20; // max # posts to request when updating posts - public static final int READER_MAX_POSTS_TO_DISPLAY = 200; // max # posts to display - public static final int READER_MAX_COMMENTS_TO_REQUEST = 20; // max # top-level comments to request when updating comments - public static final int READER_MAX_USERS_TO_DISPLAY = 500; // max # users to show in ReaderUserListActivity - public static final long READER_AUTO_UPDATE_DELAY_MINUTES = 10; // 10 minute delay between automatic updates - public static final int READER_MAX_RECOMMENDED_TO_REQUEST = 20; // max # of recommended blogs to request + public static final int READER_MAX_POSTS_TO_REQUEST = 20; // max # posts to request when updating posts + public static final int READER_MAX_SEARCH_POSTS_TO_REQUEST = 10; // max # posts to request when searching posts + public static final int READER_MAX_POSTS_TO_DISPLAY = 200; // max # posts to display + public static final int READER_MAX_COMMENTS_TO_REQUEST = 20; // max # top-level comments to request when updating comments + public static final int READER_MAX_USERS_TO_DISPLAY = 500; // max # users to show in ReaderUserListActivity + public static final long READER_AUTO_UPDATE_DELAY_MINUTES = 10; // 10 minute delay between automatic updates + public static final int READER_MAX_RECOMMENDED_TO_REQUEST = 20; // max # of recommended blogs to request - public static final int MIN_FEATURED_IMAGE_WIDTH = 640; // min width for an image to be suitable featured image + public static final int MIN_FEATURED_IMAGE_WIDTH = 640; // min width for an image to be suitable featured image - public static final String HTTP_REFERER_URL = "https://wordpress.com"; // referrer url for reader posts opened in a browser + public static final String HTTP_REFERER_URL = "https://wordpress.com"; // referrer url for reader posts opened in a browser // intent arguments / keys static final String ARG_TAG = "tag"; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index 14637f186c9f..7ff233e505dd 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -76,7 +76,7 @@ private void startSearch(final String query, int offset) { // TODO: verify that &meta param is honored String path = "read/search?q=" + UrlUtils.urlEncode(query) - + "&number=" + ReaderConstants.READER_MAX_POSTS_TO_REQUEST + + "&number=" + ReaderConstants.READER_MAX_SEARCH_POSTS_TO_REQUEST + "&meta=site,likes"; if (offset > 0) { path += "&offset=" + offset; From b543159a4572556c1694e9619274bb6ba0f6ea23 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 16:14:42 -0400 Subject: [PATCH 12/44] Set the autocomplete search threshold to 1 --- .../android/ui/reader/ReaderPostListFragment.java | 11 ++++++++++- .../adapters/ReaderSearchSuggestionAdapter.java | 9 ++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 29b49c983c75..174ff45e08a3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -19,6 +19,7 @@ import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.AdapterView; +import android.widget.AutoCompleteTextView; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; @@ -592,7 +593,7 @@ private void showSearchUI() { if (mSearchSuggestionAdapter == null) { setupSearchSuggestions(); } else { - mSearchSuggestionAdapter.populate(); + mSearchSuggestionAdapter.populate(null); } // hide the recycler (post list) @@ -664,6 +665,14 @@ public boolean onSuggestionClick(int position) { return true; } }); + + // this is hacky, but we want to change the SearchView's autocomplete to show suggestions + // after a single character is typed, and there's no less hacky way to do this... + View view = mSearchView.findViewById(android.support.v7.appcompat.R.id.search_src_text); + if (view instanceof AutoCompleteTextView) { + AutoCompleteTextView autoText = (AutoCompleteTextView) view; + autoText.setThreshold(1); + } } /* diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java index 32ca51d8702c..bec5c598518b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java @@ -18,13 +18,12 @@ public ReaderSearchSuggestionAdapter(Context context) { new String[]{"query"}, new int[]{android.R.id.text1}, 0); - populate(); - } - - public void populate() { - populate(null); } + /* + * populates the suggestion list with previous suggestions with the passed filter applied - pass + * null for the filter to show all suggestions + */ public void populate(String filter) { mSearchSuggestions = ReaderSearchTable.getQueryStrings(filter); MatrixCursor cursor = new MatrixCursor(new String[]{"_id", "query"}); From 60a7fb2f1dddbd2c0e65c36e52f72307f427c735 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 16:37:06 -0400 Subject: [PATCH 13/44] Move setThreshold() to initial setup --- .../android/ui/reader/ReaderPostListFragment.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 174ff45e08a3..03cacca9be74 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -550,6 +550,13 @@ public boolean onMenuItemClick(MenuItem item) { mSearchView.setIconifiedByDefault(true); mSearchView.setIconified(true); + // this is hacky, but we want to change the SearchView's autocomplete to show suggestions + // after a single character is typed, and there's no less hacky way to do this... + View view = mSearchView.findViewById(android.support.v7.appcompat.R.id.search_src_text); + if (view instanceof AutoCompleteTextView) { + ((AutoCompleteTextView) view).setThreshold(1); + } + MenuItemCompat.setOnActionExpandListener(mSearchMenuItem, new MenuItemCompat.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { @@ -665,14 +672,6 @@ public boolean onSuggestionClick(int position) { return true; } }); - - // this is hacky, but we want to change the SearchView's autocomplete to show suggestions - // after a single character is typed, and there's no less hacky way to do this... - View view = mSearchView.findViewById(android.support.v7.appcompat.R.id.search_src_text); - if (view instanceof AutoCompleteTextView) { - AutoCompleteTextView autoText = (AutoCompleteTextView) view; - autoText.setThreshold(1); - } } /* From dcbfb3ead1c19ebba11626b59991007fd1160028 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 16:46:50 -0400 Subject: [PATCH 14/44] Show boxes & pages anim when searching --- .../wordpress/android/ui/reader/ReaderPostListFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 03cacca9be74..5270443b4ceb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -783,10 +783,10 @@ public void onClick(View v) { } /* - * box/pages animation that appears when loading an empty list (only appears for tags) + * box/pages animation that appears when loading an empty list */ private boolean shouldShowBoxAndPagesAnimation() { - return getPostListType().isTagType(); + return getPostListType().isTagType() || getPostListType() == ReaderPostListType.SEARCH_RESULTS; } private void startBoxAndPagesAnimation() { From 87840c5b5a42fccd5daf125a986769db5c2235c0 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 18:41:32 -0400 Subject: [PATCH 15/44] Retain search activity title upon rotation --- .../wordpress/android/ui/reader/ReaderPostListActivity.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListActivity.java index 36b44f9edb67..8a226e536bdc 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListActivity.java @@ -8,7 +8,6 @@ import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; -import android.text.TextUtils; import android.view.MenuItem; import org.wordpress.android.R; @@ -61,8 +60,8 @@ public void onCreate(Bundle savedInstanceState) { } } else if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { String query = getIntent().getStringExtra(ReaderConstants.ARG_SEARCH_QUERY); - if (!TextUtils.isEmpty(query) && savedInstanceState == null) { - setTitle(String.format(getString(R.string.reader_title_search_results), query)); + setTitle(String.format(getString(R.string.reader_title_search_results), query)); + if (savedInstanceState == null) { showListFragmentForSearch(query); } } From 9e5aaf5a9ad841a59e6a4bfc5eaf6b688f1a167a Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 13 May 2016 19:10:43 -0400 Subject: [PATCH 16/44] No longer removing previous search results --- .../wordpress/android/ui/reader/ReaderActivityLauncher.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java index c59aab5248b2..aaae767806e5 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java @@ -14,7 +14,6 @@ import org.wordpress.android.R; import org.wordpress.android.analytics.AnalyticsTracker; -import org.wordpress.android.datasets.ReaderPostTable; import org.wordpress.android.datasets.ReaderSearchTable; import org.wordpress.android.models.AccountHelper; import org.wordpress.android.models.ReaderComment; @@ -23,7 +22,6 @@ import org.wordpress.android.ui.ActivityLauncher; import org.wordpress.android.ui.WPWebViewActivity; import org.wordpress.android.ui.reader.ReaderTypes.ReaderPostListType; -import org.wordpress.android.ui.reader.services.ReaderSearchService; import org.wordpress.android.util.AnalyticsUtils; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.UrlUtils; @@ -143,9 +141,6 @@ public static void showReaderSearchResults(Context context, String query) { // record this search query ReaderSearchTable.addOrUpdateQueryString(query); - // delete existing results for this query - ReaderPostTable.deletePostsWithTag(ReaderSearchService.getTagForSearchQuery(query)); - // TODO: track analytics //AnalyticsTracker.track(AnalyticsTracker.Stat.???); From c3585661ab41850b1294b5c1a4859898e9e857db Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sat, 14 May 2016 07:58:11 -0400 Subject: [PATCH 17/44] Use custom layout for search suggestions --- .../adapters/ReaderSearchSuggestionAdapter.java | 5 +++-- .../res/layout/reader_listitem_suggestion.xml | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 WordPress/src/main/res/layout/reader_listitem_suggestion.xml diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java index bec5c598518b..e859ab7f4754 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java @@ -4,6 +4,7 @@ import android.database.MatrixCursor; import android.support.v4.widget.SimpleCursorAdapter; +import org.wordpress.android.R; import org.wordpress.android.datasets.ReaderSearchTable; import java.util.List; @@ -13,7 +14,7 @@ public class ReaderSearchSuggestionAdapter extends SimpleCursorAdapter { public ReaderSearchSuggestionAdapter(Context context) { super(context, - android.R.layout.simple_list_item_1, + R.layout.reader_listitem_suggestion, null, new String[]{"query"}, new int[]{android.R.id.text1}, @@ -21,7 +22,7 @@ public ReaderSearchSuggestionAdapter(Context context) { } /* - * populates the suggestion list with previous suggestions with the passed filter applied - pass + * populates the suggestion list from previous suggestions with the passed filter applied - pass * null for the filter to show all suggestions */ public void populate(String filter) { diff --git a/WordPress/src/main/res/layout/reader_listitem_suggestion.xml b/WordPress/src/main/res/layout/reader_listitem_suggestion.xml new file mode 100644 index 000000000000..261078ac752b --- /dev/null +++ b/WordPress/src/main/res/layout/reader_listitem_suggestion.xml @@ -0,0 +1,15 @@ + + + \ No newline at end of file From 3f687aaa45b0124ccc1ba42873cc68314f0f1e5d Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sat, 14 May 2016 18:06:59 -0400 Subject: [PATCH 18/44] Apply max suggestions --- .../android/datasets/ReaderSearchTable.java | 20 ++++++++++++------- .../ui/reader/ReaderPostListFragment.java | 10 ++++++++-- .../ReaderSearchSuggestionAdapter.java | 3 ++- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java index c8b6f10c7013..625ee9e0b128 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java @@ -54,19 +54,25 @@ private static int getCounterForQueryString(@NonNull String query) { "SELECT counter FROM tbl_search_history WHERE query_string=?", args); } - public static List getQueryStrings(String filter) { - List queries = new ArrayList<>(); - Cursor cursor; + public static List getQueryStrings(String filter, int max) { + String sql; + String[] args; if (TextUtils.isEmpty(filter)) { - cursor = ReaderDatabase.getReadableDb().rawQuery( - "SELECT query_string FROM tbl_search_history ORDER BY date_used DESC", null); + sql = "SELECT query_string FROM tbl_search_history ORDER BY date_used DESC"; + args = null; } else { + sql = "SELECT query_string FROM tbl_search_history WHERE query_string LIKE ? ORDER BY date_used DESC"; String likeFilter = filter + "%"; - cursor = ReaderDatabase.getReadableDb().rawQuery( - "SELECT query_string FROM tbl_search_history WHERE query_string LIKE ? ORDER BY date_used DESC", new String[]{likeFilter}); + args = new String[]{likeFilter}; } + if (max > 0) { + sql += " LIMIT " + max; + } + + Cursor cursor = ReaderDatabase.getReadableDb().rawQuery(sql, args); try { + List queries = new ArrayList<>(); while (cursor.moveToNext()) { queries.add(cursor.getString(0)); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 5270443b4ceb..58d7b2749a22 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -574,8 +574,7 @@ public boolean onMenuItemActionCollapse(MenuItem item) { mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { - ReaderActivityLauncher.showReaderSearchResults(getActivity(), query); - mSearchMenuItem.collapseActionView(); + submitSearchQuery(query); return true; } @@ -588,6 +587,13 @@ public boolean onQueryTextChange(String newText) { ); } + private void submitSearchQuery(@NonNull String query) { + if (!isAdded()) return; + + ReaderActivityLauncher.showReaderSearchResults(getActivity(), query); + mSearchMenuItem.collapseActionView(); + } + private void showSearchUI() { if (!isAdded()) return; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java index e859ab7f4754..072299e473f9 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java @@ -11,6 +11,7 @@ public class ReaderSearchSuggestionAdapter extends SimpleCursorAdapter { private List mSearchSuggestions; + private static final int MAX_SUGGESTIONS = 5; public ReaderSearchSuggestionAdapter(Context context) { super(context, @@ -26,7 +27,7 @@ public ReaderSearchSuggestionAdapter(Context context) { * null for the filter to show all suggestions */ public void populate(String filter) { - mSearchSuggestions = ReaderSearchTable.getQueryStrings(filter); + mSearchSuggestions = ReaderSearchTable.getQueryStrings(filter, MAX_SUGGESTIONS); MatrixCursor cursor = new MatrixCursor(new String[]{"_id", "query"}); int id = 0; From fef6b5c8cccfd3dede0e73e34867944d0c242fd7 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sat, 14 May 2016 19:19:52 -0400 Subject: [PATCH 19/44] Clean up getQueryStrings --- .../android/datasets/ReaderSearchTable.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java index 625ee9e0b128..91766e6cc538 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java @@ -54,18 +54,24 @@ private static int getCounterForQueryString(@NonNull String query) { "SELECT counter FROM tbl_search_history WHERE query_string=?", args); } + /** + * Returns a list of query strings previously typed by the user + * @param filter - filters the list using LIKE syntax (pass null for no filter) + * @param max - limit the list to this many items (pass zero for no limit) + */ public static List getQueryStrings(String filter, int max) { String sql; String[] args; if (TextUtils.isEmpty(filter)) { - sql = "SELECT query_string FROM tbl_search_history ORDER BY date_used DESC"; + sql = "SELECT query_string FROM tbl_search_history"; args = null; } else { - sql = "SELECT query_string FROM tbl_search_history WHERE query_string LIKE ? ORDER BY date_used DESC"; - String likeFilter = filter + "%"; - args = new String[]{likeFilter}; + sql = "SELECT query_string FROM tbl_search_history WHERE query_string LIKE ?"; + args = new String[]{filter + "%"}; } + sql += " ORDER BY date_used DESC"; + if (max > 0) { sql += " LIMIT " + max; } From f5696bdea2939de7da7ec3fbcb10f90e095a9a53 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sat, 14 May 2016 19:54:09 -0400 Subject: [PATCH 20/44] First pass at infinite scroll for search --- .../android/ui/reader/ReaderPostListFragment.java | 15 ++++++++++++++- .../ui/reader/services/ReaderSearchService.java | 3 --- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 58d7b2749a22..17eb08c4aacb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -339,7 +339,7 @@ private void checkAdapter() { } else if (getPostListType() == ReaderPostListType.BLOG_PREVIEW) { updatePostsInCurrentBlogOrFeed(UpdateAction.REQUEST_NEWER); } else if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { - ReaderSearchService.startService(getActivity(), mCurrentSearchQuery); + updatePostsInCurrentSearch(0); } } } @@ -587,6 +587,10 @@ public boolean onQueryTextChange(String newText) { ); } + private void updatePostsInCurrentSearch(int offset) { + ReaderSearchService.startService(getActivity(), mCurrentSearchQuery, offset); + } + private void submitSearchQuery(@NonNull String query) { if (!isAdded()) return; @@ -928,6 +932,15 @@ public void onRequestData() { AnalyticsTracker.track(AnalyticsTracker.Stat.READER_INFINITE_SCROLL); } break; + + case SEARCH_RESULTS: + ReaderTag searchTag = ReaderSearchService.getTagForSearchQuery(mCurrentSearchQuery); + int offset = ReaderPostTable.getNumPostsWithTag(searchTag); + if (offset < ReaderConstants.READER_MAX_POSTS_TO_DISPLAY) { + updatePostsInCurrentSearch(offset); + AnalyticsTracker.track(AnalyticsTracker.Stat.READER_INFINITE_SCROLL); + } + break; } } }; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index 7ff233e505dd..1ab41cf86701 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -32,9 +32,6 @@ public class ReaderSearchService extends Service { private static final String ARG_QUERY = "query"; private static final String ARG_OFFSET = "offset"; - public static void startService(Context context, @NonNull String query) { - startService(context, query, 0); - } public static void startService(Context context, @NonNull String query, int offset) { Intent intent = new Intent(context, ReaderSearchService.class); intent.putExtra(ARG_QUERY, query); From fd358778bae15e3f06e5b7b8512e379db2df3eba Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sat, 14 May 2016 20:07:04 -0400 Subject: [PATCH 21/44] Second pass at infinite scroll for search --- .../android/ui/reader/ReaderActivityLauncher.java | 6 ++++++ .../org/wordpress/android/ui/reader/ReaderEvents.java | 8 ++++---- .../ui/reader/services/ReaderSearchService.java | 11 +++++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java index aaae767806e5..8a5487e7dd61 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java @@ -14,6 +14,7 @@ import org.wordpress.android.R; import org.wordpress.android.analytics.AnalyticsTracker; +import org.wordpress.android.datasets.ReaderPostTable; import org.wordpress.android.datasets.ReaderSearchTable; import org.wordpress.android.models.AccountHelper; import org.wordpress.android.models.ReaderComment; @@ -22,6 +23,7 @@ import org.wordpress.android.ui.ActivityLauncher; import org.wordpress.android.ui.WPWebViewActivity; import org.wordpress.android.ui.reader.ReaderTypes.ReaderPostListType; +import org.wordpress.android.ui.reader.services.ReaderSearchService; import org.wordpress.android.util.AnalyticsUtils; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.UrlUtils; @@ -141,6 +143,10 @@ public static void showReaderSearchResults(Context context, String query) { // record this search query ReaderSearchTable.addOrUpdateQueryString(query); + // delete existing results + ReaderTag searchTag = ReaderSearchService.getTagForSearchQuery(query); + ReaderPostTable.deletePostsWithTag(searchTag); + // TODO: track analytics //AnalyticsTracker.track(AnalyticsTracker.Stat.???); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java index 4fc06241897c..1bdebb4302fa 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java @@ -76,13 +76,13 @@ public String getQuery() { } public static class SearchPostsEnded { private final String mQuery; - private final int mNumResults; - public SearchPostsEnded(@NonNull String query, int numResults) { + private final boolean mHasResults; + public SearchPostsEnded(@NonNull String query, boolean hasResults) { mQuery = query; - mNumResults = numResults; + mHasResults = hasResults; } public boolean hasResults() { - return mNumResults > 0; + return mHasResults; } public String getQuery() { return mQuery; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index 1ab41cf86701..7590639cbd2c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -85,7 +85,7 @@ public void onResponse(JSONObject jsonObject) { if (jsonObject != null) { handleSearchResponse(query, jsonObject); } else { - EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, 0)); + EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, false)); } } }; @@ -93,7 +93,7 @@ public void onResponse(JSONObject jsonObject) { @Override public void onErrorResponse(VolleyError volleyError) { AppLog.e(AppLog.T.READER, volleyError); - EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, 0)); + EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, false)); } }; @@ -107,8 +107,11 @@ private static void handleSearchResponse(final String query, final JSONObject js @Override public void run() { ReaderPostList serverPosts = ReaderPostList.fromJson(jsonObject); - ReaderPostTable.addOrUpdatePosts(getTagForSearchQuery(query), serverPosts); - EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, serverPosts.size())); + boolean hasResults = ReaderPostTable.comparePosts(serverPosts).isNewOrChanged(); + if (hasResults) { + ReaderPostTable.addOrUpdatePosts(getTagForSearchQuery(query), serverPosts); + } + EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, hasResults)); } }.start(); } From 1dfe743aef4c6d22bfb3bbd91a84af70cb8511d3 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sat, 14 May 2016 20:17:19 -0400 Subject: [PATCH 22/44] Third pass at infinite scroll for search --- .../wordpress/android/ui/reader/ReaderEvents.java | 14 ++++++++++++-- .../android/ui/reader/ReaderPostListFragment.java | 10 ++++------ .../ui/reader/services/ReaderSearchService.java | 15 +++++++-------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java index 1bdebb4302fa..fc27d404f370 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderEvents.java @@ -67,18 +67,25 @@ public ReaderPostService.UpdateAction getAction() { public static class SearchPostsStarted { private final String mQuery; - public SearchPostsStarted(@NonNull String query) { + private final int mOffset; + public SearchPostsStarted(@NonNull String query, int offset) { mQuery = query; + mOffset = offset; } public String getQuery() { return mQuery; } + public int getOffset() { + return mOffset; + } } public static class SearchPostsEnded { private final String mQuery; private final boolean mHasResults; - public SearchPostsEnded(@NonNull String query, boolean hasResults) { + private final int mOffset; + public SearchPostsEnded(@NonNull String query, int offset, boolean hasResults) { mQuery = query; + mOffset = offset; mHasResults = hasResults; } public boolean hasResults() { @@ -87,6 +94,9 @@ public boolean hasResults() { public String getQuery() { return mQuery; } + public int getOffset() { + return mOffset; + } } public static class UpdateCommentsStarted {} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 17eb08c4aacb..eb3c7c6a200b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -695,7 +695,8 @@ private boolean isSearchViewExpanded() { public void onEventMainThread(ReaderEvents.SearchPostsStarted event) { if (!isAdded()) return; - setIsUpdating(true); + UpdateAction updateAction = event.getOffset() == 0 ? UpdateAction.REQUEST_NEWER : UpdateAction.REQUEST_OLDER; + setIsUpdating(true, updateAction); } @SuppressWarnings("unused") @@ -708,7 +709,8 @@ public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { return; } - setIsUpdating(false); + UpdateAction updateAction = event.getOffset() == 0 ? UpdateAction.REQUEST_NEWER : UpdateAction.REQUEST_OLDER; + setIsUpdating(false, updateAction); if (event.hasResults()) { // this will load the search results @@ -1231,10 +1233,6 @@ private void showLoadingProgress(boolean showProgress) { } } - private void setIsUpdating(boolean isUpdating) { - setIsUpdating(isUpdating, UpdateAction.REQUEST_NEWER); - } - private void setIsUpdating(boolean isUpdating, UpdateAction updateAction) { if (!isAdded() || mIsUpdating == isUpdating) { return; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index 7590639cbd2c..15d6322f6ee2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -69,8 +69,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { return START_NOT_STICKY; } - private void startSearch(final String query, int offset) { - // TODO: verify that &meta param is honored + private void startSearch(final String query, final int offset) { String path = "read/search?q=" + UrlUtils.urlEncode(query) + "&number=" + ReaderConstants.READER_MAX_SEARCH_POSTS_TO_REQUEST @@ -83,9 +82,9 @@ private void startSearch(final String query, int offset) { @Override public void onResponse(JSONObject jsonObject) { if (jsonObject != null) { - handleSearchResponse(query, jsonObject); + handleSearchResponse(query, offset, jsonObject); } else { - EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, false)); + EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, offset, false)); } } }; @@ -93,16 +92,16 @@ public void onResponse(JSONObject jsonObject) { @Override public void onErrorResponse(VolleyError volleyError) { AppLog.e(AppLog.T.READER, volleyError); - EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, false)); + EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, offset, false)); } }; AppLog.d(AppLog.T.READER, "reader search service > starting search for " + query); - EventBus.getDefault().post(new ReaderEvents.SearchPostsStarted(query)); + EventBus.getDefault().post(new ReaderEvents.SearchPostsStarted(query, offset)); WordPress.getRestClientUtilsV1_2().get(path, null, null, listener, errorListener); } - private static void handleSearchResponse(final String query, final JSONObject jsonObject) { + private static void handleSearchResponse(final String query, final int offset, final JSONObject jsonObject) { new Thread() { @Override public void run() { @@ -111,7 +110,7 @@ public void run() { if (hasResults) { ReaderPostTable.addOrUpdatePosts(getTagForSearchQuery(query), serverPosts); } - EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, hasResults)); + EventBus.getDefault().post(new ReaderEvents.SearchPostsEnded(query, offset, hasResults)); } }.start(); } From bd3551fdb89ebb1e7efe13d1d50036a59d4d0d7e Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sat, 14 May 2016 20:36:38 -0400 Subject: [PATCH 23/44] Parse score from search results to determine sort order --- .../main/java/org/wordpress/android/models/ReaderPost.java | 6 ++++++ .../wordpress/android/ui/reader/ReaderPostListFragment.java | 3 +-- .../android/ui/reader/adapters/ReaderPostAdapter.java | 1 - .../android/ui/reader/services/ReaderSearchService.java | 4 +--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java index 550aba98ea8c..656a3ca53944 100644 --- a/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java +++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java @@ -126,6 +126,12 @@ public static ReaderPost fromJson(JSONObject json) { post.timestamp = DateTimeUtils.iso8601ToTimestamp(post.published); } + // search results include a "score" that should be used for sorting + if (json.has("score")) { + double score = json.optDouble("score"); + post.timestamp = Math.round(score); + } + // if the post is untitled, make up a title from the excerpt if (!post.hasTitle() && post.hasExcerpt()) { post.title = extractTitle(post.excerpt, 50); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index eb3c7c6a200b..cd1c454f3a07 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -713,8 +713,7 @@ public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { setIsUpdating(false, updateAction); if (event.hasResults()) { - // this will load the search results - getPostAdapter().setCurrentSearchQuery(event.getQuery()); + getPostAdapter().refresh(); } else { mDataLoadedListener.onDataLoaded(true); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java index 2131e1afaf65..e4f56e3f799c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java @@ -585,7 +585,6 @@ public void setCurrentBlogAndFeed(long blogId, long feedId) { public void setCurrentSearchQuery(@NonNull String query) { mCurrentSearchQuery = query; - reload(); } public void clear() { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index 15d6322f6ee2..b2f738bcded0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -73,10 +73,8 @@ private void startSearch(final String query, final int offset) { String path = "read/search?q=" + UrlUtils.urlEncode(query) + "&number=" + ReaderConstants.READER_MAX_SEARCH_POSTS_TO_REQUEST + + "&offset=" + offset + "&meta=site,likes"; - if (offset > 0) { - path += "&offset=" + offset; - } RestRequest.Listener listener = new RestRequest.Listener() { @Override From 4367231b0a421759cae1a5e2b4ff2878e31cbaea Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sat, 14 May 2016 20:46:53 -0400 Subject: [PATCH 24/44] Updated comment about timestamps --- .../wordpress/android/models/ReaderPost.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java index 656a3ca53944..11e8ac2aa480 100644 --- a/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java +++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java @@ -116,20 +116,20 @@ public static ReaderPost fromJson(JSONObject json) { post.blogName = JSONUtils.getStringDecoded(json, "site_name"); post.published = JSONUtils.getString(json, "date"); - // the date a post was liked is only returned by the read/liked/ endpoint - if this exists, - // set it as the timestamp so posts are sorted by the date they were liked rather than the - // date they were published (the timestamp is used to sort posts when querying) - String likeDate = JSONUtils.getString(json, "date_liked"); - if (!TextUtils.isEmpty(likeDate)) { - post.timestamp = DateTimeUtils.iso8601ToTimestamp(likeDate); - } else { - post.timestamp = DateTimeUtils.iso8601ToTimestamp(post.published); - } - - // search results include a "score" that should be used for sorting + // a post's timestamp determines its sort order if (json.has("score")) { + // search results include a "score" that should be used for sorting double score = json.optDouble("score"); post.timestamp = Math.round(score); + } else { + // liked posts should be sorted by the date they were liked, otherwise sort by the + // published date + String likeDate = JSONUtils.getString(json, "date_liked"); + if (!TextUtils.isEmpty(likeDate)) { + post.timestamp = DateTimeUtils.iso8601ToTimestamp(likeDate); + } else { + post.timestamp = DateTimeUtils.iso8601ToTimestamp(post.published); + } } // if the post is untitled, make up a title from the excerpt From 7d529c4d9a5c870456fb85039617dad09c25fdd0 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sat, 14 May 2016 20:54:38 -0400 Subject: [PATCH 25/44] Change post timestamp to REAL (double) --- .../java/org/wordpress/android/datasets/ReaderDatabase.java | 3 ++- .../org/wordpress/android/datasets/ReaderPostTable.java | 6 +++--- .../main/java/org/wordpress/android/models/ReaderPost.java | 5 ++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderDatabase.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderDatabase.java index 2158691e9f28..6efbe3d9e07e 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderDatabase.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderDatabase.java @@ -19,7 +19,7 @@ */ public class ReaderDatabase extends SQLiteOpenHelper { protected static final String DB_NAME = "wpreader.db"; - private static final int DB_VERSION = 116; + private static final int DB_VERSION = 117; /* * version history @@ -68,6 +68,7 @@ public class ReaderDatabase extends SQLiteOpenHelper { * 114 - renamed tag_name to tag_slug in tag tables * 115 - added ReaderSearchTable * 116 - added tag_display_name to tag tables + * 117 - changed tbl_posts.timestamp from INTEGER to REAL */ /* diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderPostTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderPostTable.java index 60732bfd845e..7ca98ea61249 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderPostTable.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderPostTable.java @@ -122,7 +122,7 @@ protected static void createTables(SQLiteDatabase db) { + " featured_image TEXT," + " featured_video TEXT," + " post_avatar TEXT," - + " timestamp INTEGER DEFAULT 0," + + " timestamp REAL DEFAULT 0," + " published TEXT," + " num_replies INTEGER DEFAULT 0," + " num_likes INTEGER DEFAULT 0," @@ -624,7 +624,7 @@ public static void addOrUpdatePosts(final ReaderTag tag, ReaderPostList posts) { stmtPosts.bindString(16, post.getFeaturedImage()); stmtPosts.bindString(17, post.getFeaturedVideo()); stmtPosts.bindString(18, post.getPostAvatar()); - stmtPosts.bindLong (19, post.timestamp); + stmtPosts.bindDouble(19, post.timestamp); stmtPosts.bindString(20, post.getPublished()); stmtPosts.bindLong (21, post.numReplies); stmtPosts.bindLong (22, post.numLikes); @@ -839,7 +839,7 @@ private static ReaderPost getPostFromCursor(Cursor c) { post.setShortUrl(c.getString(c.getColumnIndex("short_url"))); post.setPostAvatar(c.getString(c.getColumnIndex("post_avatar"))); - post.timestamp = c.getLong(c.getColumnIndex("timestamp")); + post.timestamp = c.getDouble(c.getColumnIndex("timestamp")); post.setPublished(c.getString(c.getColumnIndex("published"))); post.numReplies = c.getInt(c.getColumnIndex("num_replies")); diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java index 11e8ac2aa480..72907cdb3597 100644 --- a/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java +++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java @@ -39,7 +39,7 @@ public class ReaderPost { private String primaryTag; // most popular tag on this post based on usage in blog private String secondaryTag; // second most popular tag on this post based on usage in blog - public long timestamp; // used for sorting + public double timestamp; // used for sorting private String published; private String url; @@ -119,8 +119,7 @@ public static ReaderPost fromJson(JSONObject json) { // a post's timestamp determines its sort order if (json.has("score")) { // search results include a "score" that should be used for sorting - double score = json.optDouble("score"); - post.timestamp = Math.round(score); + post.timestamp = json.optDouble("score"); } else { // liked posts should be sorted by the date they were liked, otherwise sort by the // published date From 132fe188367f2b131cc3271e0a69cbe6f75001df Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 10:25:30 -0400 Subject: [PATCH 26/44] First pass at showing search results in main reader (no separate activity) --- .../ui/reader/ReaderActivityLauncher.java | 23 ------------ .../ui/reader/ReaderPostListFragment.java | 36 ++++++++++++++++--- .../reader/services/ReaderSearchService.java | 4 +++ .../wordpress/android/util/EditTextUtils.java | 5 +-- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java index 8a5487e7dd61..1ec2519d697f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java @@ -14,8 +14,6 @@ import org.wordpress.android.R; import org.wordpress.android.analytics.AnalyticsTracker; -import org.wordpress.android.datasets.ReaderPostTable; -import org.wordpress.android.datasets.ReaderSearchTable; import org.wordpress.android.models.AccountHelper; import org.wordpress.android.models.ReaderComment; import org.wordpress.android.models.ReaderPost; @@ -23,7 +21,6 @@ import org.wordpress.android.ui.ActivityLauncher; import org.wordpress.android.ui.WPWebViewActivity; import org.wordpress.android.ui.reader.ReaderTypes.ReaderPostListType; -import org.wordpress.android.ui.reader.services.ReaderSearchService; import org.wordpress.android.util.AnalyticsUtils; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.UrlUtils; @@ -137,26 +134,6 @@ public static void showReaderTagPreview(Context context, ReaderTag tag) { context.startActivity(intent); } - public static void showReaderSearchResults(Context context, String query) { - if (TextUtils.isEmpty(query)) return; - - // record this search query - ReaderSearchTable.addOrUpdateQueryString(query); - - // delete existing results - ReaderTag searchTag = ReaderSearchService.getTagForSearchQuery(query); - ReaderPostTable.deletePostsWithTag(searchTag); - - // TODO: track analytics - //AnalyticsTracker.track(AnalyticsTracker.Stat.???); - - Intent intent = new Intent(context, ReaderPostListActivity.class); - intent.putExtra(ReaderConstants.ARG_SEARCH_QUERY, query); - intent.putExtra(ReaderConstants.ARG_POST_LIST_TYPE, ReaderPostListType.SEARCH_RESULTS); - context.startActivity(intent); - } - - /* * show comments for the passed Ids */ diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index cd1c454f3a07..a7d4b5719dae 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -60,6 +60,7 @@ import org.wordpress.android.util.AppLog.T; import org.wordpress.android.util.DateTimeUtils; import org.wordpress.android.util.DisplayUtils; +import org.wordpress.android.util.EditTextUtils; import org.wordpress.android.util.NetworkUtils; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.WPActivityUtils; @@ -268,7 +269,7 @@ public void onPause() { @Override public void onResume() { super.onResume(); - checkAdapter(); + checkPostAdapter(); if (mWasPaused) { AppLog.d(T.READER, "reader post list > resumed from paused state"); @@ -328,7 +329,7 @@ public void onStop() { /* * ensures the adapter is created and posts are updated if they haven't already been */ - private void checkAdapter() { + private void checkPostAdapter() { if (isAdded() && mRecyclerView.getAdapter() == null) { mRecyclerView.setAdapter(getPostAdapter()); @@ -345,6 +346,14 @@ private void checkAdapter() { } } + private void resetPostAdapter(ReaderPostListType postListType) { + mPostListType = postListType; + mPostAdapter = null; + mRecyclerView.setAdapter(null); + mHasUpdatedPosts = false; + checkPostAdapter(); + } + @SuppressWarnings("unused") public void onEventMainThread(ReaderEvents.FollowedTagsChanged event) { if (getPostListType() == ReaderPostListType.TAG_FOLLOWED) { @@ -505,7 +514,8 @@ public void onShowCustomEmptyView (EmptyViewMessageType emptyViewMsgType) { getResources().getDimensionPixelSize(R.dimen.margin_extra_large) + spacingHorizontal); // add a menu to the filtered recycler's toolbar - if (!ReaderUtils.isLoggedOutReader() && getPostListType() == ReaderPostListType.TAG_FOLLOWED) { + if (!ReaderUtils.isLoggedOutReader() + && (getPostListType() == ReaderPostListType.TAG_FOLLOWED || getPostListType() == ReaderPostListType.SEARCH_RESULTS)) { setupRecyclerToolbar(); } @@ -581,6 +591,9 @@ public boolean onQueryTextSubmit(String query) { @Override public boolean onQueryTextChange(String newText) { mSearchSuggestionAdapter.populate(newText); + if (TextUtils.isEmpty(newText)) { + getPostAdapter().clear(); + } return true; } } @@ -594,8 +607,18 @@ private void updatePostsInCurrentSearch(int offset) { private void submitSearchQuery(@NonNull String query) { if (!isAdded()) return; - ReaderActivityLauncher.showReaderSearchResults(getActivity(), query); - mSearchMenuItem.collapseActionView(); + //mSearchMenuItem.collapseActionView(); + EditTextUtils.hideSoftInput(mSearchView); + hideSearchMessage(); + + // make sure the recycler is showing again + RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); + if (recycler != null && recycler.getVisibility() != View.VISIBLE) { + AniUtils.fadeIn(recycler, AniUtils.Duration.LONG); + } + + mCurrentSearchQuery = query; + resetPostAdapter(ReaderPostListType.SEARCH_RESULTS); } private void showSearchUI() { @@ -626,6 +649,9 @@ private void hideSearchUI() { mSettingsMenuItem.setVisible(true); hideSearchMessage(); + mCurrentSearchQuery = null; + resetPostAdapter(ReaderPostListType.TAG_FOLLOWED); + // show the recycler again RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); if (recycler != null && recycler.getVisibility() != View.VISIBLE) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index b2f738bcded0..f136e450f8e6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -70,6 +70,10 @@ public int onStartCommand(Intent intent, int flags, int startId) { } private void startSearch(final String query, final int offset) { + if (offset == 0) { + ReaderPostTable.deletePostsWithTag(getTagForSearchQuery(query)); + } + String path = "read/search?q=" + UrlUtils.urlEncode(query) + "&number=" + ReaderConstants.READER_MAX_SEARCH_POSTS_TO_REQUEST diff --git a/libs/utils/WordPressUtils/src/main/java/org/wordpress/android/util/EditTextUtils.java b/libs/utils/WordPressUtils/src/main/java/org/wordpress/android/util/EditTextUtils.java index 66a0c77fdf8f..3d6d72eec75b 100644 --- a/libs/utils/WordPressUtils/src/main/java/org/wordpress/android/util/EditTextUtils.java +++ b/libs/utils/WordPressUtils/src/main/java/org/wordpress/android/util/EditTextUtils.java @@ -2,6 +2,7 @@ import android.content.Context; import android.text.TextUtils; +import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; @@ -41,7 +42,7 @@ public static boolean isEmpty(EditText edit) { /** * hide the soft keyboard for the passed EditText */ - public static void hideSoftInput(EditText edit) { + public static void hideSoftInput(View edit) { if (edit == null) { return; } @@ -68,7 +69,7 @@ public static void showSoftInput(EditText edit) { } } - private static InputMethodManager getInputMethodManager(EditText edit) { + private static InputMethodManager getInputMethodManager(View edit) { Context context = edit.getContext(); return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); } From db99f1e8e3e5d696c339ad17a6f24a438b843927 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 10:41:36 -0400 Subject: [PATCH 27/44] 2nd pass at showing search results in main reader --- .../ui/reader/ReaderPostListFragment.java | 82 +++++++------------ .../reader/services/ReaderSearchService.java | 1 + .../wordpress/android/util/EditTextUtils.java | 5 +- 3 files changed, 33 insertions(+), 55 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index a7d4b5719dae..8556fb67b4ac 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -60,7 +60,6 @@ import org.wordpress.android.util.AppLog.T; import org.wordpress.android.util.DateTimeUtils; import org.wordpress.android.util.DisplayUtils; -import org.wordpress.android.util.EditTextUtils; import org.wordpress.android.util.NetworkUtils; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.WPActivityUtils; @@ -570,13 +569,20 @@ public boolean onMenuItemClick(MenuItem item) { MenuItemCompat.setOnActionExpandListener(mSearchMenuItem, new MenuItemCompat.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { - showSearchUI(); + mSettingsMenuItem.setVisible(false); + setupSearchSuggestions(); return true; } @Override public boolean onMenuItemActionCollapse(MenuItem item) { - hideSearchUI(); + mSettingsMenuItem.setVisible(true); + hideSearchMessage(); + + mCurrentSearchQuery = null; + mSearchSuggestionAdapter = null; + resetPostAdapter(ReaderPostListType.TAG_FOLLOWED); + return true; } }); @@ -592,7 +598,7 @@ public boolean onQueryTextSubmit(String query) { public boolean onQueryTextChange(String newText) { mSearchSuggestionAdapter.populate(newText); if (TextUtils.isEmpty(newText)) { - getPostAdapter().clear(); + showSearchMessage(); } return true; } @@ -607,58 +613,15 @@ private void updatePostsInCurrentSearch(int offset) { private void submitSearchQuery(@NonNull String query) { if (!isAdded()) return; - //mSearchMenuItem.collapseActionView(); - EditTextUtils.hideSoftInput(mSearchView); + // clearing the focus will hide suggestions and the virtual keyboard + mSearchView.clearFocus(); hideSearchMessage(); - // make sure the recycler is showing again - RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); - if (recycler != null && recycler.getVisibility() != View.VISIBLE) { - AniUtils.fadeIn(recycler, AniUtils.Duration.LONG); - } - + // this will start the search mCurrentSearchQuery = query; resetPostAdapter(ReaderPostListType.SEARCH_RESULTS); } - private void showSearchUI() { - if (!isAdded()) return; - - // hide settings icon and show message letting user know what they're searching - mSettingsMenuItem.setVisible(false); - showSearchMessage(); - - // create the suggestion adapter if it doesn't already exist, otherwise repopulate it - // so the latest suggestions appear - if (mSearchSuggestionAdapter == null) { - setupSearchSuggestions(); - } else { - mSearchSuggestionAdapter.populate(null); - } - - // hide the recycler (post list) - RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); - if (recycler != null && recycler.getVisibility() == View.VISIBLE) { - AniUtils.fadeOut(recycler, AniUtils.Duration.LONG); - } - } - - private void hideSearchUI() { - if (!isAdded()) return; - - mSettingsMenuItem.setVisible(true); - hideSearchMessage(); - - mCurrentSearchQuery = null; - resetPostAdapter(ReaderPostListType.TAG_FOLLOWED); - - // show the recycler again - RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); - if (recycler != null && recycler.getVisibility() != View.VISIBLE) { - AniUtils.fadeIn(recycler, AniUtils.Duration.LONG); - } - } - /* * show message letting user know what they're querying, but only if the user is in * portrait mode or the device is a tablet (since there's not enough space for the @@ -673,8 +636,17 @@ private void showSearchMessage() { TextView txtSearchMsg = (TextView) getView().findViewById(R.id.text_search_message); if (txtSearchMsg.getVisibility() != View.VISIBLE) { - AniUtils.fadeIn(txtSearchMsg, AniUtils.Duration.LONG); + AniUtils.fadeIn(txtSearchMsg, AniUtils.Duration.MEDIUM); } + + // hide the recycler (post list) so only the above message is visible + RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); + if (recycler != null && recycler.getVisibility() == View.VISIBLE) { + AniUtils.fadeOut(recycler, AniUtils.Duration.MEDIUM); + } + + // make sure the empty view isn't showing + mEmptyView.setVisibility(View.GONE); } private void hideSearchMessage() { @@ -682,7 +654,13 @@ private void hideSearchMessage() { TextView txtSearchMsg = (TextView) getView().findViewById(R.id.text_search_message); if (txtSearchMsg.getVisibility() == View.VISIBLE) { - AniUtils.fadeOut(txtSearchMsg, AniUtils.Duration.LONG); + AniUtils.fadeOut(txtSearchMsg, AniUtils.Duration.MEDIUM); + } + + // make sure the recycler is showing again + RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); + if (recycler != null && recycler.getVisibility() != View.VISIBLE) { + AniUtils.fadeIn(recycler, AniUtils.Duration.MEDIUM); } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index f136e450f8e6..ac85bc127043 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -70,6 +70,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { } private void startSearch(final String query, final int offset) { + // delete existing results if this is the initial search if (offset == 0) { ReaderPostTable.deletePostsWithTag(getTagForSearchQuery(query)); } diff --git a/libs/utils/WordPressUtils/src/main/java/org/wordpress/android/util/EditTextUtils.java b/libs/utils/WordPressUtils/src/main/java/org/wordpress/android/util/EditTextUtils.java index 3d6d72eec75b..66a0c77fdf8f 100644 --- a/libs/utils/WordPressUtils/src/main/java/org/wordpress/android/util/EditTextUtils.java +++ b/libs/utils/WordPressUtils/src/main/java/org/wordpress/android/util/EditTextUtils.java @@ -2,7 +2,6 @@ import android.content.Context; import android.text.TextUtils; -import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; @@ -42,7 +41,7 @@ public static boolean isEmpty(EditText edit) { /** * hide the soft keyboard for the passed EditText */ - public static void hideSoftInput(View edit) { + public static void hideSoftInput(EditText edit) { if (edit == null) { return; } @@ -69,7 +68,7 @@ public static void showSoftInput(EditText edit) { } } - private static InputMethodManager getInputMethodManager(View edit) { + private static InputMethodManager getInputMethodManager(EditText edit) { Context context = edit.getContext(); return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); } From 7112499517bc8816fa8ce96230d672beee92a4b6 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 11:04:08 -0400 Subject: [PATCH 28/44] 3rd pass at showing search results in main reader --- .../ui/reader/ReaderPostListFragment.java | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 8556fb67b4ac..09acdc062d09 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -29,6 +29,7 @@ import org.wordpress.android.datasets.ReaderBlogTable; import org.wordpress.android.datasets.ReaderDatabase; import org.wordpress.android.datasets.ReaderPostTable; +import org.wordpress.android.datasets.ReaderSearchTable; import org.wordpress.android.datasets.ReaderTagTable; import org.wordpress.android.models.FilterCriteria; import org.wordpress.android.models.ReaderPost; @@ -345,6 +346,9 @@ private void checkPostAdapter() { } } + /* + * reset the post adapter to initial state and create it again using the passed list type + */ private void resetPostAdapter(ReaderPostListType postListType) { mPostListType = postListType; mPostAdapter = null; @@ -570,17 +574,18 @@ public boolean onMenuItemClick(MenuItem item) { @Override public boolean onMenuItemActionExpand(MenuItem item) { mSettingsMenuItem.setVisible(false); - setupSearchSuggestions(); return true; } @Override public boolean onMenuItemActionCollapse(MenuItem item) { - mSettingsMenuItem.setVisible(true); hideSearchMessage(); + resetSearchSuggestionAdapter(); mCurrentSearchQuery = null; - mSearchSuggestionAdapter = null; + mSettingsMenuItem.setVisible(true); + + // return to showing the followed tag that was showing prior to searching resetPostAdapter(ReaderPostListType.TAG_FOLLOWED); return true; @@ -596,9 +601,10 @@ public boolean onQueryTextSubmit(String query) { @Override public boolean onQueryTextChange(String newText) { - mSearchSuggestionAdapter.populate(newText); if (TextUtils.isEmpty(newText)) { showSearchMessage(); + } else { + populateSearchSuggestionAdapter(newText); } return true; } @@ -606,6 +612,10 @@ public boolean onQueryTextChange(String newText) { ); } + /* + * start the search service to search for posts matching the current query - the passed + * offset is used during infinite scroll, pass zero for initial search + */ private void updatePostsInCurrentSearch(int offset) { ReaderSearchService.startService(getActivity(), mCurrentSearchQuery, offset); } @@ -613,11 +623,13 @@ private void updatePostsInCurrentSearch(int offset) { private void submitSearchQuery(@NonNull String query) { if (!isAdded()) return; - // clearing the focus will hide suggestions and the virtual keyboard - mSearchView.clearFocus(); + // remember this query for future suggestions + ReaderSearchTable.addOrUpdateQueryString(query); + + mSearchView.clearFocus(); // this will hide suggestions and the virtual keyboard hideSearchMessage(); - // this will start the search + // start the search mCurrentSearchQuery = query; resetPostAdapter(ReaderPostListType.SEARCH_RESULTS); } @@ -667,7 +679,7 @@ private void hideSearchMessage() { /* * create and assign the suggestion adapter for the search view */ - private void setupSearchSuggestions() { + private void createSearchSuggestionAdapter() { mSearchSuggestionAdapter = new ReaderSearchSuggestionAdapter(getActivity()); mSearchView.setSuggestionsAdapter(mSearchSuggestionAdapter); @@ -688,6 +700,18 @@ public boolean onSuggestionClick(int position) { }); } + private void populateSearchSuggestionAdapter(String query) { + if (mSearchSuggestionAdapter == null) { + createSearchSuggestionAdapter(); + } + mSearchSuggestionAdapter.populate(query); + } + + private void resetSearchSuggestionAdapter() { + mSearchView.setSuggestionsAdapter(null); + mSearchSuggestionAdapter = null; + } + /* * is the search input showing? */ @@ -716,11 +740,8 @@ public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { UpdateAction updateAction = event.getOffset() == 0 ? UpdateAction.REQUEST_NEWER : UpdateAction.REQUEST_OLDER; setIsUpdating(false, updateAction); - if (event.hasResults()) { - getPostAdapter().refresh(); - } else { - mDataLoadedListener.onDataLoaded(true); - } + // load the results, or show empty message if there aren't any + getPostAdapter().refresh(); } /* From 5e7d3978e7ed263690dd93851507cc94985a823a Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 11:44:21 -0400 Subject: [PATCH 29/44] Fixed search message for landscape --- .../android/ui/reader/ReaderPostListFragment.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 09acdc062d09..70e88f94c454 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -573,6 +573,7 @@ public boolean onMenuItemClick(MenuItem item) { MenuItemCompat.setOnActionExpandListener(mSearchMenuItem, new MenuItemCompat.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { + showSearchMessage(); mSettingsMenuItem.setVisible(false); return true; } @@ -585,7 +586,7 @@ public boolean onMenuItemActionCollapse(MenuItem item) { mCurrentSearchQuery = null; mSettingsMenuItem.setVisible(true); - // return to showing the followed tag that was showing prior to searching + // return to the followed tag that was showing prior to searching resetPostAdapter(ReaderPostListType.TAG_FOLLOWED); return true; @@ -644,11 +645,11 @@ private void showSearchMessage() { boolean isLandscape = DisplayUtils.isLandscape(getActivity()); boolean isTablet = DisplayUtils.isXLarge(getActivity()); - if (isLandscape && !isTablet) return; - - TextView txtSearchMsg = (TextView) getView().findViewById(R.id.text_search_message); - if (txtSearchMsg.getVisibility() != View.VISIBLE) { - AniUtils.fadeIn(txtSearchMsg, AniUtils.Duration.MEDIUM); + if (!isLandscape || isTablet) { + TextView txtSearchMsg = (TextView) getView().findViewById(R.id.text_search_message); + if (txtSearchMsg.getVisibility() != View.VISIBLE) { + AniUtils.fadeIn(txtSearchMsg, AniUtils.Duration.MEDIUM); + } } // hide the recycler (post list) so only the above message is visible From 32f883eff6ebc4cd0c619c8089cf03407268be27 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 12:22:54 -0400 Subject: [PATCH 30/44] Fixed device rotation issues, now re-using empty view for search message --- .../ui/reader/ReaderPostListFragment.java | 48 ++++++------------- .../ui/reader/adapters/ReaderPostAdapter.java | 5 +- .../res/layout/reader_fragment_post_cards.xml | 10 ---- 3 files changed, 19 insertions(+), 44 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 70e88f94c454..3d6df3327294 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -339,8 +339,6 @@ private void checkPostAdapter() { updateCurrentTagIfTime(); } else if (getPostListType() == ReaderPostListType.BLOG_PREVIEW) { updatePostsInCurrentBlogOrFeed(UpdateAction.REQUEST_NEWER); - } else if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { - updatePostsInCurrentSearch(0); } } } @@ -353,8 +351,7 @@ private void resetPostAdapter(ReaderPostListType postListType) { mPostListType = postListType; mPostAdapter = null; mRecyclerView.setAdapter(null); - mHasUpdatedPosts = false; - checkPostAdapter(); + mRecyclerView.setAdapter(getPostAdapter()); } @SuppressWarnings("unused") @@ -630,51 +627,36 @@ private void submitSearchQuery(@NonNull String query) { mSearchView.clearFocus(); // this will hide suggestions and the virtual keyboard hideSearchMessage(); - // start the search mCurrentSearchQuery = query; resetPostAdapter(ReaderPostListType.SEARCH_RESULTS); + + // start the search + updatePostsInCurrentSearch(0); } /* - * show message letting user know what they're querying, but only if the user is in - * portrait mode or the device is a tablet (since there's not enough space for the - * message when the virtual keyboard is visible) + * reuse "empty" view to let user know what they're querying */ private void showSearchMessage() { if (!isAdded()) return; + // clear posts so only the empty view is visible + getPostAdapter().clear(); + + // note that we only show the message in portrait unless the device is a tablet since + // there isn't enough room for it in landscape when the soft keyboard is showing boolean isLandscape = DisplayUtils.isLandscape(getActivity()); boolean isTablet = DisplayUtils.isXLarge(getActivity()); if (!isLandscape || isTablet) { - TextView txtSearchMsg = (TextView) getView().findViewById(R.id.text_search_message); - if (txtSearchMsg.getVisibility() != View.VISIBLE) { - AniUtils.fadeIn(txtSearchMsg, AniUtils.Duration.MEDIUM); - } - } - - // hide the recycler (post list) so only the above message is visible - RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); - if (recycler != null && recycler.getVisibility() == View.VISIBLE) { - AniUtils.fadeOut(recycler, AniUtils.Duration.MEDIUM); + setEmptyTitleAndDescription(getString(R.string.reader_label_post_search_explainer), null); + mEmptyView.setVisibility(View.VISIBLE); } - - // make sure the empty view isn't showing - mEmptyView.setVisibility(View.GONE); } private void hideSearchMessage() { if (!isAdded()) return; - TextView txtSearchMsg = (TextView) getView().findViewById(R.id.text_search_message); - if (txtSearchMsg.getVisibility() == View.VISIBLE) { - AniUtils.fadeOut(txtSearchMsg, AniUtils.Duration.MEDIUM); - } - - // make sure the recycler is showing again - RecyclerView recycler = mRecyclerView.getInternalRecyclerView(); - if (recycler != null && recycler.getVisibility() != View.VISIBLE) { - AniUtils.fadeIn(recycler, AniUtils.Duration.MEDIUM); - } + mEmptyView.setVisibility(View.GONE); } /* @@ -726,6 +708,7 @@ public void onEventMainThread(ReaderEvents.SearchPostsStarted event) { UpdateAction updateAction = event.getOffset() == 0 ? UpdateAction.REQUEST_NEWER : UpdateAction.REQUEST_OLDER; setIsUpdating(true, updateAction); + setEmptyTitleAndDescription(false); } @SuppressWarnings("unused") @@ -823,7 +806,7 @@ public void onClick(View v) { * box/pages animation that appears when loading an empty list */ private boolean shouldShowBoxAndPagesAnimation() { - return getPostListType().isTagType() || getPostListType() == ReaderPostListType.SEARCH_RESULTS; + return getPostListType().isTagType(); } private void startBoxAndPagesAnimation() { @@ -1130,7 +1113,6 @@ private void reloadTags() { } } - /* * get posts for the current blog from the server */ diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java index e4f56e3f799c..a102b8087176 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java @@ -584,7 +584,10 @@ public void setCurrentBlogAndFeed(long blogId, long feedId) { } public void setCurrentSearchQuery(@NonNull String query) { - mCurrentSearchQuery = query; + if (!query.equals(mCurrentSearchQuery)) { + mCurrentSearchQuery = query; + reload(); + } } public void clear() { diff --git a/WordPress/src/main/res/layout/reader_fragment_post_cards.xml b/WordPress/src/main/res/layout/reader_fragment_post_cards.xml index 8e6144d76657..66a14fe9c601 100644 --- a/WordPress/src/main/res/layout/reader_fragment_post_cards.xml +++ b/WordPress/src/main/res/layout/reader_fragment_post_cards.xml @@ -57,14 +57,4 @@ android:visibility="gone" tools:visibility="visible" /> - - From 1363d6f631344e9844aa0b5fe4814263ebe92661 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 16:32:54 -0400 Subject: [PATCH 31/44] Simplified post adapter to use the current tag when searching --- .../ui/reader/ReaderPostListFragment.java | 24 ++++++++++++------- .../ui/reader/adapters/ReaderPostAdapter.java | 20 +--------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 3d6df3327294..7fc61224229e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -570,6 +570,7 @@ public boolean onMenuItemClick(MenuItem item) { MenuItemCompat.setOnActionExpandListener(mSearchMenuItem, new MenuItemCompat.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { + resetPostAdapter(ReaderPostListType.SEARCH_RESULTS); showSearchMessage(); mSettingsMenuItem.setVisible(false); return true; @@ -600,6 +601,7 @@ public boolean onQueryTextSubmit(String query) { @Override public boolean onQueryTextChange(String newText) { if (TextUtils.isEmpty(newText)) { + mCurrentSearchQuery = null; showSearchMessage(); } else { populateSearchSuggestionAdapter(newText); @@ -628,9 +630,6 @@ private void submitSearchQuery(@NonNull String query) { hideSearchMessage(); mCurrentSearchQuery = query; - resetPostAdapter(ReaderPostListType.SEARCH_RESULTS); - - // start the search updatePostsInCurrentSearch(0); } @@ -648,7 +647,7 @@ private void showSearchMessage() { boolean isLandscape = DisplayUtils.isLandscape(getActivity()); boolean isTablet = DisplayUtils.isXLarge(getActivity()); if (!isLandscape || isTablet) { - setEmptyTitleAndDescription(getString(R.string.reader_label_post_search_explainer), null); + setEmptyTitleAndDescription(false); mEmptyView.setVisibility(View.VISIBLE); } } @@ -725,7 +724,8 @@ public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { setIsUpdating(false, updateAction); // load the results, or show empty message if there aren't any - getPostAdapter().refresh(); + ReaderTag searchTag = ReaderSearchService.getTagForSearchQuery(mCurrentSearchQuery); + mPostAdapter.setCurrentTag(searchTag); } /* @@ -856,14 +856,17 @@ private void setEmptyTitleAndDescription(boolean requestFailed) { title = getString(R.string.reader_empty_posts_in_tag); } } else if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { - title = getString(R.string.reader_empty_posts_in_search_title); - description = String.format(getString(R.string.reader_empty_posts_in_search_description), mCurrentSearchQuery); + if (TextUtils.isEmpty(mCurrentSearchQuery)) { + title = getString(R.string.reader_label_post_search_explainer); + } else { + title = getString(R.string.reader_empty_posts_in_search_title); + description = String.format(getString(R.string.reader_empty_posts_in_search_description), mCurrentSearchQuery); + } } else { title = getString(R.string.reader_empty_posts_in_tag); } setEmptyTitleAndDescription(title, description); - mEmptyViewBoxImages.setVisibility(shouldShowBoxAndPagesAnimation() ? View.VISIBLE : View.GONE); } private void setEmptyTitleAndDescription(@NonNull String title, String description) { @@ -879,6 +882,8 @@ private void setEmptyTitleAndDescription(@NonNull String title, String descripti descriptionView.setText(description); descriptionView.setVisibility(View.VISIBLE); } + + mEmptyViewBoxImages.setVisibility(shouldShowBoxAndPagesAnimation() ? View.VISIBLE : View.GONE); } /* @@ -973,7 +978,8 @@ private ReaderPostAdapter getPostAdapter() { } else if (getPostListType() == ReaderPostListType.BLOG_PREVIEW) { mPostAdapter.setCurrentBlogAndFeed(mCurrentBlogId, mCurrentFeedId); } else if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { - mPostAdapter.setCurrentSearchQuery(mCurrentSearchQuery); + ReaderTag searchTag = ReaderSearchService.getTagForSearchQuery(mCurrentSearchQuery); + mPostAdapter.setCurrentTag(searchTag); } } return mPostAdapter; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java index a102b8087176..c11aefcc5756 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java @@ -2,7 +2,6 @@ import android.content.Context; import android.os.AsyncTask; -import android.support.annotation.NonNull; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; @@ -28,7 +27,6 @@ import org.wordpress.android.ui.reader.actions.ReaderActions; import org.wordpress.android.ui.reader.actions.ReaderPostActions; import org.wordpress.android.ui.reader.models.ReaderBlogIdPostId; -import org.wordpress.android.ui.reader.services.ReaderSearchService; import org.wordpress.android.ui.reader.utils.ReaderUtils; import org.wordpress.android.ui.reader.utils.ReaderXPostUtils; import org.wordpress.android.ui.reader.views.ReaderBlogInfoView; @@ -49,7 +47,6 @@ public class ReaderPostAdapter extends RecyclerView.Adapter Date: Sun, 15 May 2016 16:57:04 -0400 Subject: [PATCH 32/44] No longer clearing cached searches again --- .../android/ui/reader/ReaderPostListFragment.java | 12 +++++++----- .../ui/reader/adapters/ReaderPostAdapter.java | 6 ++++-- .../ui/reader/services/ReaderSearchService.java | 5 ----- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 7fc61224229e..c31dd4a950a0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -629,6 +629,9 @@ private void submitSearchQuery(@NonNull String query) { mSearchView.clearFocus(); // this will hide suggestions and the virtual keyboard hideSearchMessage(); + ReaderTag searchTag = ReaderSearchService.getTagForSearchQuery(query); + mPostAdapter.setCurrentTag(searchTag); + mCurrentSearchQuery = query; updatePostsInCurrentSearch(0); } @@ -724,8 +727,7 @@ public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { setIsUpdating(false, updateAction); // load the results, or show empty message if there aren't any - ReaderTag searchTag = ReaderSearchService.getTagForSearchQuery(mCurrentSearchQuery); - mPostAdapter.setCurrentTag(searchTag); + mPostAdapter.refresh(); } /* @@ -1151,10 +1153,10 @@ public void onEventMainThread(ReaderEvents.UpdatePostsEnded event) { return; } - // don't show new posts if user is entering a search query - posts will automatically + // don't show new posts if user is searching - posts will automatically // appear when search is exited - if (isSearchViewExpanded()) { - AppLog.d(T.READER, "skipping post reload, search view is expanded"); + if (isSearchViewExpanded() + || getPostListType() == ReaderPostListType.SEARCH_RESULTS) { return; } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java index c11aefcc5756..72e9a79741a7 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderPostAdapter.java @@ -577,8 +577,10 @@ public void setCurrentBlogAndFeed(long blogId, long feedId) { } public void clear() { - mPosts.clear(); - notifyDataSetChanged(); + if (!mPosts.isEmpty()) { + mPosts.clear(); + notifyDataSetChanged(); + } } public void refresh() { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index ac85bc127043..b2f738bcded0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -70,11 +70,6 @@ public int onStartCommand(Intent intent, int flags, int startId) { } private void startSearch(final String query, final int offset) { - // delete existing results if this is the initial search - if (offset == 0) { - ReaderPostTable.deletePostsWithTag(getTagForSearchQuery(query)); - } - String path = "read/search?q=" + UrlUtils.urlEncode(query) + "&number=" + ReaderConstants.READER_MAX_SEARCH_POSTS_TO_REQUEST From 0f1b3462495ee723104464ab941d201c5de38ed1 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 17:04:59 -0400 Subject: [PATCH 33/44] Make sure toolbar showing search keyword is showing when returning to list view --- .../org/wordpress/android/ui/FilteredRecyclerView.java | 4 ---- .../android/ui/reader/ReaderPostListFragment.java | 8 +++++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/FilteredRecyclerView.java b/WordPress/src/main/java/org/wordpress/android/ui/FilteredRecyclerView.java index b58c72b3aa41..835f3293e528 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/FilteredRecyclerView.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/FilteredRecyclerView.java @@ -358,10 +358,6 @@ public void removeOnScrollListener(RecyclerView.OnScrollListener listener) { } } - public RecyclerView getInternalRecyclerView() { - return mRecyclerView; - } - public void hideToolbar(){ mAppBarLayout.setExpanded(false, true); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index c31dd4a950a0..e8f2594eeb81 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -274,11 +274,17 @@ public void onResume() { if (mWasPaused) { AppLog.d(T.READER, "reader post list > resumed from paused state"); mWasPaused = false; - if (getPostListType().equals(ReaderPostListType.TAG_FOLLOWED)) { + if (getPostListType() == ReaderPostListType.TAG_FOLLOWED) { resumeFollowedTag(); } else { refreshPosts(); } + + // if the user was searching, make sure the filter toolbar is showing + // so the user can see the search keyword they entered + if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { + mRecyclerView.showToolbar(); + } } } From 72987f176ed3e1e28add6a928d6c9a9cd9585843 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 17:38:46 -0400 Subject: [PATCH 34/44] Use a CursorAdapter rather than a SimpleCursorAdapter for search suggestions --- .../android/datasets/ReaderSearchTable.java | 27 ++++----- .../ReaderSearchSuggestionAdapter.java | 58 ++++++++++--------- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java index 91766e6cc538..e5c94de1ec41 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java @@ -9,20 +9,22 @@ import org.wordpress.android.util.DateTimeUtils; import org.wordpress.android.util.SqlUtils; -import java.util.ArrayList; import java.util.Date; -import java.util.List; /** * user's reader search history */ public class ReaderSearchTable { + public static final String COL_QUERY = "query_string"; + protected static void createTables(SQLiteDatabase db) { db.execSQL("CREATE TABLE tbl_search_history (" - + " query_string TEXT NOT NULL COLLATE NOCASE PRIMARY KEY," + + " _id INTEGER PRIMARY KEY AUTOINCREMENT," + + " query_string TEXT NOT NULL COLLATE NOCASE," + " date_used TEXT," + " counter INTEGER DEFAULT 1)"); + db.execSQL("CREATE UNIQUE INDEX idx_search_query ON tbl_search_history(query_string)"); } protected static void dropTables(SQLiteDatabase db) { @@ -55,18 +57,18 @@ private static int getCounterForQueryString(@NonNull String query) { } /** - * Returns a list of query strings previously typed by the user + * Returns a cursor containing query strings previously typed by the user * @param filter - filters the list using LIKE syntax (pass null for no filter) * @param max - limit the list to this many items (pass zero for no limit) */ - public static List getQueryStrings(String filter, int max) { + public static Cursor getQueryStringCursor(String filter, int max) { String sql; String[] args; if (TextUtils.isEmpty(filter)) { - sql = "SELECT query_string FROM tbl_search_history"; + sql = "SELECT * FROM tbl_search_history"; args = null; } else { - sql = "SELECT query_string FROM tbl_search_history WHERE query_string LIKE ?"; + sql = "SELECT * FROM tbl_search_history WHERE query_string LIKE ?"; args = new String[]{filter + "%"}; } @@ -76,15 +78,6 @@ public static List getQueryStrings(String filter, int max) { sql += " LIMIT " + max; } - Cursor cursor = ReaderDatabase.getReadableDb().rawQuery(sql, args); - try { - List queries = new ArrayList<>(); - while (cursor.moveToNext()) { - queries.add(cursor.getString(0)); - } - return queries; - } finally { - SqlUtils.closeCursor(cursor); - } + return ReaderDatabase.getReadableDb().rawQuery(sql, args); } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java index 072299e473f9..37fb85991633 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java @@ -1,48 +1,54 @@ package org.wordpress.android.ui.reader.adapters; import android.content.Context; -import android.database.MatrixCursor; -import android.support.v4.widget.SimpleCursorAdapter; +import android.database.Cursor; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; import org.wordpress.android.R; import org.wordpress.android.datasets.ReaderSearchTable; -import java.util.List; +public class ReaderSearchSuggestionAdapter extends CursorAdapter { + private final LayoutInflater mInflater; -public class ReaderSearchSuggestionAdapter extends SimpleCursorAdapter { - private List mSearchSuggestions; private static final int MAX_SUGGESTIONS = 5; public ReaderSearchSuggestionAdapter(Context context) { - super(context, - R.layout.reader_listitem_suggestion, - null, - new String[]{"query"}, - new int[]{android.R.id.text1}, - 0); + super(context, null, false); + mInflater = LayoutInflater.from(context); } - /* - * populates the suggestion list from previous suggestions with the passed filter applied - pass - * null for the filter to show all suggestions - */ public void populate(String filter) { - mSearchSuggestions = ReaderSearchTable.getQueryStrings(filter, MAX_SUGGESTIONS); - MatrixCursor cursor = new MatrixCursor(new String[]{"_id", "query"}); - - int id = 0; - for (String query : mSearchSuggestions) { - cursor.addRow(new Object[] {id++, query}); - } - + Cursor cursor = ReaderSearchTable.getQueryStringCursor(filter, MAX_SUGGESTIONS); swapCursor(cursor); } public String getSuggestion(int position) { - if (position < 0 || position > mSearchSuggestions.size() - 1) { - return null; + Cursor cursor = (Cursor) getItem(position); + return cursor.getString(cursor.getColumnIndex(ReaderSearchTable.COL_QUERY)); + } + + private class SuggestionViewHolder { + private final TextView textView; + SuggestionViewHolder(View view) { + textView = (TextView) view.findViewById(android.R.id.text1); } - return mSearchSuggestions.get(position); } + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + View view = mInflater.inflate(R.layout.reader_listitem_suggestion, parent, false); + view.setTag(new SuggestionViewHolder(view)); + return view; + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + String query = cursor.getString(cursor.getColumnIndex(ReaderSearchTable.COL_QUERY)); + SuggestionViewHolder holder = (SuggestionViewHolder) view.getTag(); + holder.textView.setText(query); + } } From 5136967ae0d5b96411b638dad2ae55c52ffd8578 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 17:51:18 -0400 Subject: [PATCH 35/44] Enable deleting search suggestions --- .../android/datasets/ReaderSearchTable.java | 5 +++ .../ReaderSearchSuggestionAdapter.java | 20 ++++++++--- .../res/layout/reader_listitem_suggestion.xml | 34 ++++++++++++++----- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java index e5c94de1ec41..61d0fc383e86 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java @@ -50,6 +50,11 @@ public static void addOrUpdateQueryString(@NonNull String query) { } } + public static void deleteQueryString(@NonNull String query) { + String[] args = new String[]{query}; + ReaderDatabase.getWritableDb().delete("tbl_search_history", "query_string=?", args); + } + private static int getCounterForQueryString(@NonNull String query) { String[] args = {query}; return SqlUtils.intForQuery(ReaderDatabase.getReadableDb(), diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java index 37fb85991633..d76ad3f7d862 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java @@ -6,24 +6,24 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.TextView; import org.wordpress.android.R; import org.wordpress.android.datasets.ReaderSearchTable; public class ReaderSearchSuggestionAdapter extends CursorAdapter { - private final LayoutInflater mInflater; - private static final int MAX_SUGGESTIONS = 5; + private String mCurrentFilter; public ReaderSearchSuggestionAdapter(Context context) { super(context, null, false); - mInflater = LayoutInflater.from(context); } public void populate(String filter) { Cursor cursor = ReaderSearchTable.getQueryStringCursor(filter, MAX_SUGGESTIONS); swapCursor(cursor); + mCurrentFilter = filter; } public String getSuggestion(int position) { @@ -33,22 +33,32 @@ public String getSuggestion(int position) { private class SuggestionViewHolder { private final TextView textView; + private final ImageView imgDelete; SuggestionViewHolder(View view) { textView = (TextView) view.findViewById(android.R.id.text1); + imgDelete = (ImageView) view.findViewById(R.id.image_delete); } } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - View view = mInflater.inflate(R.layout.reader_listitem_suggestion, parent, false); + View view = LayoutInflater.from(context).inflate(R.layout.reader_listitem_suggestion, parent, false); view.setTag(new SuggestionViewHolder(view)); return view; } @Override public void bindView(View view, Context context, Cursor cursor) { - String query = cursor.getString(cursor.getColumnIndex(ReaderSearchTable.COL_QUERY)); SuggestionViewHolder holder = (SuggestionViewHolder) view.getTag(); + final String query = cursor.getString(cursor.getColumnIndex(ReaderSearchTable.COL_QUERY)); + holder.textView.setText(query); + holder.imgDelete.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ReaderSearchTable.deleteQueryString(query); + populate(mCurrentFilter); + } + }); } } diff --git a/WordPress/src/main/res/layout/reader_listitem_suggestion.xml b/WordPress/src/main/res/layout/reader_listitem_suggestion.xml index 261078ac752b..0eee2e60a6f9 100644 --- a/WordPress/src/main/res/layout/reader_listitem_suggestion.xml +++ b/WordPress/src/main/res/layout/reader_listitem_suggestion.xml @@ -1,15 +1,31 @@ - \ No newline at end of file + android:paddingStart="?android:attr/listPreferredItemPaddingStart"> + + + + + \ No newline at end of file From 4032f4ccbad099638b4bfd84752eaae907df2571 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 18:09:07 -0400 Subject: [PATCH 36/44] Renamed suggestion table and dropped counter --- .../android/datasets/ReaderSearchTable.java | 29 +++++++------------ .../ReaderSearchSuggestionAdapter.java | 12 +++++--- .../res/layout/reader_listitem_suggestion.xml | 2 +- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java index 61d0fc383e86..598e596c29cc 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderSearchTable.java @@ -12,38 +12,35 @@ import java.util.Date; /** - * user's reader search history + * search suggestion table - populated by user's reader search history */ public class ReaderSearchTable { public static final String COL_QUERY = "query_string"; protected static void createTables(SQLiteDatabase db) { - db.execSQL("CREATE TABLE tbl_search_history (" + db.execSQL("CREATE TABLE tbl_search_suggestions (" + " _id INTEGER PRIMARY KEY AUTOINCREMENT," + " query_string TEXT NOT NULL COLLATE NOCASE," - + " date_used TEXT," - + " counter INTEGER DEFAULT 1)"); - db.execSQL("CREATE UNIQUE INDEX idx_search_query ON tbl_search_history(query_string)"); + + " date_used TEXT)"); + db.execSQL("CREATE UNIQUE INDEX idx_search_suggestions_query ON tbl_search_suggestions(query_string)"); } protected static void dropTables(SQLiteDatabase db) { - db.execSQL("DROP TABLE IF EXISTS tbl_search_history"); + db.execSQL("DROP TABLE IF EXISTS tbl_search_suggestions"); } /* - * adds the passed query string, updating the usage counter and date + * adds the passed query string, updating the usage date */ public static void addOrUpdateQueryString(@NonNull String query) { String date = DateTimeUtils.javaDateToIso8601(new Date()); - int counter = getCounterForQueryString(query) + 1; SQLiteStatement stmt = ReaderDatabase.getWritableDb().compileStatement( - "INSERT OR REPLACE INTO tbl_search_history (query_string, date_used, counter) VALUES (?1,?2,?3)"); + "INSERT OR REPLACE INTO tbl_search_suggestions (query_string, date_used) VALUES (?1,?2)"); try { stmt.bindString(1, query); stmt.bindString(2, date); - stmt.bindLong (3, counter); stmt.execute(); } finally { SqlUtils.closeStatement(stmt); @@ -52,13 +49,7 @@ public static void addOrUpdateQueryString(@NonNull String query) { public static void deleteQueryString(@NonNull String query) { String[] args = new String[]{query}; - ReaderDatabase.getWritableDb().delete("tbl_search_history", "query_string=?", args); - } - - private static int getCounterForQueryString(@NonNull String query) { - String[] args = {query}; - return SqlUtils.intForQuery(ReaderDatabase.getReadableDb(), - "SELECT counter FROM tbl_search_history WHERE query_string=?", args); + ReaderDatabase.getWritableDb().delete("tbl_search_suggestions", "query_string=?", args); } /** @@ -70,10 +61,10 @@ public static Cursor getQueryStringCursor(String filter, int max) { String sql; String[] args; if (TextUtils.isEmpty(filter)) { - sql = "SELECT * FROM tbl_search_history"; + sql = "SELECT * FROM tbl_search_suggestions"; args = null; } else { - sql = "SELECT * FROM tbl_search_history WHERE query_string LIKE ?"; + sql = "SELECT * FROM tbl_search_suggestions WHERE query_string LIKE ?"; args = new String[]{filter + "%"}; } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java index d76ad3f7d862..633e6edaa509 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderSearchSuggestionAdapter.java @@ -28,14 +28,18 @@ public void populate(String filter) { public String getSuggestion(int position) { Cursor cursor = (Cursor) getItem(position); - return cursor.getString(cursor.getColumnIndex(ReaderSearchTable.COL_QUERY)); + if (cursor != null) { + return cursor.getString(cursor.getColumnIndex(ReaderSearchTable.COL_QUERY)); + } else { + return null; + } } private class SuggestionViewHolder { - private final TextView textView; + private final TextView txtSuggestion; private final ImageView imgDelete; SuggestionViewHolder(View view) { - textView = (TextView) view.findViewById(android.R.id.text1); + txtSuggestion = (TextView) view.findViewById(R.id.text_suggestion); imgDelete = (ImageView) view.findViewById(R.id.image_delete); } } @@ -52,7 +56,7 @@ public void bindView(View view, Context context, Cursor cursor) { SuggestionViewHolder holder = (SuggestionViewHolder) view.getTag(); final String query = cursor.getString(cursor.getColumnIndex(ReaderSearchTable.COL_QUERY)); - holder.textView.setText(query); + holder.txtSuggestion.setText(query); holder.imgDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/WordPress/src/main/res/layout/reader_listitem_suggestion.xml b/WordPress/src/main/res/layout/reader_listitem_suggestion.xml index 0eee2e60a6f9..040f83a20520 100644 --- a/WordPress/src/main/res/layout/reader_listitem_suggestion.xml +++ b/WordPress/src/main/res/layout/reader_listitem_suggestion.xml @@ -8,7 +8,7 @@ android:paddingStart="?android:attr/listPreferredItemPaddingStart"> Date: Sun, 15 May 2016 18:12:49 -0400 Subject: [PATCH 37/44] Increment db version counter --- .../java/org/wordpress/android/datasets/ReaderDatabase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderDatabase.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderDatabase.java index 6efbe3d9e07e..c7e105f62e23 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderDatabase.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderDatabase.java @@ -19,7 +19,7 @@ */ public class ReaderDatabase extends SQLiteOpenHelper { protected static final String DB_NAME = "wpreader.db"; - private static final int DB_VERSION = 117; + private static final int DB_VERSION = 118; /* * version history @@ -69,6 +69,7 @@ public class ReaderDatabase extends SQLiteOpenHelper { * 115 - added ReaderSearchTable * 116 - added tag_display_name to tag tables * 117 - changed tbl_posts.timestamp from INTEGER to REAL + * 118 - renamed tbl_search_history to tbl_search_suggestions */ /* From ca4c263726c3c72dd854cadf0770412abf2ea403 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Sun, 15 May 2016 19:39:50 -0400 Subject: [PATCH 38/44] Rewrote `setEmptyTitleAndDescription` to better handle search --- .../wordpress/android/models/ReaderTag.java | 1 + .../ui/reader/ReaderPostListFragment.java | 72 ++++++++++--------- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderTag.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderTag.java index e6bcc629e07b..0fa75a8b2a8b 100644 --- a/WordPress/src/main/java/org/wordpress/android/models/ReaderTag.java +++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderTag.java @@ -150,6 +150,7 @@ public static boolean isSameTag(ReaderTag tag1, ReaderTag tag2) { public boolean isPostsILike() { return tagType == ReaderTagType.DEFAULT && getEndpoint().endsWith("/read/liked"); } + public boolean isFollowedSites() { return tagType == ReaderTagType.DEFAULT && getEndpoint().endsWith("/read/following"); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index e8f2594eeb81..4de4c3dbf676 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -839,39 +839,47 @@ private void setEmptyTitleAndDescription(boolean requestFailed) { title = getString(R.string.reader_empty_posts_no_connection); } else if (requestFailed) { title = getString(R.string.reader_empty_posts_request_failed); - } else if (isUpdating()) { - if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { - title = getString(R.string.reader_label_post_search_running); - } else { - title = getString(R.string.reader_empty_posts_in_tag_updating); - } - } else if (getPostListType() == ReaderPostListType.BLOG_PREVIEW) { - title = getString(R.string.reader_empty_posts_in_blog); - } else if (getPostListType() == ReaderPostListType.TAG_FOLLOWED && hasCurrentTag()) { - if (getCurrentTag().isFollowedSites()) { - if (ReaderBlogTable.hasFollowedBlogs()) { - title = getString(R.string.reader_empty_followed_blogs_no_recent_posts_title); - description = getString(R.string.reader_empty_followed_blogs_no_recent_posts_description); - } else { - title = getString(R.string.reader_empty_followed_blogs_title); - description = getString(R.string.reader_empty_followed_blogs_description); - } - } else if (getCurrentTag().isPostsILike()) { - title = getString(R.string.reader_empty_posts_liked); - } else if (getCurrentTag().tagType == ReaderTagType.CUSTOM_LIST) { - title = getString(R.string.reader_empty_posts_in_custom_list); - } else { - title = getString(R.string.reader_empty_posts_in_tag); - } - } else if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { - if (TextUtils.isEmpty(mCurrentSearchQuery)) { - title = getString(R.string.reader_label_post_search_explainer); - } else { - title = getString(R.string.reader_empty_posts_in_search_title); - description = String.format(getString(R.string.reader_empty_posts_in_search_description), mCurrentSearchQuery); - } + } else if (isUpdating() && getPostListType() != ReaderPostListType.SEARCH_RESULTS) { + title = getString(R.string.reader_empty_posts_in_tag_updating); } else { - title = getString(R.string.reader_empty_posts_in_tag); + switch (getPostListType()) { + case TAG_FOLLOWED: + if (getCurrentTag().isFollowedSites()) { + if (ReaderBlogTable.hasFollowedBlogs()) { + title = getString(R.string.reader_empty_followed_blogs_no_recent_posts_title); + description = getString(R.string.reader_empty_followed_blogs_no_recent_posts_description); + } else { + title = getString(R.string.reader_empty_followed_blogs_title); + description = getString(R.string.reader_empty_followed_blogs_description); + } + } else if (getCurrentTag().isPostsILike()) { + title = getString(R.string.reader_empty_posts_liked); + } else if (getCurrentTag().isListTopic()) { + title = getString(R.string.reader_empty_posts_in_custom_list); + } else { + title = getString(R.string.reader_empty_posts_in_tag); + } + break; + + case BLOG_PREVIEW: + title = getString(R.string.reader_empty_posts_in_blog); + break; + + case SEARCH_RESULTS: + if (TextUtils.isEmpty(mCurrentSearchQuery)) { + title = getString(R.string.reader_label_post_search_explainer); + } else if (isUpdating()) { + title = getString(R.string.reader_label_post_search_running); + } else { + title = getString(R.string.reader_empty_posts_in_search_title); + description = String.format(getString(R.string.reader_empty_posts_in_search_description), mCurrentSearchQuery); + } + break; + + default: + title = getString(R.string.reader_empty_posts_in_tag); + break; + } } setEmptyTitleAndDescription(title, description); From d0912a9361ad61c19d1b922763c915b24dc61435 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Mon, 16 May 2016 06:33:04 -0400 Subject: [PATCH 39/44] Added `showEmptyView()` and `hideEmptyView()` --- .../ui/reader/ReaderPostListFragment.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 4de4c3dbf676..2a9b49d26fcb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -657,14 +657,12 @@ private void showSearchMessage() { boolean isTablet = DisplayUtils.isXLarge(getActivity()); if (!isLandscape || isTablet) { setEmptyTitleAndDescription(false); - mEmptyView.setVisibility(View.VISIBLE); + showEmptyView(); } } private void hideSearchMessage() { - if (!isAdded()) return; - - mEmptyView.setVisibility(View.GONE); + hideEmptyView(); } /* @@ -733,7 +731,7 @@ public void onEventMainThread(ReaderEvents.SearchPostsEnded event) { setIsUpdating(false, updateAction); // load the results, or show empty message if there aren't any - mPostAdapter.refresh(); + refreshPosts(); } /* @@ -902,6 +900,18 @@ private void setEmptyTitleAndDescription(@NonNull String title, String descripti mEmptyViewBoxImages.setVisibility(shouldShowBoxAndPagesAnimation() ? View.VISIBLE : View.GONE); } + private void showEmptyView() { + if (isAdded()) { + mEmptyView.setVisibility(View.VISIBLE); + } + } + + private void hideEmptyView() { + if (isAdded()) { + mEmptyView.setVisibility(View.GONE); + } + } + /* * called by post adapter when data has been loaded */ @@ -914,12 +924,12 @@ public void onDataLoaded(boolean isEmpty) { mRecyclerView.setRefreshing(false); if (isEmpty) { setEmptyTitleAndDescription(false); - mEmptyView.setVisibility(View.VISIBLE); + showEmptyView(); if (shouldShowBoxAndPagesAnimation()) { startBoxAndPagesAnimation(); } } else { - mEmptyView.setVisibility(View.GONE); + hideEmptyView(); if (mRestorePosition > 0) { AppLog.d(T.READER, "reader post list > restoring position"); mRecyclerView.scrollRecycleViewToPosition(mRestorePosition); From 11eb8b82ed9b007e8a945a4d6a4cb683121e45fe Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Mon, 16 May 2016 06:49:47 -0400 Subject: [PATCH 40/44] Removed `newInstanceForSearch` --- .../ui/reader/ReaderPostListActivity.java | 20 ------------------- .../ui/reader/ReaderPostListFragment.java | 13 ------------ 2 files changed, 33 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListActivity.java index 8a226e536bdc..f32d190bd831 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListActivity.java @@ -58,12 +58,6 @@ public void onCreate(Bundle savedInstanceState) { if (tag != null && savedInstanceState == null) { showListFragmentForTag(tag, mPostListType); } - } else if (getPostListType() == ReaderPostListType.SEARCH_RESULTS) { - String query = getIntent().getStringExtra(ReaderConstants.ARG_SEARCH_QUERY); - setTitle(String.format(getString(R.string.reader_title_search_results), query)); - if (savedInstanceState == null) { - showListFragmentForSearch(query); - } } } @@ -164,20 +158,6 @@ private void showListFragmentForFeed(long feedId) { .commit(); } - /* - * show fragment containing list of posts matching the passed search query - */ - private void showListFragmentForSearch(@NonNull String query) { - if (isFinishing()) { - return; - } - Fragment fragment = ReaderPostListFragment.newInstanceForSearch(query); - getFragmentManager() - .beginTransaction() - .replace(R.id.fragment_container, fragment, getString(R.string.fragment_tag_reader_post_list)) - .commit(); - } - private ReaderPostListFragment getListFragment() { Fragment fragment = getFragmentManager().findFragmentByTag(getString(R.string.fragment_tag_reader_post_list)); if (fragment == null) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 2a9b49d26fcb..5a3364dccb2e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -194,19 +194,6 @@ public static ReaderPostListFragment newInstanceForFeed(long feedId) { return fragment; } - public static ReaderPostListFragment newInstanceForSearch(@NonNull String query) { - AppLog.d(T.READER, "reader post list > newInstance (search)"); - - Bundle args = new Bundle(); - args.putString(ReaderConstants.ARG_SEARCH_QUERY, query); - args.putSerializable(ReaderConstants.ARG_POST_LIST_TYPE, ReaderPostListType.SEARCH_RESULTS); - - ReaderPostListFragment fragment = new ReaderPostListFragment(); - fragment.setArguments(args); - - return fragment; - } - @Override public void setArguments(Bundle args) { super.setArguments(args); From 0808f6e156b8dc1fb298f3068eb69d63dac1ffa0 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Mon, 16 May 2016 07:12:06 -0400 Subject: [PATCH 41/44] Show search message even in landscape --- .../ui/reader/ReaderPostListFragment.java | 20 +++++++------------ .../res/layout/filtered_list_component.xml | 1 + .../src/main/res/layout/reader_empty_view.xml | 4 +++- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 5a3364dccb2e..6f92293a3bcf 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -60,7 +60,6 @@ import org.wordpress.android.util.AppLog; import org.wordpress.android.util.AppLog.T; import org.wordpress.android.util.DateTimeUtils; -import org.wordpress.android.util.DisplayUtils; import org.wordpress.android.util.NetworkUtils; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.WPActivityUtils; @@ -573,8 +572,6 @@ public boolean onMenuItemActionExpand(MenuItem item) { public boolean onMenuItemActionCollapse(MenuItem item) { hideSearchMessage(); resetSearchSuggestionAdapter(); - - mCurrentSearchQuery = null; mSettingsMenuItem.setVisible(true); // return to the followed tag that was showing prior to searching @@ -594,7 +591,6 @@ public boolean onQueryTextSubmit(String query) { @Override public boolean onQueryTextChange(String newText) { if (TextUtils.isEmpty(newText)) { - mCurrentSearchQuery = null; showSearchMessage(); } else { populateSearchSuggestionAdapter(newText); @@ -638,14 +634,8 @@ private void showSearchMessage() { // clear posts so only the empty view is visible getPostAdapter().clear(); - // note that we only show the message in portrait unless the device is a tablet since - // there isn't enough room for it in landscape when the soft keyboard is showing - boolean isLandscape = DisplayUtils.isLandscape(getActivity()); - boolean isTablet = DisplayUtils.isXLarge(getActivity()); - if (!isLandscape || isTablet) { - setEmptyTitleAndDescription(false); - showEmptyView(); - } + setEmptyTitleAndDescription(false); + showEmptyView(); } private void hideSearchMessage() { @@ -695,6 +685,10 @@ private boolean isSearchViewExpanded() { return mSearchView != null && !mSearchView.isIconified(); } + private boolean isSearchViewEmpty() { + return mSearchView != null && mSearchView.getQuery().length() == 0; + } + @SuppressWarnings("unused") public void onEventMainThread(ReaderEvents.SearchPostsStarted event) { if (!isAdded()) return; @@ -851,7 +845,7 @@ private void setEmptyTitleAndDescription(boolean requestFailed) { break; case SEARCH_RESULTS: - if (TextUtils.isEmpty(mCurrentSearchQuery)) { + if (isSearchViewEmpty() || TextUtils.isEmpty(mCurrentSearchQuery)) { title = getString(R.string.reader_label_post_search_explainer); } else if (isUpdating()) { title = getString(R.string.reader_label_post_search_running); diff --git a/WordPress/src/main/res/layout/filtered_list_component.xml b/WordPress/src/main/res/layout/filtered_list_component.xml index 96c788e14402..1c5a60bb4309 100644 --- a/WordPress/src/main/res/layout/filtered_list_component.xml +++ b/WordPress/src/main/res/layout/filtered_list_component.xml @@ -15,6 +15,7 @@ style="@style/FilteredRecyclerViewToolbar" android:layout_width="match_parent" android:layout_height="@dimen/toolbar_height" + android:focusableInTouchMode="true" app:contentInsetLeft="0dp" app:contentInsetStart="0dp" app:layout_scrollFlags="scroll|enterAlways"> diff --git a/WordPress/src/main/res/layout/reader_empty_view.xml b/WordPress/src/main/res/layout/reader_empty_view.xml index f1a80a0b0c8f..b09c5154324e 100644 --- a/WordPress/src/main/res/layout/reader_empty_view.xml +++ b/WordPress/src/main/res/layout/reader_empty_view.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_marginTop="@dimen/toolbar_height" android:gravity="center" android:orientation="vertical" android:visibility="gone" @@ -13,7 +14,7 @@ android:id="@+id/layout_box_images" android:layout_width="wrap_content" android:layout_height="100dp" - android:layout_marginBottom="8dp" + android:layout_marginBottom="@dimen/margin_medium" android:visibility="gone" tools:visibility="visible"> @@ -68,6 +69,7 @@ android:layout_marginBottom="@dimen/empty_list_title_bottom_margin" android:layout_marginLeft="@dimen/empty_list_title_side_margin" android:layout_marginRight="@dimen/empty_list_title_side_margin" + android:layout_marginTop="@dimen/margin_medium" android:text="@string/reader_empty_posts_in_tag" app:fixWidowWords="true" /> From 946eed14efe19a6048c5c00afed4de7e4c2bdf53 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Mon, 16 May 2016 07:22:00 -0400 Subject: [PATCH 42/44] Clear query when searchView is collapsed --- .../android/ui/reader/ReaderPostListFragment.java | 3 +++ .../android/ui/reader/services/ReaderSearchService.java | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 6f92293a3bcf..01dfefe9be46 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -573,6 +573,7 @@ public boolean onMenuItemActionCollapse(MenuItem item) { hideSearchMessage(); resetSearchSuggestionAdapter(); mSettingsMenuItem.setVisible(true); + mCurrentSearchQuery = null; // return to the followed tag that was showing prior to searching resetPostAdapter(ReaderPostListType.TAG_FOLLOWED); @@ -618,7 +619,9 @@ private void submitSearchQuery(@NonNull String query) { mSearchView.clearFocus(); // this will hide suggestions and the virtual keyboard hideSearchMessage(); + // delete existing results before setting this as the current search ReaderTag searchTag = ReaderSearchService.getTagForSearchQuery(query); + ReaderPostTable.deletePostsWithTag(searchTag); mPostAdapter.setCurrentTag(searchTag); mCurrentSearchQuery = query; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java index b2f738bcded0..e91e5bc63e17 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/services/ReaderSearchService.java @@ -39,6 +39,13 @@ public static void startService(Context context, @NonNull String query, int offs context.startService(intent); } + public static void stopService(Context context) { + if (context == null) return; + + Intent intent = new Intent(context, ReaderSearchService.class); + context.stopService(intent); + } + @Override public IBinder onBind(Intent intent) { return null; From ca9d45ebada86b8518bf97412a256b9ab07169e0 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli Date: Mon, 16 May 2016 16:07:02 +0200 Subject: [PATCH 43/44] Added some comments that describe how we're detecting the "onBackground" state. --- .../main/java/org/wordpress/android/WordPress.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/WordPress.java b/WordPress/src/main/java/org/wordpress/android/WordPress.java index f1d92d83429a..1e4a2149df6e 100644 --- a/WordPress/src/main/java/org/wordpress/android/WordPress.java +++ b/WordPress/src/main/java/org/wordpress/android/WordPress.java @@ -762,6 +762,20 @@ private void updatePushNotificationTokenIfNotLimited() { } } + /** + * The two methods below (startActivityTransitionTimer and stopActivityTransitionTimer) + * are used to track when the app goes to background. + * + * Our implementation uses `onActivityPaused` and `onActivityResumed` of ApplicationLifecycleMonitor + * to start and stop the timer that detects when the app goes to background. + * + * So when the user is simply navigating between the activities, the onActivityPaused() calls `startActivityTransitionTimer` + * and starts the timer, but almost immediately the new activity being entered, the ApplicationLifecycleMonitor cancels the timer + * in its onActivityResumed method, that in order calls `stopActivityTransitionTimer`. + * And so mIsInBackground would be false. + * + * In the case the app is sent to background, the TimerTask is instead executed, and the code that handles all the background logic is run. + */ private void startActivityTransitionTimer() { this.mActivityTransitionTimer = new Timer(); this.mActivityTransitionTimerTask = new TimerTask() { From ffdf77a37c99e4268b71b5b2799a469983b368bc Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Mon, 16 May 2016 15:13:29 -0400 Subject: [PATCH 44/44] Purge all searches when reader db is purged at startup --- .../wordpress/android/datasets/ReaderPostTable.java | 11 +++++++++++ .../android/ui/reader/ReaderPostListFragment.java | 2 -- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderPostTable.java b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderPostTable.java index 7ca98ea61249..55429f6eb377 100644 --- a/WordPress/src/main/java/org/wordpress/android/datasets/ReaderPostTable.java +++ b/WordPress/src/main/java/org/wordpress/android/datasets/ReaderPostTable.java @@ -180,6 +180,9 @@ protected static int purge(SQLiteDatabase db) { numDeleted += purgePostsForTag(db, tag); } + // delete search results + numDeleted += purgeSearchResults(db); + // delete posts in tbl_posts that no longer exist in tbl_post_tags numDeleted += db.delete("tbl_posts", "pseudo_id NOT IN (SELECT DISTINCT pseudo_id FROM tbl_post_tags)", null); @@ -211,6 +214,14 @@ private static int purgePostsForTag(SQLiteDatabase db, ReaderTag tag) { return numDeleted; } + /* + * purge all posts that were retained from previous searches + */ + private static int purgeSearchResults(SQLiteDatabase db) { + String[] args = {Integer.toString(ReaderTagType.SEARCH.toInt())}; + return db.delete("tbl_post_tags", "tag_type=?", args); + } + public static int getNumPostsInBlog(long blogId) { if (blogId == 0) { return 0; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 01dfefe9be46..de599e9c6735 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -619,9 +619,7 @@ private void submitSearchQuery(@NonNull String query) { mSearchView.clearFocus(); // this will hide suggestions and the virtual keyboard hideSearchMessage(); - // delete existing results before setting this as the current search ReaderTag searchTag = ReaderSearchService.getTagForSearchQuery(query); - ReaderPostTable.deletePostsWithTag(searchTag); mPostAdapter.setCurrentTag(searchTag); mCurrentSearchQuery = query;