Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5380aa29 authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 7512 into donut

* changes:
  Get rid of drawing hacks for search dialog suggestions
parents ee716fa2 391eaa3b
Loading
Loading
Loading
Loading
+62 −191
Original line number Diff line number Diff line
@@ -23,22 +23,20 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.server.search.SearchableInfo;
import android.text.Html;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
@@ -63,6 +61,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
    private SearchableInfo mSearchable;
    private Context mProviderContext;
    private WeakHashMap<String, Drawable> mOutsideDrawablesCache;
    private SparseArray<Drawable> mBackgroundsCache;
    private boolean mGlobalSearchMode;

    // Cached column indexes, updated when the cursor changes.
@@ -106,6 +105,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
        mProviderContext = mSearchable.getProviderContext(mContext, activityContext);

        mOutsideDrawablesCache = outsideDrawablesCache;
        mBackgroundsCache = new SparseArray<Drawable>();
        mGlobalSearchMode = globalSearchMode;

        mStartSpinnerRunnable = new Runnable() {
@@ -256,7 +256,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
     */
    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View v = new SuggestionItemView(context, cursor);
        View v = super.newView(context, cursor, parent);
        v.setTag(new ChildViewCache(v));
        return v;
    }
@@ -301,18 +301,13 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
        if (mBackgroundColorCol != -1) {
            backgroundColor = cursor.getInt(mBackgroundColorCol);
        }
        ((SuggestionItemView)view).setColor(backgroundColor);
        Drawable background = getItemBackground(backgroundColor);
        view.setBackgroundDrawable(background);

        final boolean isHtml = mFormatCol > 0 && "html".equals(cursor.getString(mFormatCol));
        String text1 = null;
        if (mText1Col >= 0) {
            text1 = cursor.getString(mText1Col);
        }
        String text2 = null;
        if (mText2Col >= 0) {
            text2 = cursor.getString(mText2Col);
        }
        ((SuggestionItemView)view).setTextStrings(text1, text2, isHtml, mProviderContext);
        setViewText(cursor, views.mText1, mText1Col, isHtml);
        setViewText(cursor, views.mText2, mText2Col, isHtml);

        if (views.mIcon1 != null) {
            setViewDrawable(views.mIcon1, getIcon1(cursor));
        }
@@ -321,6 +316,57 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
        }
    }

    /**
     * Gets a drawable with no color when selected or pressed, and the given color when
     * neither selected nor pressed.
     *
     * @return A drawable, or {@code null} if the given color is transparent.
     */
    private Drawable getItemBackground(int backgroundColor) {
        if (backgroundColor == 0) {
            return null;
        } else {
            Drawable cachedBg = mBackgroundsCache.get(backgroundColor);
            if (cachedBg != null) {
                if (DBG) Log.d(LOG_TAG, "Background cache hit for color " + backgroundColor);
                // copy the drawable so that they don't share states
                return cachedBg.getConstantState().newDrawable();
            }
            if (DBG) Log.d(LOG_TAG, "Creating new background for color " + backgroundColor);
            ColorDrawable transparent = new ColorDrawable(0);
            ColorDrawable background = new ColorDrawable(backgroundColor);
            StateListDrawable newBg = new StateListDrawable();
            newBg.addState(new int[]{android.R.attr.state_selected}, transparent);
            newBg.addState(new int[]{android.R.attr.state_pressed}, transparent);
            newBg.addState(new int[]{}, background);
            mBackgroundsCache.put(backgroundColor, newBg);
            return newBg;
        }
    }

    private void setViewText(Cursor cursor, TextView v, int textCol, boolean isHtml) {
        if (v == null) {
            return;
        }
        CharSequence text = null;
        if (textCol >= 0) {
            String str = cursor.getString(textCol);
            if (isHtml && !TextUtils.isEmpty(str)) {
                text = Html.fromHtml(str);
            } else {
                text = str;
            }
        }
        // Set the text even if it's null, since we need to clear any previous text.
        v.setText(text);

        if (TextUtils.isEmpty(text)) {
            v.setVisibility(View.GONE);
        } else {
            v.setVisibility(View.VISIBLE);
        }
    }

    private Drawable getIcon1(Cursor cursor) {
        if (mIconName1Col < 0) {
            return null;
@@ -594,179 +640,4 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
        return cursor.getString(col);
    }

    /**
     * A parent viewgroup class which holds the actual suggestion item as a child.
     *
     * The sole purpose of this class is to draw the given background color when the item is in
     * normal state and not draw the background color when it is pressed, so that when pressed the
     * list view's selection highlight will be displayed properly (if we draw our background it
     * draws on top of the list view selection highlight).
     */
    private class SuggestionItemView extends ViewGroup {
        /**
         * Parses a given HTMl string and manages Spannable variants of the string for different
         * states of the suggestion item (selected, pressed and normal). Colors for these different
         * states are specified in the html font tag color attribute in the format '@<RESOURCEID>'
         * where RESOURCEID is the ID of a ColorStateList or Color resource. 
         */
        private class MultiStateText {
            private CharSequence mNormal = null;  // text to display in normal state.
            private CharSequence mSelected = null;  // text to display in selected state.
            private CharSequence mPressed = null;  // text to display in pressed state.
            private String mPlainText = null;  // valid if the text is stateless plain text.

            public MultiStateText(boolean isHtml, String text, Context context) {
                if (!isHtml || text == null) {
                    mPlainText = text;
                    return;
                }

                String textNormal = text;
                String textSelected = text;
                String textPressed = text;
                int textLength = text.length();
                int start = text.indexOf("\"@");

                // For each font color attribute which has the value in the form '@<RESOURCEID>',
                // try to load the resource and create the display strings for the 3 states.
                while (start >= 0) {
                    start++;
                    int end = text.indexOf("\"", start);
                    if (end == -1) break;

                    String colorIdString = text.substring(start, end);
                    int colorId = Integer.parseInt(colorIdString.substring(1));
                    try {
                        // The following call works both for color lists and colors.
                        ColorStateList csl = context.getResources().getColorStateList(colorId);
                        int normalColor = csl.getColorForState(
                                View.EMPTY_STATE_SET, csl.getDefaultColor());
                        int selectedColor = csl.getColorForState(
                                View.SELECTED_STATE_SET, csl.getDefaultColor());
                        int pressedColor = csl.getColorForState(
                                View.PRESSED_STATE_SET, csl.getDefaultColor());

                        // Convert the int color values into a hex string, and strip the first 2
                        // characters which will be the alpha (html doesn't want this).
                        textNormal = textNormal.replace(colorIdString,
                                "#" + Integer.toHexString(normalColor).substring(2));
                        textSelected = textSelected.replace(colorIdString,
                                "#" + Integer.toHexString(selectedColor).substring(2));
                        textPressed = textPressed.replace(colorIdString,
                                "#" + Integer.toHexString(pressedColor).substring(2));
                    } catch (Resources.NotFoundException e) {
                        // Nothing to do.
                    }

                    start = text.indexOf("\"@", end);
                }
                mNormal = Html.fromHtml(textNormal);
                mSelected = Html.fromHtml(textSelected);
                mPressed = Html.fromHtml(textPressed);
            }
            public CharSequence normal() {
                return (mPlainText != null) ? mPlainText : mNormal;
            }
            public CharSequence selected() {
                return (mPlainText != null) ? mPlainText : mSelected;
            }
            public CharSequence pressed() {
                return (mPlainText != null) ? mPlainText : mPressed;
            }
        }

        private int mBackgroundColor;  // the background color to draw in normal state.
        private View mView;  // the suggestion item's view.
        private MultiStateText mText1Strings = null;
        private MultiStateText mText2Strings = null;

        protected SuggestionItemView(Context context, Cursor cursor) {
            // Initialize ourselves
            super(context);
            mBackgroundColor = 0;  // transparent by default.

            // For our layout use the default list item height from the current theme.
            TypedValue lineHeight = new TypedValue();
            context.getTheme().resolveAttribute(
                    com.android.internal.R.attr.searchResultListItemHeight, lineHeight, true);
            DisplayMetrics metrics = new DisplayMetrics();
            metrics.setToDefaults();
            AbsListView.LayoutParams layout = new AbsListView.LayoutParams(
                    AbsListView.LayoutParams.FILL_PARENT,
                    (int)lineHeight.getDimension(metrics));

            setLayoutParams(layout);

            // Initialize the child view
            mView = SuggestionsAdapter.super.newView(context, cursor, this);
            if (mView != null) {
                addView(mView, layout.width, layout.height);
                mView.setVisibility(View.VISIBLE);
            }
        }

        private void setInitialTextForView(TextView view, MultiStateText multiState,
                String plainText) {
            // Set the text even if it's null, since we need to clear any previous text.
            CharSequence text = (multiState != null) ? multiState.normal() : plainText;
            view.setText(text);

            if (TextUtils.isEmpty(text)) {
                view.setVisibility(View.GONE);
            } else {
                view.setVisibility(View.VISIBLE);
            }
        }

        public void setTextStrings(String text1, String text2, boolean isHtml, Context context) {
            mText1Strings = new MultiStateText(isHtml, text1, context);
            mText2Strings = new MultiStateText(isHtml, text2, context);

            ChildViewCache views = (ChildViewCache) getTag();
            setInitialTextForView(views.mText1, mText1Strings, text1);
            setInitialTextForView(views.mText2, mText2Strings, text2);
        }

        public void updateTextViewContentIfRequired() {
            // Check if the pressed or selected state has changed since the last call.
            boolean isPressedNow = isPressed();
            boolean isSelectedNow = isSelected();

            ChildViewCache views = (ChildViewCache) getTag();
            views.mText1.setText((isPressedNow ? mText1Strings.pressed() :
                (isSelectedNow ? mText1Strings.selected() : mText1Strings.normal())));
            views.mText2.setText((isPressedNow ? mText2Strings.pressed() :
                (isSelectedNow ? mText2Strings.selected() : mText2Strings.normal())));
        }

        public void setColor(int backgroundColor) {
            mBackgroundColor = backgroundColor;
        }

        @Override
        public void dispatchDraw(Canvas canvas) {
            updateTextViewContentIfRequired();

            if (mBackgroundColor != 0 && !isPressed() && !isSelected()) {
                canvas.drawColor(mBackgroundColor);
            }
            super.dispatchDraw(canvas);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if (mView != null) {
                mView.measure(widthMeasureSpec, heightMeasureSpec);
            }
        }

        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            if (mView != null) {
                mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
            }
        }
    }

}