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