Home | History | Annotate | Download | only in quicksearchbox
      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