// 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 static final String ANSWERS_JSON_NUMBER_OF_LINES = "ln";
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;
private final int mNumLines;
TextField(JSONObject jsonTextField) throws JSONException {
mType = jsonTextField.getInt(ANSWERS_JSON_TEXT_TYPE);
mText = jsonTextField.getString(ANSWERS_JSON_TEXT);
mNumLines = jsonTextField.has(ANSWERS_JSON_NUMBER_OF_LINES)
? jsonTextField.getInt(ANSWERS_JSON_NUMBER_OF_LINES)
: -1;
}
public int getType() {
return mType;
}
public String getText() {
return mText;
}
public boolean hasNumLines() {
return mNumLines != -1;
}
public int getNumLines() {
return mNumLines;
}
}
}