[AiS] Add a structured representation of Answer JSON.

BUG=383907

Review URL: https://codereview.chromium.org/332943002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278217 0039d316-1c4b-4281-b951-d872f2087c98
parent 32809e9e
......@@ -19,6 +19,7 @@ public class OmniboxSuggestion {
private final String mDescription;
private final String mAnswerContents;
private final String mAnswerType;
private final SuggestionAnswer mAnswer;
private final String mFillIntoEdit;
private final String mUrl;
private final String mFormattedUrl;
......@@ -104,6 +105,14 @@ public class OmniboxSuggestion {
mFormattedUrl = formattedUrl;
mIsStarred = isStarred;
mIsDeletable = isDeletable;
if (!TextUtils.isEmpty(mAnswerContents)) {
// If any errors are encountered parsing the answer contents, this will return null and
// hasAnswer will return false, just as if there were no answer contents at all.
mAnswer = SuggestionAnswer.parseAnswerContents(mAnswerContents);
} else {
mAnswer = null;
}
}
/* TODO(groby): Remove - see http://crbug.com/375482 */
......@@ -138,6 +147,14 @@ public class OmniboxSuggestion {
return mAnswerType;
}
public SuggestionAnswer getAnswer() {
return mAnswer;
}
public boolean hasAnswer() {
return mAnswer != null;
}
public String getFillIntoEdit() {
return mFillIntoEdit;
}
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.omnibox;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* Structured representation of the JSON payload of a suggestion with an answer. An answer has
* exactly two image lines, so called because they are a combination of text and an optional
* image. Each image line has 0 or more text fields, each of which is required to contain a string
* and an integer type. The text fields are contained in a list and two optional named properties,
* referred to as "additional text" and "status text". The image, if present, contains a single
* string, which may be a URL or base64-encoded image data.
*
* When represented in the UI, these elements should be styled and layed out according to the
* specification at http://goto.google.com/ais_api.
*/
public class SuggestionAnswer {
private static final String TAG = "SuggestionAnswer";
private ImageLine mFirstLine;
private ImageLine mSecondLine;
private static final String ANSWERS_JSON_LINE = "l";
private static final String ANSWERS_JSON_IMAGE_LINE = "il";
private static final String ANSWERS_JSON_TEXT = "t";
private static final String ANSWERS_JSON_ADDITIONAL_TEXT = "at";
private static final String ANSWERS_JSON_STATUS_TEXT = "st";
private static final String ANSWERS_JSON_TEXT_TYPE = "tt";
private static final String ANSWERS_JSON_IMAGE = "i";
private static final String ANSWERS_JSON_IMAGE_DATA = "d";
private SuggestionAnswer() {
}
/**
* Parses the JSON representation of an answer and constructs a SuggestionAnswer from the
* contents.
*
* @param answerContents The JSON representation of an answer.
* @return A SuggestionAnswer with the answer contents or null if the contents are malformed or
* missing required elements.
*/
public static SuggestionAnswer parseAnswerContents(String answerContents) {
SuggestionAnswer answer = new SuggestionAnswer();
try {
JSONObject jsonAnswer = new JSONObject(answerContents);
JSONArray jsonLines = jsonAnswer.getJSONArray(ANSWERS_JSON_LINE);
if (jsonLines.length() != 2) {
Log.e(TAG, "Answer JSON doesn't contain exactly two lines: " + jsonAnswer);
return null;
}
answer.mFirstLine = new SuggestionAnswer.ImageLine(
jsonLines.getJSONObject(0).getJSONObject(ANSWERS_JSON_IMAGE_LINE));
answer.mSecondLine = new SuggestionAnswer.ImageLine(
jsonLines.getJSONObject(1).getJSONObject(ANSWERS_JSON_IMAGE_LINE));
} catch (JSONException e) {
Log.e(TAG, "Problem parsing answer JSON: " + e.getMessage());
return null;
}
return answer;
}
/**
* Returns the first of the two required image lines.
*/
public ImageLine getFirstLine() {
return mFirstLine;
}
/**
* Returns the second of the two required image lines.
*/
public ImageLine getSecondLine() {
return mSecondLine;
}
/**
* Represents a single line of an answer, containing any number of typed text fields and an
* optional image.
*/
public static class ImageLine {
private final List<TextField> mTextFields;
private final TextField mAdditionalText;
private final TextField mStatusText;
private final String mImage;
ImageLine(JSONObject jsonLine) throws JSONException {
mTextFields = new ArrayList<TextField>();
JSONArray textValues = jsonLine.getJSONArray(ANSWERS_JSON_TEXT);
for (int i = 0; i < textValues.length(); i++) {
mTextFields.add(new TextField(textValues.getJSONObject(i)));
}
mAdditionalText = jsonLine.has(ANSWERS_JSON_ADDITIONAL_TEXT) ?
new TextField(jsonLine.getJSONObject(ANSWERS_JSON_ADDITIONAL_TEXT)) :
null;
mStatusText = jsonLine.has(ANSWERS_JSON_STATUS_TEXT) ?
new TextField(jsonLine.getJSONObject(ANSWERS_JSON_STATUS_TEXT)) :
null;
mImage = jsonLine.has(ANSWERS_JSON_IMAGE) ?
jsonLine.getJSONObject(ANSWERS_JSON_IMAGE).getString(ANSWERS_JSON_IMAGE_DATA) :
null;
}
/**
* Return an unnamed list of text fields. These represent the main content of the line.
*/
public List<TextField> getTextFields() {
return mTextFields;
}
/**
* Returns true if the line contains an "additional text" field.
*/
public boolean hasAdditionalText() {
return mAdditionalText != null;
}
/**
* Return the "additional text" field.
*/
public TextField getAdditionalText() {
return mAdditionalText;
}
/**
* Returns true if the line contains an "status text" field.
*/
public boolean hasStatusText() {
return mStatusText != null;
}
/**
* Return the "status text" field.
*/
public TextField getStatusText() {
return mStatusText;
}
/**
* Returns true if the line contains an image.
*/
public boolean hasImage() {
return mImage != null;
}
/**
* Return the optional image (URL or base64-encoded image data).
*/
public String getImage() {
return mImage;
}
}
/**
* Represents one text field of an answer, containing a integer type and a string.
*/
public static class TextField {
private final int mType;
private final String mText;
TextField(JSONObject jsonTextField) throws JSONException {
mType = jsonTextField.getInt(ANSWERS_JSON_TEXT_TYPE);
mText = jsonTextField.getString(ANSWERS_JSON_TEXT);
}
public int getType() {
return mType;
}
public String getText() {
return mText;
}
}
}
\ No newline at end of file
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.omnibox;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
public class SuggestionAnswerTest extends TestCase {
@SmallTest
public void testMalformedJsonReturnsNull() {
String json = "} malformed json {";
SuggestionAnswer answer = SuggestionAnswer.parseAnswerContents(json);
assertNull(answer);
}
@SmallTest
public void testEmpyJsonReturnsNull() {
String json = "";
SuggestionAnswer answer = SuggestionAnswer.parseAnswerContents(json);
assertNull(answer);
}
@SmallTest
public void testOneLineReturnsNull() {
String json =
"{ 'l': [" +
" { 'il': { 't': [{ 't': 'text', 'tt': 8 }] } }, " +
"] }";
SuggestionAnswer answer = SuggestionAnswer.parseAnswerContents(json);
assertNull(answer);
}
@SmallTest
public void testTwoLinesDoesntReturnNull() {
String json =
"{ 'l': [" +
" { 'il': { 't': [{ 't': 'text', 'tt': 8 }] } }, " +
" { 'il': { 't': [{ 't': 'other text', 'tt': 5 }] } }" +
"] }";
SuggestionAnswer answer = SuggestionAnswer.parseAnswerContents(json);
assertNotNull(answer);
}
@SmallTest
public void testThreeLinesReturnsNull() {
String json =
"{ 'l': [" +
" { 'il': { 't': [{ 't': 'text', 'tt': 8 }] } }, " +
" { 'il': { 't': [{ 't': 'other text', 'tt': 5 }] } }" +
" { 'il': { 't': [{ 't': 'yet more text', 'tt': 13 }] } }" +
"] }";
SuggestionAnswer answer = SuggestionAnswer.parseAnswerContents(json);
assertNull(answer);
}
@SmallTest
public void testFiveLinesReturnsNull() {
String json =
"{ 'l': [" +
" { 'il': { 't': [{ 't': 'line 1', 'tt': 0 }] } }, " +
" { 'il': { 't': [{ 't': 'line 2', 'tt': 5 }] } }" +
" { 'il': { 't': [{ 't': 'line 3', 'tt': 13 }] } }" +
" { 'il': { 't': [{ 't': 'line 4', 'tt': 14 }] } }" +
" { 'il': { 't': [{ 't': 'line 5', 'tt': 5 }] } }" +
"] }";
SuggestionAnswer answer = SuggestionAnswer.parseAnswerContents(json);
assertNull(answer);
}
@SmallTest
public void testPropertyPresence() {
String json =
"{ 'l': [" +
" { 'il': { 't': [{ 't': 'text', 'tt': 8 }, { 't': 'moar', 'tt': 0 }], " +
" 'i': { 'd': 'http://example.com/foo.jpg' } } }, " +
" { 'il': { 't': [{ 't': 'other text', 'tt': 5 }], " +
" 'at': { 't': 'slatfotf', 'tt': 42 }, " +
" 'st': { 't': 'oh hi, Mark', 'tt': 7666 } } } " +
"] }";
SuggestionAnswer answer = SuggestionAnswer.parseAnswerContents(json);
SuggestionAnswer.ImageLine firstLine = answer.getFirstLine();
assertEquals(2, firstLine.getTextFields().size());
assertFalse(firstLine.hasAdditionalText());
assertFalse(firstLine.hasStatusText());
assertTrue(firstLine.hasImage());
SuggestionAnswer.ImageLine secondLine = answer.getSecondLine();
assertEquals(1, secondLine.getTextFields().size());
assertTrue(secondLine.hasAdditionalText());
assertTrue(secondLine.hasStatusText());
assertFalse(secondLine.hasImage());
}
@SmallTest
public void testContents() {
String json =
"{ 'l': [" +
" { 'il': { 't': [{ 't': 'text', 'tt': 8 }, { 't': 'moar', 'tt': 0 }], " +
" 'at': { 't': 'hi there', 'tt': 7 } } }, " +
" { 'il': { 't': [{ 't': 'ftw', 'tt': 6006 }], " +
" 'st': { 't': 'shop S-Mart', 'tt': 666 }, " +
" 'i': { 'd': 'Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGlj' } } } " +
"] }";
SuggestionAnswer answer = SuggestionAnswer.parseAnswerContents(json);
SuggestionAnswer.ImageLine firstLine = answer.getFirstLine();
assertEquals("text", firstLine.getTextFields().get(0).getText());
assertEquals(8, firstLine.getTextFields().get(0).getType());
assertEquals("moar", firstLine.getTextFields().get(1).getText());
assertEquals(0, firstLine.getTextFields().get(1).getType());
assertEquals("hi there", firstLine.getAdditionalText().getText());
assertEquals(7, firstLine.getAdditionalText().getType());
SuggestionAnswer.ImageLine secondLine = answer.getSecondLine();
assertEquals("ftw", secondLine.getTextFields().get(0).getText());
assertEquals(6006, secondLine.getTextFields().get(0).getType());
assertEquals("shop S-Mart", secondLine.getStatusText().getText());
assertEquals(666, secondLine.getStatusText().getType());
assertEquals("Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGlj", secondLine.getImage());
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment