Home | History | Annotate | Download | only in ui
      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 
     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
     60     public boolean isEmpty() {
     61         return mAdapter.getGroupCount() == 0;
     62     }
     63 
     64     @Override
     65     public boolean willPublishNonPromotedSuggestions() {
     66         return true;
     67     }
     68 
     69     @Override
     70     public SuggestionPosition getSuggestion(long suggestionId) {
     71         return mAdapter.getChildById(suggestionId);
     72     }
     73 
     74     @Override
     75     public ExpandableListAdapter getListAdapter() {
     76         return mAdapter;
     77     }
     78 
     79     @Override
     80     protected void notifyDataSetChanged() {
     81         mAdapter.buildCorpusGroups();
     82         mAdapter.notifyDataSetChanged();
     83     }
     84 
     85     @Override
     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 
     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
    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
    140         public long getCombinedGroupId(long groupId) {
    141             return groupId << GROUP_SHIFT;
    142         }
    143 
    144         public int getChildPosition(long childId) {
    145             return (int) (childId & CHILD_MASK) - 1;
    146         }
    147 
    148         public int getGroupPosition(long childId) {
    149             return (int) ((childId >> GROUP_SHIFT) & CHILD_MASK);
    150         }
    151 
    152         @Override
    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 
    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
    173         public long getChildId(int groupPosition, int childPosition) {
    174             return childPosition;
    175         }
    176 
    177         @Override
    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
    187         public int getChildrenCount(int groupPosition) {
    188             SuggestionCursor group = getGroup(groupPosition);
    189             return group == null ? 0 : group.getCount();
    190         }
    191 
    192         @Override
    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 
    203         private int promotedCount() {
    204             SuggestionCursor promoted = getCurrentPromotedSuggestions();
    205             return (promoted == null ? 0 : promoted.getCount());
    206         }
    207 
    208         private int promotedGroupCount() {
    209             return (promotedCount() == 0) ? 0 : 1;
    210         }
    211 
    212         private int corpusGroupCount() {
    213             return mCorpusGroups == null ? 0 : mCorpusGroups.size();
    214         }
    215 
    216         @Override
    217         public int getGroupCount() {
    218             return promotedGroupCount() + corpusGroupCount();
    219         }
    220 
    221         @Override
    222         public long getGroupId(int groupPosition) {
    223             return groupPosition;
    224         }
    225 
    226         @Override
    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
    246         public boolean hasStableIds() {
    247             return false;
    248         }
    249 
    250         @Override
    251         public boolean isChildSelectable(int groupPosition, int childPosition) {
    252             return true;
    253         }
    254 
    255         @Override
    256         public int getChildType(int groupPosition, int childPosition) {
    257             return getSuggestionViewType(getGroup(groupPosition), childPosition);
    258         }
    259 
    260         @Override
    261         public int getChildTypeCount() {
    262             return getSuggestionViewTypeCount();
    263         }
    264     }
    265 
    266 }
    267