1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.omnibox; 6 7 import android.util.Log; 8 9 import org.json.JSONArray; 10 import org.json.JSONException; 11 import org.json.JSONObject; 12 13 import java.util.ArrayList; 14 import java.util.List; 15 16 /** 17 * Structured representation of the JSON payload of a suggestion with an answer. An answer has 18 * exactly two image lines, so called because they are a combination of text and an optional 19 * image. Each image line has 0 or more text fields, each of which is required to contain a string 20 * and an integer type. The text fields are contained in a list and two optional named properties, 21 * referred to as "additional text" and "status text". The image, if present, contains a single 22 * string, which may be a URL or base64-encoded image data. 23 * 24 * When represented in the UI, these elements should be styled and layed out according to the 25 * specification at http://goto.google.com/ais_api. 26 */ 27 public class SuggestionAnswer { 28 private static final String TAG = "SuggestionAnswer"; 29 30 private ImageLine mFirstLine; 31 private ImageLine mSecondLine; 32 33 private static final String ANSWERS_JSON_LINE = "l"; 34 private static final String ANSWERS_JSON_IMAGE_LINE = "il"; 35 private static final String ANSWERS_JSON_TEXT = "t"; 36 private static final String ANSWERS_JSON_ADDITIONAL_TEXT = "at"; 37 private static final String ANSWERS_JSON_STATUS_TEXT = "st"; 38 private static final String ANSWERS_JSON_TEXT_TYPE = "tt"; 39 private static final String ANSWERS_JSON_IMAGE = "i"; 40 private static final String ANSWERS_JSON_IMAGE_DATA = "d"; 41 SuggestionAnswer()42 private SuggestionAnswer() { 43 } 44 45 /** 46 * Parses the JSON representation of an answer and constructs a SuggestionAnswer from the 47 * contents. 48 * 49 * @param answerContents The JSON representation of an answer. 50 * @return A SuggestionAnswer with the answer contents or null if the contents are malformed or 51 * missing required elements. 52 */ parseAnswerContents(String answerContents)53 public static SuggestionAnswer parseAnswerContents(String answerContents) { 54 SuggestionAnswer answer = new SuggestionAnswer(); 55 56 try { 57 JSONObject jsonAnswer = new JSONObject(answerContents); 58 JSONArray jsonLines = jsonAnswer.getJSONArray(ANSWERS_JSON_LINE); 59 60 if (jsonLines.length() != 2) { 61 Log.e(TAG, "Answer JSON doesn't contain exactly two lines: " + jsonAnswer); 62 return null; 63 } 64 65 answer.mFirstLine = new SuggestionAnswer.ImageLine( 66 jsonLines.getJSONObject(0).getJSONObject(ANSWERS_JSON_IMAGE_LINE)); 67 answer.mSecondLine = new SuggestionAnswer.ImageLine( 68 jsonLines.getJSONObject(1).getJSONObject(ANSWERS_JSON_IMAGE_LINE)); 69 } catch (JSONException e) { 70 Log.e(TAG, "Problem parsing answer JSON: " + e.getMessage()); 71 return null; 72 } 73 74 return answer; 75 } 76 77 /** 78 * Returns the first of the two required image lines. 79 */ getFirstLine()80 public ImageLine getFirstLine() { 81 return mFirstLine; 82 } 83 84 /** 85 * Returns the second of the two required image lines. 86 */ getSecondLine()87 public ImageLine getSecondLine() { 88 return mSecondLine; 89 } 90 91 /** 92 * Represents a single line of an answer, containing any number of typed text fields and an 93 * optional image. 94 */ 95 public static class ImageLine { 96 private final List<TextField> mTextFields; 97 private final TextField mAdditionalText; 98 private final TextField mStatusText; 99 private final String mImage; 100 ImageLine(JSONObject jsonLine)101 ImageLine(JSONObject jsonLine) throws JSONException { 102 mTextFields = new ArrayList<TextField>(); 103 104 JSONArray textValues = jsonLine.getJSONArray(ANSWERS_JSON_TEXT); 105 for (int i = 0; i < textValues.length(); i++) { 106 mTextFields.add(new TextField(textValues.getJSONObject(i))); 107 } 108 109 mAdditionalText = jsonLine.has(ANSWERS_JSON_ADDITIONAL_TEXT) ? 110 new TextField(jsonLine.getJSONObject(ANSWERS_JSON_ADDITIONAL_TEXT)) : 111 null; 112 113 mStatusText = jsonLine.has(ANSWERS_JSON_STATUS_TEXT) ? 114 new TextField(jsonLine.getJSONObject(ANSWERS_JSON_STATUS_TEXT)) : 115 null; 116 117 mImage = jsonLine.has(ANSWERS_JSON_IMAGE) ? 118 jsonLine.getJSONObject(ANSWERS_JSON_IMAGE).getString(ANSWERS_JSON_IMAGE_DATA) : 119 null; 120 } 121 122 /** 123 * Return an unnamed list of text fields. These represent the main content of the line. 124 */ getTextFields()125 public List<TextField> getTextFields() { 126 return mTextFields; 127 } 128 129 /** 130 * Returns true if the line contains an "additional text" field. 131 */ hasAdditionalText()132 public boolean hasAdditionalText() { 133 return mAdditionalText != null; 134 } 135 136 /** 137 * Return the "additional text" field. 138 */ getAdditionalText()139 public TextField getAdditionalText() { 140 return mAdditionalText; 141 } 142 143 /** 144 * Returns true if the line contains an "status text" field. 145 */ hasStatusText()146 public boolean hasStatusText() { 147 return mStatusText != null; 148 } 149 150 /** 151 * Return the "status text" field. 152 */ getStatusText()153 public TextField getStatusText() { 154 return mStatusText; 155 } 156 157 /** 158 * Returns true if the line contains an image. 159 */ hasImage()160 public boolean hasImage() { 161 return mImage != null; 162 } 163 164 /** 165 * Return the optional image (URL or base64-encoded image data). 166 */ getImage()167 public String getImage() { 168 return mImage; 169 } 170 } 171 172 /** 173 * Represents one text field of an answer, containing a integer type and a string. 174 */ 175 public static class TextField { 176 private final int mType; 177 private final String mText; 178 TextField(JSONObject jsonTextField)179 TextField(JSONObject jsonTextField) throws JSONException { 180 mType = jsonTextField.getInt(ANSWERS_JSON_TEXT_TYPE); 181 mText = jsonTextField.getString(ANSWERS_JSON_TEXT); 182 } 183 getType()184 public int getType() { 185 return mType; 186 } 187 getText()188 public String getText() { 189 return mText; 190 } 191 } 192 }