Skip to content
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
1d9c4e0
First pass at reader search service
nbradbury May 13, 2016
24a4bf5
Submit search query on suggestion click
nbradbury May 13, 2016
5813aed
Empty view for no results now works
nbradbury May 13, 2016
f9a5b4b
Added "Searching..." message while search is active
nbradbury May 13, 2016
ca18d72
Search results are now stored in main post table
nbradbury May 13, 2016
aa4b578
Updated getTagForSearchQuery
nbradbury May 13, 2016
41477b5
Added ReaderPostListType.SEARCH_RESULTS
nbradbury May 13, 2016
39f2a1f
Search results are now correctly displayed when search finishes
nbradbury May 13, 2016
89190a4
Clear existing results before performing search
nbradbury May 13, 2016
b366ecc
Return max 10 results
nbradbury May 13, 2016
b543159
Set the autocomplete search threshold to 1
nbradbury May 13, 2016
60a7fb2
Move setThreshold() to initial setup
nbradbury May 13, 2016
dcbfb3e
Show boxes & pages anim when searching
nbradbury May 13, 2016
87840c5
Retain search activity title upon rotation
nbradbury May 13, 2016
9e5aaf5
No longer removing previous search results
nbradbury May 13, 2016
c358566
Use custom layout for search suggestions
nbradbury May 14, 2016
3f687aa
Apply max suggestions
nbradbury May 14, 2016
fef6b5c
Clean up getQueryStrings
nbradbury May 14, 2016
f5696bd
First pass at infinite scroll for search
nbradbury May 14, 2016
fd35877
Second pass at infinite scroll for search
nbradbury May 15, 2016
1dfe743
Third pass at infinite scroll for search
nbradbury May 15, 2016
bd3551f
Parse score from search results to determine sort order
nbradbury May 15, 2016
4367231
Updated comment about timestamps
nbradbury May 15, 2016
7d529c4
Change post timestamp to REAL (double)
nbradbury May 15, 2016
132fe18
First pass at showing search results in main reader (no separate acti…
nbradbury May 15, 2016
db99f1e
2nd pass at showing search results in main reader
nbradbury May 15, 2016
7112499
3rd pass at showing search results in main reader
nbradbury May 15, 2016
5e7d397
Fixed search message for landscape
nbradbury May 15, 2016
32f883e
Fixed device rotation issues, now re-using empty view for search message
nbradbury May 15, 2016
1363d6f
Simplified post adapter to use the current tag when searching
nbradbury May 15, 2016
007ae27
No longer clearing cached searches again
nbradbury May 15, 2016
0f1b346
Make sure toolbar showing search keyword is showing when returning to…
nbradbury May 15, 2016
d4cd5d0
Merge branch 'develop' of https://github.com/wordpress-mobile/WordPre…
nbradbury May 15, 2016
72987f1
Use a CursorAdapter rather than a SimpleCursorAdapter for search sugg…
nbradbury May 15, 2016
5136967
Enable deleting search suggestions
nbradbury May 15, 2016
4032f4c
Renamed suggestion table and dropped counter
nbradbury May 15, 2016
769acfb
Increment db version counter
nbradbury May 15, 2016
ca4c263
Rewrote `setEmptyTitleAndDescription` to better handle search
nbradbury May 15, 2016
d0912a9
Added `showEmptyView()` and `hideEmptyView()`
nbradbury May 16, 2016
11eb8b8
Removed `newInstanceForSearch`
nbradbury May 16, 2016
0808f6e
Show search message even in landscape
nbradbury May 16, 2016
946eed1
Clear query when searchView is collapsed
nbradbury May 16, 2016
c973d68
Merge branch 'develop' of https://github.com/wordpress-mobile/WordPre…
nbradbury May 16, 2016
ffdf77a
Purge all searches when reader db is purged at startup
nbradbury May 16, 2016
dfca3da
Merge branch 'develop' of https://github.com/wordpress-mobile/WordPre…
nbradbury May 17, 2016
fcacbc1
Merge remote-tracking branch 'origin/feature/reader-search-master' in…
nbradbury May 17, 2016
6662401
Simplified and clarified the usage of SearchPostsEnded
nbradbury May 17, 2016
3a42260
Remove cached results before requesting search results
nbradbury May 17, 2016
d67aabe
Removed todo (separate issue filed)
nbradbury May 19, 2016
3bbf7b6
Merge branch 'feature/reader-search-master' of https://github.com/wor…
nbradbury May 19, 2016
84a5048
Merge branch 'feature/reader-search-master' of https://github.com/wor…
nbradbury May 20, 2016
6c572bc
Don't repopulate search suggestions unless the filter has changed
nbradbury May 23, 2016
0cc5bd2
Merge branch 'feature/reader-search-master' of https://github.com/wor…
nbradbury May 23, 2016
b64f2a8
Ensure `filter` isn't null
nbradbury May 23, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions WordPress/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,10 @@
android:name=".ui.reader.services.ReaderPostService"
android:exported="false"
android:label="Reader Post Service" />
<service
android:name=".ui.reader.services.ReaderSearchService"
android:exported="false"
android:label="Reader Search Service" />
<service
android:name=".ui.reader.services.ReaderCommentService"
android:exported="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 118;

/*
* version history
Expand Down Expand Up @@ -68,6 +68,8 @@ 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
* 118 - renamed tbl_search_history to tbl_search_suggestions
*/

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,"
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -624,7 +635,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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering why this attribute is declared as a double, being that elsewhere the timestamp can be held as a long. The only place I can think of is maybe forcing this is this one https://github.com/wordpress-mobile/WordPress-Android/blob/feature/reader-search-results/WordPress/src/main/java/org/wordpress/android/models/ReaderPost.java#L122 - is that maybe because the API is returning a value there that could grow into a double? if not, then we should probably leave/refactor everything to be treated as long, as per this https://developer.android.com/reference/java/sql/Timestamp.html

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering why this attribute is declared as a double, being that elsewhere the timestamp can be held as a long.

So, until search came along, that timestamp column really was a timestamp, but it's only purpose has been to determine the sort order of posts.

Search results, however, include a "score" - a double which states how relevant each post is to the submitted query - and this determines how posts are sorted. To get this to work within the existing code, I changed the timestamp column to a double and store the "score" in there.

And now that I look at it, I confess to not being proud of it :)

What really should happen is for me to rename that timestamp column (and related field in the ReaderPost model) to better reflect its purpose. If that would address your (perfectly valid) complaint, I'd like to file that as a separate issue to avoid further bloating this PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely! Let's file that as a separate issue then and ref it here. I have to confess I doubted of the actual value of mentioning this at all but... when you write near-to-perfect code, it's easier for the reviewer to point fingers on stuff that don't really matter - LOL

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Referenced here #4119

Copy link
Copy Markdown
Contributor Author

@nbradbury nbradbury May 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but... when you write near-to-perfect code, it's easier for the reviewer to point fingers on stuff that don't really matter

Hah! Thanks for the ego boost, but after you've been here for a while you may feel otherwise ;)

Update: About my code, that is!

stmtPosts.bindString(20, post.getPublished());
stmtPosts.bindLong (21, post.numReplies);
stmtPosts.bindLong (22, post.numLikes);
Expand Down Expand Up @@ -839,7 +850,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"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,73 +9,71 @@
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
* 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 ("
+ " query_string TEXT NOT NULL COLLATE NOCASE PRIMARY KEY,"
+ " date_used TEXT,"
+ " counter INTEGER DEFAULT 1)");
db.execSQL("CREATE TABLE tbl_search_suggestions ("
+ " _id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " query_string TEXT NOT NULL COLLATE NOCASE,"
+ " 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);
}
}

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);
public static void deleteQueryString(@NonNull String query) {
String[] args = new String[]{query};
ReaderDatabase.getWritableDb().delete("tbl_search_suggestions", "query_string=?", args);
}

public static List<String> getQueryStrings() {
return getQueryStrings(null);
}
public static List<String> getQueryStrings(String filter) {
List<String> queries = new ArrayList<>();
Cursor cursor;
/**
* 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 Cursor getQueryStringCursor(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 * FROM tbl_search_suggestions";
args = null;
} else {
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});
sql = "SELECT * FROM tbl_search_suggestions WHERE query_string LIKE ?";
args = new String[]{filter + "%"};
}

try {
while (cursor.moveToNext()) {
queries.add(cursor.getString(0));
}
return queries;
} finally {
SqlUtils.closeCursor(cursor);
sql += " ORDER BY date_used DESC";

if (max > 0) {
sql += " LIMIT " + max;
}

return ReaderDatabase.getReadableDb().rawQuery(sql, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -116,14 +116,19 @@ 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);
// a post's timestamp determines its sort order
if (json.has("score")) {
// search results include a "score" that should be used for sorting
post.timestamp = json.optDouble("score");
} else {
post.timestamp = DateTimeUtils.iso8601ToTimestamp(post.published);
// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,6 @@ public void removeOnScrollListener(RecyclerView.OnScrollListener listener) {
}
}

public RecyclerView getInternalRecyclerView() {
return mRecyclerView;
}

public void hideToolbar(){
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, might have been left from a previous refactor. Good catch. 👍

mAppBarLayout.setExpanded(false, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import org.wordpress.android.R;
import org.wordpress.android.analytics.AnalyticsTracker;
import org.wordpress.android.datasets.ReaderSearchTable;
import org.wordpress.android.models.AccountHelper;
import org.wordpress.android.models.ReaderComment;
import org.wordpress.android.models.ReaderPost;
Expand Down Expand Up @@ -134,22 +133,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);

// 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
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.wordpress.android.ui.reader;

import android.support.annotation.NonNull;

import org.wordpress.android.models.ReaderTag;
import org.wordpress.android.ui.reader.actions.ReaderActions;
import org.wordpress.android.ui.reader.services.ReaderPostService;
Expand Down Expand Up @@ -63,6 +65,40 @@ public ReaderPostService.UpdateAction getAction() {
}
}

public static class SearchPostsStarted {
private final String mQuery;
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 mDidSucceed;
private final int mOffset;
public SearchPostsEnded(@NonNull String query, int offset, boolean didSucceed) {
mQuery = query;
mOffset = offset;
mDidSucceed = didSucceed;
}
public boolean didSucceed() {
return mDidSucceed;
}
public String getQuery() {
return mQuery;
}
public int getOffset() {
return mOffset;
}
}

public static class UpdateCommentsStarted {}
public static class UpdateCommentsEnded {
private final ReaderActions.UpdateResult mResult;
Expand Down
Loading