RankAwarePromoter.java revision 4572856ac85bb53ea06e65d00beebdf336af9b27
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 17package com.android.quicksearchbox; 18 19import android.util.Log; 20 21import java.util.ArrayList; 22import java.util.Iterator; 23import java.util.LinkedList; 24 25/** 26 * A promoter that gives preference to suggestions from higher ranking corpora. 27 */ 28public class RankAwarePromoter implements Promoter<CorpusResult> { 29 30 private static final boolean DBG = false; 31 private static final String TAG = "QSB.RankAwarePromoter"; 32 33 private final Config mConfig; 34 35 public RankAwarePromoter(Config config) { 36 mConfig = config; 37 } 38 39 public void pickPromoted(SuggestionCursor shortcuts, ArrayList<CorpusResult> suggestions, 40 int maxPromoted, ListSuggestionCursor promoted) { 41 42 if (DBG) Log.d(TAG, "Available results: " + suggestions); 43 44 // Split non-empty results into default sources and other, positioned at first suggestion 45 LinkedList<CorpusResult> defaultResults = new LinkedList<CorpusResult>(); 46 LinkedList<CorpusResult> otherResults = new LinkedList<CorpusResult>(); 47 for (CorpusResult result : suggestions) { 48 if (result.getCount() > 0) { 49 result.moveTo(0); 50 Corpus corpus = result.getCorpus(); 51 if (corpus == null || corpus.isCorpusDefaultEnabled()) { 52 defaultResults.add(result); 53 } else { 54 otherResults.add(result); 55 } 56 } 57 } 58 59 // Share the top slots equally among each of the default corpora 60 if (maxPromoted > 0 && !defaultResults.isEmpty()) { 61 int slotsToFill = Math.min(getSlotsAboveKeyboard() - promoted.getCount(), maxPromoted); 62 if (slotsToFill > 0) { 63 int stripeSize = Math.max(1, slotsToFill / defaultResults.size()); 64 maxPromoted -= roundRobin(defaultResults, slotsToFill, stripeSize, promoted); 65 } 66 } 67 68 // Then try to fill with the remaining promoted results 69 if (maxPromoted > 0 && !defaultResults.isEmpty()) { 70 int stripeSize = Math.max(1, maxPromoted / defaultResults.size()); 71 maxPromoted -= roundRobin(defaultResults, maxPromoted, stripeSize, promoted); 72 // We may still have a few slots left 73 maxPromoted -= roundRobin(defaultResults, maxPromoted, maxPromoted, promoted); 74 } 75 76 // Then try to fill with the rest 77 if (maxPromoted > 0 && !otherResults.isEmpty()) { 78 int stripeSize = Math.max(1, maxPromoted / otherResults.size()); 79 maxPromoted -= roundRobin(otherResults, maxPromoted, stripeSize, promoted); 80 // We may still have a few slots left 81 maxPromoted -= roundRobin(otherResults, maxPromoted, maxPromoted, promoted); 82 } 83 84 if (DBG) Log.d(TAG, "Returning " + promoted.toString()); 85 } 86 87 private int getSlotsAboveKeyboard() { 88 return mConfig.getNumSuggestionsAboveKeyboard(); 89 } 90 91 /** 92 * Promotes "stripes" of suggestions from each corpus. 93 * 94 * @param results the list of CorpusResults from which to promote. 95 * Exhausted CorpusResults are removed from the list. 96 * @param maxPromoted maximum number of suggestions to promote. 97 * @param stripeSize number of suggestions to take from each corpus. 98 * @param promoted the list to which promoted suggestions are added. 99 * @return the number of suggestions actually promoted. 100 */ 101 private int roundRobin(LinkedList<CorpusResult> results, int maxPromoted, int stripeSize, 102 ListSuggestionCursor promoted) { 103 int count = 0; 104 if (maxPromoted > 0 && !results.isEmpty()) { 105 for (Iterator<CorpusResult> iter = results.iterator(); 106 count < maxPromoted && iter.hasNext();) { 107 CorpusResult result = iter.next(); 108 count += promote(result, stripeSize, promoted); 109 if (result.getPosition() == result.getCount()) { 110 iter.remove(); 111 } 112 } 113 } 114 return count; 115 } 116 117 /** 118 * Copies suggestions from a SuggestionCursor to the list of promoted suggestions. 119 * 120 * @param cursor from which to copy the suggestions 121 * @param count maximum number of suggestions to copy 122 * @param promoted the list to which to add the suggestions 123 * @return the number of suggestions actually copied. 124 */ 125 private int promote(SuggestionCursor cursor, int count, ListSuggestionCursor promoted) { 126 if (count < 1 || cursor.getPosition() >= cursor.getCount()) { 127 return 0; 128 } 129 int i = 0; 130 do { 131 promoted.add(new SuggestionPosition(cursor)); 132 i++; 133 } while (cursor.moveToNext() && i < count); 134 return i; 135 } 136} 137