DefaultSuggestionView.java revision 93bd2e70b8b08da1ec37fd0e990dac05551d2e90
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.quicksearchbox.ui; 18 19import com.android.quicksearchbox.R; 20import com.android.quicksearchbox.Source; 21import com.android.quicksearchbox.Suggestion; 22import com.android.quicksearchbox.SuggestionCursor; 23 24import android.content.Context; 25import android.content.res.ColorStateList; 26import android.graphics.drawable.Drawable; 27import android.text.Html; 28import android.text.Spannable; 29import android.text.SpannableString; 30import android.text.TextUtils; 31import android.text.style.TextAppearanceSpan; 32import android.util.AttributeSet; 33import android.util.Log; 34import android.view.View; 35import android.widget.ImageView; 36import android.widget.RelativeLayout; 37import android.widget.TextView; 38 39/** 40 * View for the items in the suggestions list. This includes promoted suggestions, 41 * sources, and suggestions under each source. 42 * 43 */ 44public class DefaultSuggestionView extends RelativeLayout implements SuggestionView { 45 46 private static final boolean DBG = false; 47 private static final String TAG = "QSB.SuggestionView"; 48 49 private TextView mText1; 50 private TextView mText2; 51 private ImageView mIcon1; 52 private ImageView mIcon2; 53 54 public DefaultSuggestionView(Context context, AttributeSet attrs, int defStyle) { 55 super(context, attrs, defStyle); 56 } 57 58 public DefaultSuggestionView(Context context, AttributeSet attrs) { 59 super(context, attrs); 60 } 61 62 public DefaultSuggestionView(Context context) { 63 super(context); 64 } 65 66 @Override 67 protected void onFinishInflate() { 68 super.onFinishInflate(); 69 mText1 = (TextView) findViewById(R.id.text1); 70 mText2 = (TextView) findViewById(R.id.text2); 71 mIcon1 = (ImageView) findViewById(R.id.icon1); 72 mIcon2 = (ImageView) findViewById(R.id.icon2); 73 } 74 75 public void bindAsSuggestion(SuggestionCursor suggestion) { 76 String format = suggestion.getSuggestionFormat(); 77 CharSequence text1 = formatText(suggestion.getSuggestionText1(), format); 78 CharSequence text2 = suggestion.getSuggestionText2Url(); 79 if (text2 != null) { 80 text2 = formatUrl(text2); 81 } else { 82 text2 = formatText(suggestion.getSuggestionText2(), format); 83 } 84 Drawable icon1 = getSuggestionDrawableIcon1(suggestion); 85 Drawable icon2 = getSuggestionDrawableIcon2(suggestion); 86 if (DBG) { 87 Log.d(TAG, "bindAsSuggestion(), text1=" + text1 + ",text2=" + text2 88 + ",icon1=" + icon1 + ",icon2=" + icon2); 89 } 90 // If there is no text for the second line, allow the first line to be up to two lines 91 if (TextUtils.isEmpty(text2)) { 92 mText1.setSingleLine(false); 93 mText1.setMaxLines(2); 94 mText1.setEllipsize(TextUtils.TruncateAt.START); 95 } else { 96 mText1.setSingleLine(true); 97 mText1.setMaxLines(1); 98 mText1.setEllipsize(TextUtils.TruncateAt.MIDDLE); 99 } 100 setText1(text1); 101 setText2(text2); 102 setIcon1(icon1); 103 setIcon2(icon2); 104 updateRefinable(suggestion); 105 } 106 107 protected void updateRefinable(SuggestionCursor suggestion) { 108 boolean refinable = 109 suggestion.isWebSearchSuggestion() 110 && mIcon2.getDrawable() == null 111 && !TextUtils.isEmpty(suggestion.getSuggestionQuery()); 112 setRefinable(suggestion, refinable); 113 } 114 115 protected void setRefinable(SuggestionCursor suggestion, boolean refinable) { 116 if (refinable) { 117 final int position = suggestion.getPosition(); 118 mIcon2.setOnClickListener(new View.OnClickListener() { 119 public void onClick(View v) { 120 Log.d(TAG, "Clicked query refine"); 121 SuggestionsView suggestions = (SuggestionsView) getParent(); 122 suggestions.onIcon2Clicked(position); 123 } 124 }); 125 Drawable icon2 = getContext().getResources().getDrawable(R.drawable.refine_query); 126 setIcon2(icon2); 127 } else { 128 mIcon2.setOnClickListener(null); 129 } 130 } 131 132 private CharSequence formatUrl(CharSequence url) { 133 SpannableString text = new SpannableString(url); 134 ColorStateList colors = getResources().getColorStateList(R.color.url_text); 135 text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null), 136 0, url.length(), 137 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 138 return text; 139 } 140 141 public Drawable getSuggestionDrawableIcon1(Suggestion suggestion) { 142 Source source = suggestion.getSuggestionSource(); 143 String iconId = suggestion.getSuggestionIcon1(); 144 Drawable icon1 = iconId == null ? null : source.getIcon(iconId); 145 return icon1 == null ? source.getSourceIcon() : icon1; 146 } 147 148 public Drawable getSuggestionDrawableIcon2(Suggestion suggestion) { 149 Source source = suggestion.getSuggestionSource(); 150 String iconId = suggestion.getSuggestionIcon2(); 151 return iconId == null ? null : source.getIcon(iconId); 152 } 153 154 private CharSequence formatText(String str, String format) { 155 boolean isHtml = "html".equals(format); 156 if (isHtml && looksLikeHtml(str)) { 157 return Html.fromHtml(str); 158 } else { 159 return str; 160 } 161 } 162 163 private boolean looksLikeHtml(String str) { 164 if (TextUtils.isEmpty(str)) return false; 165 for (int i = str.length() - 1; i >= 0; i--) { 166 char c = str.charAt(i); 167 if (c == '>' || c == '&') return true; 168 } 169 return false; 170 } 171 172 /** 173 * Sets the first text line. 174 */ 175 private void setText1(CharSequence text) { 176 mText1.setText(text); 177 } 178 179 /** 180 * Sets the second text line. 181 */ 182 private void setText2(CharSequence text) { 183 mText2.setText(text); 184 if (TextUtils.isEmpty(text)) { 185 mText2.setVisibility(GONE); 186 } else { 187 mText2.setVisibility(VISIBLE); 188 } 189 } 190 191 /** 192 * Sets the left-hand-side icon. 193 */ 194 private void setIcon1(Drawable icon) { 195 setViewDrawable(mIcon1, icon); 196 } 197 198 /** 199 * Sets the right-hand-side icon. 200 */ 201 private void setIcon2(Drawable icon) { 202 setViewDrawable(mIcon2, icon); 203 } 204 205 /** 206 * Sets the drawable in an image view, makes sure the view is only visible if there 207 * is a drawable. 208 */ 209 private static void setViewDrawable(ImageView v, Drawable drawable) { 210 // Set the icon even if the drawable is null, since we need to clear any 211 // previous icon. 212 v.setImageDrawable(drawable); 213 214 if (drawable == null) { 215 v.setVisibility(View.GONE); 216 } else { 217 v.setVisibility(View.VISIBLE); 218 219 // This is a hack to get any animated drawables (like a 'working' spinner) 220 // to animate. You have to setVisible true on an AnimationDrawable to get 221 // it to start animating, but it must first have been false or else the 222 // call to setVisible will be ineffective. We need to clear up the story 223 // about animated drawables in the future, see http://b/1878430. 224 drawable.setVisible(false, false); 225 drawable.setVisible(true, false); 226 } 227 } 228 229} 230