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