Bug 959297 - Get description and approx. reading time for reading list items. r=liuche, r=lucasr, r=margaret, r=rnewman

This commit is contained in:
Sola Ogunsakin 2014-03-12 14:53:25 -07:00
Родитель a8936cf3db
Коммит 93a0a6fc56
7 изменённых файлов: 122 добавлений и 23 удалений

Просмотреть файл

@ -20,6 +20,7 @@ import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.animation.ViewHelper; import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.db.BrowserContract.Combined;
import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.favicons.Favicons; import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.LoadFaviconTask; import org.mozilla.gecko.favicons.LoadFaviconTask;
@ -59,6 +60,7 @@ import org.mozilla.gecko.widget.GeckoActionProvider;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
@ -385,7 +387,7 @@ abstract public class BrowserApp extends GeckoApp
}); });
} }
void handleReaderAdded(int result, final String title, final String url) { private void handleReaderAdded(int result, final ContentValues values) {
if (result != READER_ADD_SUCCESS) { if (result != READER_ADD_SUCCESS) {
if (result == READER_ADD_FAILED) { if (result == READER_ADD_FAILED) {
showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT); showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT);
@ -399,7 +401,7 @@ abstract public class BrowserApp extends GeckoApp
ThreadUtils.postToBackgroundThread(new Runnable() { ThreadUtils.postToBackgroundThread(new Runnable() {
@Override @Override
public void run() { public void run() {
BrowserDB.addReadingListItem(getContentResolver(), title, url); BrowserDB.addReadingListItem(getContentResolver(), values);
showToast(R.string.reading_list_added, Toast.LENGTH_SHORT); showToast(R.string.reading_list_added, Toast.LENGTH_SHORT);
final int count = BrowserDB.getReadingListCount(getContentResolver()); final int count = BrowserDB.getReadingListCount(getContentResolver());
@ -408,6 +410,15 @@ abstract public class BrowserApp extends GeckoApp
}); });
} }
private ContentValues messageToReadingListContentValues(JSONObject message) {
final ContentValues values = new ContentValues();
values.put(ReadingListItems.URL, message.optString("url"));
values.put(ReadingListItems.TITLE, message.optString("title"));
values.put(ReadingListItems.LENGTH, message.optInt("length"));
values.put(ReadingListItems.EXCERPT, message.optString("excerpt"));
return values;
}
void handleReaderRemoved(final String url) { void handleReaderRemoved(final String url) {
ThreadUtils.postToBackgroundThread(new Runnable() { ThreadUtils.postToBackgroundThread(new Runnable() {
@Override @Override
@ -1127,9 +1138,7 @@ abstract public class BrowserApp extends GeckoApp
handleReaderListStatusRequest(message.getString("url")); handleReaderListStatusRequest(message.getString("url"));
} else if (event.equals("Reader:Added")) { } else if (event.equals("Reader:Added")) {
final int result = message.getInt("result"); final int result = message.getInt("result");
final String title = message.getString("title"); handleReaderAdded(result, messageToReadingListContentValues(message));
final String url = message.getString("url");
handleReaderAdded(result, title, url);
} else if (event.equals("Reader:Removed")) { } else if (event.equals("Reader:Removed")) {
final String url = message.getString("url"); final String url = message.getString("url");
handleReaderRemoved(url); handleReaderRemoved(url);

Просмотреть файл

@ -396,6 +396,9 @@ public class BrowserContract {
public static final String DEFAULT_SORT_ORDER = _ID + " DESC"; public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
public static final String[] DEFAULT_PROJECTION = new String[] { _ID, URL, TITLE, EXCERPT, LENGTH }; public static final String[] DEFAULT_PROJECTION = new String[] { _ID, URL, TITLE, EXCERPT, LENGTH };
// Minimum fields required to create a reading list item.
public static final String[] REQUIRED_FIELDS = { Bookmarks.URL, Bookmarks.TITLE };
public static final String TABLE_NAME = "reading_list"; public static final String TABLE_NAME = "reading_list";
} }

Просмотреть файл

@ -13,6 +13,7 @@ import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
import org.mozilla.gecko.mozglue.RobocopTarget; import org.mozilla.gecko.mozglue.RobocopTarget;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.database.Cursor; import android.database.Cursor;
import android.database.CursorWrapper; import android.database.CursorWrapper;
@ -98,7 +99,7 @@ public class BrowserDB {
@RobocopTarget @RobocopTarget
public void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword); public void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword);
public void addReadingListItem(ContentResolver cr, String title, String uri); public void addReadingListItem(ContentResolver cr, ContentValues values);
public void removeReadingListItemWithURL(ContentResolver cr, String uri); public void removeReadingListItemWithURL(ContentResolver cr, String uri);
@ -271,8 +272,8 @@ public class BrowserDB {
sDb.updateBookmark(cr, id, uri, title, keyword); sDb.updateBookmark(cr, id, uri, title, keyword);
} }
public static void addReadingListItem(ContentResolver cr, String title, String uri) { public static void addReadingListItem(ContentResolver cr, ContentValues values) {
sDb.addReadingListItem(cr, title, uri); sDb.addReadingListItem(cr, values);
} }
public static void removeReadingListItemWithURL(ContentResolver cr, String uri) { public static void removeReadingListItemWithURL(ContentResolver cr, String uri) {

Просмотреть файл

@ -699,11 +699,16 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
} }
@Override @Override
public void addReadingListItem(ContentResolver cr, String title, String uri) { public void addReadingListItem(ContentResolver cr, ContentValues values) {
final ContentValues values = new ContentValues(); // Check that required fields are present.
for (String field: ReadingListItems.REQUIRED_FIELDS) {
if (!values.containsKey(field)) {
throw new IllegalArgumentException("Missing required field for reading list item: " + field);
}
}
// Clear delete flag if necessary
values.put(ReadingListItems.IS_DELETED, 0); values.put(ReadingListItems.IS_DELETED, 0);
values.put(ReadingListItems.URL, uri);
values.put(ReadingListItems.TITLE, title);
// Restore deleted record if possible // Restore deleted record if possible
final Uri insertUri = mReadingListUriWithProfile final Uri insertUri = mReadingListUriWithProfile
@ -714,7 +719,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
final int updated = cr.update(insertUri, final int updated = cr.update(insertUri,
values, values,
ReadingListItems.URL + " = ? ", ReadingListItems.URL + " = ? ",
new String[] { uri }); new String[] { values.getAsString(ReadingListItems.URL) });
debug("Updated " + updated + " rows to new modified time."); debug("Updated " + updated + " rows to new modified time.");
} }

Просмотреть файл

@ -718,6 +718,77 @@ Readability.prototype = {
} }
}, },
/**
* Attempts to get the excerpt from these
* sources in the following order:
* - meta description tag
* - open-graph description
* - twitter cards description
* - article's first paragraph
* If no excerpt is found, an empty string will be
* returned.
*
* @param Element - root element of the processed version page
* @return String - excerpt of the article
**/
_getExcerpt: function(articleContent) {
let values = {};
let metaElements = this._doc.getElementsByTagName("meta");
// Match "description", or Twitter's "twitter:description" (Cards)
// in name attribute.
let namePattern = /^\s*((twitter)\s*:\s*)?description\s*$/gi;
// Match Facebook's og:description (Open Graph) in property attribute.
let propertyPattern = /^\s*og\s*:\s*description\s*$/gi;
// Find description tags.
for (let i = 0; i < metaElements.length; i++) {
let element = metaElements[i];
let elementName = element.getAttribute("name");
let elementProperty = element.getAttribute("property");
let name;
if (namePattern.test(elementName)) {
name = elementName;
} else if (propertyPattern.test(elementProperty)) {
name = elementProperty;
}
if (name) {
let content = element.getAttribute("content");
if (content) {
// Convert to lowercase and remove any whitespace
// so we can match below.
name = name.toLowerCase().replace(/\s/g, '');
values[name] = content.trim();
}
}
}
if ("description" in values) {
return values["description"];
}
if ("og:description" in values) {
// Use facebook open graph description.
return values["og:description"];
}
if ("twitter:description" in values) {
// Use twitter cards description.
return values["twitter:description"];
}
// No description meta tags, use the article's first paragraph.
let paragraphs = articleContent.getElementsByTagName("p");
if (paragraphs.length > 0) {
return paragraphs[0].textContent;
}
return "";
},
/** /**
* Removes script tags from the document. * Removes script tags from the document.
* *
@ -1434,9 +1505,13 @@ Readability.prototype = {
// }).bind(this), 500); // }).bind(this), 500);
// } // }
let excerpt = this._getExcerpt(articleContent);
return { title: articleTitle, return { title: articleTitle,
byline: this._articleByline, byline: this._articleByline,
dir: this._articleDir, dir: this._articleDir,
content: articleContent.innerHTML }; content: articleContent.innerHTML,
length: articleContent.textContent.length,
excerpt: excerpt };
} }
}; };

Просмотреть файл

@ -349,6 +349,8 @@ AboutReader.prototype = {
result: result, result: result,
title: this._article.title, title: this._article.title,
url: this._article.url, url: this._article.url,
length: this._article.length,
excerpt: this._article.excerpt
}); });
}.bind(this)); }.bind(this));
} else { } else {

Просмотреть файл

@ -7433,33 +7433,36 @@ let Reader = {
throw new Error("Reader:Add requires a tabID or an URL as argument"); throw new Error("Reader:Add requires a tabID or an URL as argument");
} }
let sendResult = function(result, title) { let sendResult = function(result, article) {
this.log("Reader:Add success=" + result + ", url=" + url + ", title=" + title); article = article || {};
this.log("Reader:Add success=" + result + ", url=" + url + ", title=" + article.title + ", excerpt=" + article.excerpt);
sendMessageToJava({ sendMessageToJava({
type: "Reader:Added", type: "Reader:Added",
result: result, result: result,
title: title, title: article.title,
url: url, url: url,
length: article.length,
excerpt: article.excerpt
}); });
}.bind(this); }.bind(this);
let handleArticle = function(article) { let handleArticle = function(article) {
if (!article) { if (!article) {
sendResult(this.READER_ADD_FAILED, ""); sendResult(this.READER_ADD_FAILED, null);
return; return;
} }
this.storeArticleInCache(article, function(success) { this.storeArticleInCache(article, function(success) {
let result = (success ? this.READER_ADD_SUCCESS : this.READER_ADD_FAILED); let result = (success ? this.READER_ADD_SUCCESS : this.READER_ADD_FAILED);
sendResult(result, article.title); sendResult(result, article);
}.bind(this)); }.bind(this));
}.bind(this); }.bind(this);
this.getArticleFromCache(urlWithoutRef, function (article) { this.getArticleFromCache(urlWithoutRef, function (article) {
// If the article is already in reading list, bail // If the article is already in reading list, bail
if (article) { if (article) {
sendResult(this.READER_ADD_DUPLICATE, ""); sendResult(this.READER_ADD_DUPLICATE, null);
return; return;
} }
@ -7473,13 +7476,14 @@ let Reader = {
} }
case "Reader:Remove": { case "Reader:Remove": {
this.removeArticleFromCache(aData, function(success) { let url = aData;
this.log("Reader:Remove success=" + success + ", url=" + aData); this.removeArticleFromCache(url, function(success) {
this.log("Reader:Remove success=" + success + ", url=" + url);
if (success) { if (success) {
sendMessageToJava({ sendMessageToJava({
type: "Reader:Removed", type: "Reader:Removed",
url: aData url: url
}); });
} }
}.bind(this)); }.bind(this));