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