ClusteredSuggestionsAdapter.java revision 7a0c3a7c6fdabce949b59e0a2c6ec1d44a140c24
1/*
2 * Copyright (C) 2010 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 */
16package com.android.quicksearchbox.ui;
17
18import com.android.quicksearchbox.Corpora;
19import com.android.quicksearchbox.CorpusResult;
20import com.android.quicksearchbox.R;
21import com.android.quicksearchbox.Suggestion;
22import com.android.quicksearchbox.SuggestionCursor;
23import com.android.quicksearchbox.SuggestionPosition;
24import com.android.quicksearchbox.Suggestions;
25
26import android.content.Context;
27import android.view.LayoutInflater;
28import android.view.View;
29import android.view.ViewGroup;
30import android.widget.BaseExpandableListAdapter;
31import android.widget.ExpandableListAdapter;
32
33import java.util.ArrayList;
34
35/**
36 * Adapter for suggestions list where suggestions are clustered by corpus.
37 */
38public class ClusteredSuggestionsAdapter extends SuggestionsAdapterBase<ExpandableListAdapter> {
39
40    private final static int GROUP_SHIFT = 32;
41    private final static long CHILD_MASK = 0xffffffff;
42
43    private final Adapter mAdapter;
44    private final Context mContext;
45    private final LayoutInflater mInflater;
46
47    public ClusteredSuggestionsAdapter(SuggestionViewFactory fallbackFactory,
48            Corpora corpora, Context context) {
49        super(fallbackFactory, corpora);
50        mAdapter = new Adapter();
51        mContext = context;
52        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
53    }
54
55    @Override
56    public boolean isEmpty() {
57        return mAdapter.getGroupCount() == 0;
58    }
59
60    @Override
61    public SuggestionPosition getSuggestion(long suggestionId) {
62        return mAdapter.getChildById(suggestionId);
63    }
64
65    @Override
66    public ExpandableListAdapter getListAdapter() {
67        return mAdapter;
68    }
69
70    @Override
71    protected void notifyDataSetChanged() {
72        mAdapter.buildCorpusGroups();
73        mAdapter.notifyDataSetChanged();
74    }
75
76    @Override
77    protected void notifyDataSetInvalidated() {
78        mAdapter.buildCorpusGroups();
79        mAdapter.notifyDataSetInvalidated();
80    }
81
82    private class Adapter extends BaseExpandableListAdapter {
83
84        private ArrayList<CorpusResult> mCorpusGroups;
85
86        public void buildCorpusGroups() {
87            Suggestions suggestions = getSuggestions();
88            if (suggestions == null) {
89                mCorpusGroups = null;
90            } else {
91                if (mCorpusGroups == null) {
92                    mCorpusGroups = new ArrayList<CorpusResult>();
93                } else {
94                    mCorpusGroups.clear();
95                }
96                // TODO order corpora by usage
97                for (CorpusResult result : suggestions.getCorpusResults()) {
98                    if (!result.getCorpus().isWebCorpus() && result.getCount() > 0) {
99                        mCorpusGroups.add(result);
100                    }
101                }
102            }
103        }
104
105        @Override
106        public long getCombinedChildId(long groupId, long childId) {
107            return (groupId << GROUP_SHIFT) | (childId & CHILD_MASK);
108        }
109
110        @Override
111        public long getCombinedGroupId(long groupId) {
112            return groupId << GROUP_SHIFT;
113        }
114
115        public int getChildPosition(long childId) {
116            return (int) (childId & CHILD_MASK);
117        }
118
119        public int getGroupPosition(long childId) {
120            return (int) ((childId >> GROUP_SHIFT) & CHILD_MASK);
121        }
122
123        @Override
124        public Suggestion getChild(int groupPosition, int childPosition) {
125            SuggestionCursor c = getGroup(groupPosition);
126            c.moveTo(childPosition);
127                return new SuggestionPosition(c, childPosition);
128        }
129
130        public SuggestionPosition getChildById(long childId) {
131            return new SuggestionPosition(getGroup(getGroupPosition(childId)),
132                    getChildPosition(getChildPosition(childId)));
133        }
134
135        @Override
136        public long getChildId(int groupPosition, int childPosition) {
137            return childPosition;
138        }
139
140        @Override
141        public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
142                View convertView, ViewGroup parent) {
143            SuggestionCursor cursor = getGroup(groupPosition);
144            return getView(cursor, childPosition, getCombinedChildId(groupPosition, childPosition),
145                    convertView, parent);
146        }
147
148        @Override
149        public int getChildrenCount(int groupPosition) {
150            return getGroup(groupPosition).getCount();
151        }
152
153        @Override
154        public SuggestionCursor getGroup(int groupPosition) {
155            if (groupPosition < promotedGroupCount()) {
156                return getCurrentPromotedSuggestions();
157            } else {
158                return mCorpusGroups.get(groupPosition - promotedGroupCount());
159            }
160        }
161
162        private int promotedCount() {
163            SuggestionCursor promoted = getCurrentPromotedSuggestions();
164            return (promoted == null ? 0 : promoted.getCount());
165        }
166
167        private int promotedGroupCount() {
168            return promotedCount() == 0 ? 0 : 1;
169        }
170
171        private int corpusGroupCount() {
172            return mCorpusGroups == null ? 0 : mCorpusGroups.size();
173        }
174
175        @Override
176        public int getGroupCount() {
177            return promotedGroupCount() + corpusGroupCount();
178        }
179
180        @Override
181        public long getGroupId(int groupPosition) {
182            return groupPosition;
183        }
184
185        @Override
186        public View getGroupView(
187                int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
188            if (convertView == null) {
189                convertView = mInflater.inflate(R.layout.suggestion_group, parent, false);
190            }
191            if (groupPosition == 0) {
192                // don't show the group separator for the first group, to avoid seeing an empty
193                // gap at the top of the list.
194                convertView.getLayoutParams().height = 0;
195            } else {
196                convertView.getLayoutParams().height = mContext.getResources().
197                        getDimensionPixelSize(R.dimen.suggestion_group_spacing);
198            }
199            // since we've fiddled with the layout params:
200            convertView.requestLayout();
201            return convertView;
202        }
203
204        @Override
205        public boolean hasStableIds() {
206            return false;
207        }
208
209        @Override
210        public boolean isChildSelectable(int groupPosition, int childPosition) {
211            return true;
212        }
213
214        @Override
215        public int getChildType(int groupPosition, int childPosition) {
216            return getSuggestionViewType(getGroup(groupPosition), childPosition);
217        }
218
219        @Override
220        public int getChildTypeCount() {
221            return getSuggestionViewTypeCount();
222        }
223    }
224
225}
226