• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package com.android.quicksearchbox.ui;
17 
18 import com.android.quicksearchbox.CorpusResult;
19 import com.android.quicksearchbox.ListSuggestionCursor;
20 import com.android.quicksearchbox.R;
21 import com.android.quicksearchbox.Suggestion;
22 import com.android.quicksearchbox.SuggestionCursor;
23 import com.android.quicksearchbox.SuggestionPosition;
24 import com.android.quicksearchbox.SuggestionUtils;
25 import com.android.quicksearchbox.Suggestions;
26 
27 import android.content.Context;
28 import android.util.Log;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.widget.BaseExpandableListAdapter;
33 import android.widget.ExpandableListAdapter;
34 
35 import java.util.ArrayList;
36 import java.util.HashSet;
37 
38 /**
39  * Adapter for suggestions list where suggestions are clustered by corpus.
40  */
41 public class ClusteredSuggestionsAdapter extends SuggestionsAdapterBase<ExpandableListAdapter> {
42 
43     private static final String TAG = "QSB.ClusteredSuggestionsAdapter";
44 
45     private final static int GROUP_SHIFT = 32;
46     private final static long CHILD_MASK = 0xffffffff;
47 
48     private final Adapter mAdapter;
49     private final Context mContext;
50     private final LayoutInflater mInflater;
51 
ClusteredSuggestionsAdapter(SuggestionViewFactory viewFactory, Context context)52     public ClusteredSuggestionsAdapter(SuggestionViewFactory viewFactory, Context context) {
53         super(viewFactory);
54         mAdapter = new Adapter();
55         mContext = context;
56         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
57     }
58 
59     @Override
isEmpty()60     public boolean isEmpty() {
61         return mAdapter.getGroupCount() == 0;
62     }
63 
64     @Override
willPublishNonPromotedSuggestions()65     public boolean willPublishNonPromotedSuggestions() {
66         return true;
67     }
68 
69     @Override
getSuggestion(long suggestionId)70     public SuggestionPosition getSuggestion(long suggestionId) {
71         return mAdapter.getChildById(suggestionId);
72     }
73 
74     @Override
getListAdapter()75     public ExpandableListAdapter getListAdapter() {
76         return mAdapter;
77     }
78 
79     @Override
notifyDataSetChanged()80     protected void notifyDataSetChanged() {
81         mAdapter.buildCorpusGroups();
82         mAdapter.notifyDataSetChanged();
83     }
84 
85     @Override
notifyDataSetInvalidated()86     protected void notifyDataSetInvalidated() {
87         mAdapter.buildCorpusGroups();
88         mAdapter.notifyDataSetInvalidated();
89     }
90 
91     private class Adapter extends BaseExpandableListAdapter {
92 
93         private ArrayList<SuggestionCursor> mCorpusGroups;
94 
buildCorpusGroups()95         public void buildCorpusGroups() {
96             Suggestions suggestions = getSuggestions();
97             SuggestionCursor promoted = getCurrentPromotedSuggestions();
98             HashSet<String> promotedSuggestions = new HashSet<String>();
99             if (promoted != null && promoted.getCount() > 0) {
100                 promoted.moveTo(0);
101                 do {
102                     promotedSuggestions.add(SuggestionUtils.getSuggestionKey(promoted));
103                 } while (promoted.moveToNext());
104             }
105             if (suggestions == null) {
106                 mCorpusGroups = null;
107             } else {
108                 if (mCorpusGroups == null) {
109                     mCorpusGroups = new ArrayList<SuggestionCursor>();
110                 } else {
111                     mCorpusGroups.clear();
112                 }
113                 for (CorpusResult result : suggestions.getCorpusResults()) {
114                     ListSuggestionCursor corpusSuggestions = new ListSuggestionCursor(
115                             result.getUserQuery());
116                     for (int i = 0; i < result.getCount(); ++i) {
117                         result.moveTo(i);
118                         if (!result.isWebSearchSuggestion()) {
119                             if (!promotedSuggestions.contains(
120                                     SuggestionUtils.getSuggestionKey(result))) {
121                                 corpusSuggestions.add(new SuggestionPosition(result, i));
122                             }
123                         }
124                     }
125                     if (corpusSuggestions.getCount() > 0) {
126                         mCorpusGroups.add(corpusSuggestions);
127                     }
128                 }
129             }
130         }
131 
132         @Override
getCombinedChildId(long groupId, long childId)133         public long getCombinedChildId(long groupId, long childId) {
134             // add one to the child ID to ensure that the group elements do not have the same ID
135             // as the first child within the group.
136             return (groupId << GROUP_SHIFT) | ((childId + 1) & CHILD_MASK);
137         }
138 
139         @Override
getCombinedGroupId(long groupId)140         public long getCombinedGroupId(long groupId) {
141             return groupId << GROUP_SHIFT;
142         }
143 
getChildPosition(long childId)144         public int getChildPosition(long childId) {
145             return (int) (childId & CHILD_MASK) - 1;
146         }
147 
getGroupPosition(long childId)148         public int getGroupPosition(long childId) {
149             return (int) ((childId >> GROUP_SHIFT) & CHILD_MASK);
150         }
151 
152         @Override
getChild(int groupPosition, int childPosition)153         public Suggestion getChild(int groupPosition, int childPosition) {
154             SuggestionCursor c = getGroup(groupPosition);
155             if (c != null) {
156                 c.moveTo(childPosition);
157                 return new SuggestionPosition(c, childPosition);
158             }
159             return null;
160         }
161 
getChildById(long childId)162         public SuggestionPosition getChildById(long childId) {
163             SuggestionCursor groupCursor = getGroup(getGroupPosition(childId));
164             if (groupCursor != null) {
165                 return new SuggestionPosition(groupCursor, getChildPosition(childId));
166             } else {
167                 Log.w(TAG, "Invalid childId " + Long.toHexString(childId) + " (invalid group)");
168                 return null;
169             }
170         }
171 
172         @Override
getChildId(int groupPosition, int childPosition)173         public long getChildId(int groupPosition, int childPosition) {
174             return childPosition;
175         }
176 
177         @Override
getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)178         public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
179                 View convertView, ViewGroup parent) {
180             SuggestionCursor cursor = getGroup(groupPosition);
181             if (cursor == null) return null;
182             return getView(cursor, childPosition, getCombinedChildId(groupPosition, childPosition),
183                     convertView, parent);
184         }
185 
186         @Override
getChildrenCount(int groupPosition)187         public int getChildrenCount(int groupPosition) {
188             SuggestionCursor group = getGroup(groupPosition);
189             return group == null ? 0 : group.getCount();
190         }
191 
192         @Override
getGroup(int groupPosition)193         public SuggestionCursor getGroup(int groupPosition) {
194             if (groupPosition < promotedGroupCount()) {
195                 return getCurrentPromotedSuggestions();
196             } else {
197                 int pos = groupPosition - promotedGroupCount();
198                 if ((pos < 0 ) || (pos >= mCorpusGroups.size())) return null;
199                 return mCorpusGroups.get(pos);
200             }
201         }
202 
promotedCount()203         private int promotedCount() {
204             SuggestionCursor promoted = getCurrentPromotedSuggestions();
205             return (promoted == null ? 0 : promoted.getCount());
206         }
207 
promotedGroupCount()208         private int promotedGroupCount() {
209             return (promotedCount() == 0) ? 0 : 1;
210         }
211 
corpusGroupCount()212         private int corpusGroupCount() {
213             return mCorpusGroups == null ? 0 : mCorpusGroups.size();
214         }
215 
216         @Override
getGroupCount()217         public int getGroupCount() {
218             return promotedGroupCount() + corpusGroupCount();
219         }
220 
221         @Override
getGroupId(int groupPosition)222         public long getGroupId(int groupPosition) {
223             return groupPosition;
224         }
225 
226         @Override
getGroupView( int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)227         public View getGroupView(
228                 int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
229             if (convertView == null) {
230                 convertView = mInflater.inflate(R.layout.suggestion_group, parent, false);
231             }
232             if (groupPosition == 0) {
233                 // don't show the group separator for the first group, to avoid seeing an empty
234                 // gap at the top of the list.
235                 convertView.getLayoutParams().height = 0;
236             } else {
237                 convertView.getLayoutParams().height = mContext.getResources().
238                         getDimensionPixelSize(R.dimen.suggestion_group_spacing);
239             }
240             // since we've fiddled with the layout params:
241             convertView.requestLayout();
242             return convertView;
243         }
244 
245         @Override
hasStableIds()246         public boolean hasStableIds() {
247             return false;
248         }
249 
250         @Override
isChildSelectable(int groupPosition, int childPosition)251         public boolean isChildSelectable(int groupPosition, int childPosition) {
252             return true;
253         }
254 
255         @Override
getChildType(int groupPosition, int childPosition)256         public int getChildType(int groupPosition, int childPosition) {
257             return getSuggestionViewType(getGroup(groupPosition), childPosition);
258         }
259 
260         @Override
getChildTypeCount()261         public int getChildTypeCount() {
262             return getSuggestionViewTypeCount();
263         }
264     }
265 
266 }
267