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