Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a563f1b5 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "First pass at removing duplicate results"

parents 63558780 52b6f888
Loading
Loading
Loading
Loading
+52 −1
Original line number Diff line number Diff line
@@ -144,7 +144,7 @@ public class DatabaseResultLoader extends AsyncLoader<List<? extends SearchResul
        results.addAll(secondaryResults);
        results.addAll(tertiaryResults);

        return results;
        return removeDuplicates(results);
    }

    @Override
@@ -300,4 +300,55 @@ public class DatabaseResultLoader extends AsyncLoader<List<? extends SearchResul
        }
        return selection;
    }

    /**
     * Goes through the list of search results and verifies that none of the results are duplicates.
     * A duplicate is quantified by a result with the same Title and the same non-empty Summary.
     *
     * The method walks through the results starting with the highest priority result. It removes
     * the duplicates by doing the first rule that applies below:
     * - If a result is inline, remove the intent result.
     * - Remove the lower rank item.
     * @param results A list of results with potential duplicates
     * @return The list of results with duplicates removed.
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    List<SearchResult> removeDuplicates(List<SearchResult> results) {
        SearchResult primaryResult, secondaryResult;

        // We accept the O(n^2) solution because the number of results is small.
        for (int i = results.size() - 1; i >= 0; i--) {
            secondaryResult = results.get(i);

            for (int j = i - 1; j >= 0; j--) {
                primaryResult = results.get(j);
                if (areDuplicateResults(primaryResult, secondaryResult)) {

                    if (primaryResult.viewType != ResultPayload.PayloadType.INTENT) {
                        // Case where both payloads are inline
                        results.remove(i);
                        break;
                    } else if (secondaryResult.viewType != ResultPayload.PayloadType.INTENT) {
                        // Case where only second result is inline
                        results.remove(j);
                        i--; // shift the top index to reflect the lower element being removed
                    } else {
                        // Case where both payloads are intent
                        results.remove(i);
                    }
                }
            }
        }
        return results;
    }

    /**
     * @return True when the two {@link SearchResult SearchResults} have the same title, and the same
     * non-empty summary.
     */
    private boolean areDuplicateResults(SearchResult primary, SearchResult secondary) {
        return TextUtils.equals(primary.title, secondary.title)
                && TextUtils.equals(primary.summary, secondary.summary)
                && !TextUtils.isEmpty(primary.summary);
    }
}
 No newline at end of file
+190 −12
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settings.search2;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;

import com.android.settings.SettingsRobolectricTestRunner;
@@ -38,6 +39,7 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import java.util.ArrayList;
import java.util.List;

import static com.google.common.truth.Truth.assertThat;
@@ -58,6 +60,15 @@ public class DatabaseResultLoaderTest {
    private Context mContext;
    private DatabaseResultLoader loader;

    private final String titleOne = "titleOne";
    private final String titleTwo = "titleTwo";
    private final String titleThree = "titleThree";
    private final String titleFour = "titleFour";
    private final String summaryOne = "summaryOne";
    private final String summaryTwo = "summaryTwo";
    private final String summaryThree = "summaryThree";
    private final String summaryFour = "summaryFour";

    SQLiteDatabase mDb;

    @Before
@@ -104,49 +115,49 @@ public class DatabaseResultLoaderTest {
    }

    @Test
    public void testSpecialCaseWord_MatchesNonPrefix() {
    public void testSpecialCaseWord_matchesNonPrefix() {
        insertSpecialCase("Data usage");
        loader = new DatabaseResultLoader(mContext, "usage", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
    }

    @Test
    public void testSpecialCaseSpace_Matches() {
    public void testSpecialCaseSpace_matches() {
        insertSpecialCase("space");
        loader = new DatabaseResultLoader(mContext, " space ", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
    }

    @Test
    public void testSpecialCaseDash_MatchesWordNoDash() {
    public void testSpecialCaseDash_matchesWordNoDash() {
        insertSpecialCase("wi-fi calling");
        loader = new DatabaseResultLoader(mContext, "wifi", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
    }

    @Test
    public void testSpecialCaseDash_MatchesWordWithDash() {
    public void testSpecialCaseDash_matchesWordWithDash() {
        insertSpecialCase("priorités seulment");
        loader = new DatabaseResultLoader(mContext, "priorités", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
    }

    @Test
    public void testSpecialCaseDash_MatchesWordWithoutDash() {
    public void testSpecialCaseDash_matchesWordWithoutDash() {
        insertSpecialCase("priorités seulment");
        loader = new DatabaseResultLoader(mContext, "priorites", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
    }

    @Test
    public void testSpecialCaseDash_MatchesEntireQueryWithoutDash() {
    public void testSpecialCaseDash_matchesEntireQueryWithoutDash() {
        insertSpecialCase("wi-fi calling");
        loader = new DatabaseResultLoader(mContext, "wifi calling", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
    }

    @Test
    public void testSpecialCasePrefix_MatchesPrefixOfEntry() {
    public void testSpecialCasePrefix_matchesPrefixOfEntry() {
        insertSpecialCase("Photos");
        loader = new DatabaseResultLoader(mContext, "pho", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
@@ -160,14 +171,14 @@ public class DatabaseResultLoaderTest {
    }

    @Test
    public void testSpecialCaseMultiWordPrefix_MatchesPrefixOfEntry() {
    public void testSpecialCaseMultiWordPrefix_matchesPrefixOfEntry() {
        insertSpecialCase("Apps Notifications");
        loader = new DatabaseResultLoader(mContext, "Apps", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
    }

    @Test
    public void testSpecialCaseMultiWordPrefix_MatchesSecondWordPrefixOfEntry() {
    public void testSpecialCaseMultiWordPrefix_matchesSecondWordPrefixOfEntry() {
        insertSpecialCase("Apps Notifications");
        loader = new DatabaseResultLoader(mContext, "Not", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
@@ -188,21 +199,188 @@ public class DatabaseResultLoaderTest {
    }

    @Test
    public void testSpecialCaseMultiWordPrefixWithSpecial_MatchesPrefixOfEntry() {
    public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfEntry() {
        insertSpecialCase("Apps & Notifications");
        loader = new DatabaseResultLoader(mContext, "App", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
    }

    @Test
    public void testSpecialCaseMultiWordPrefixWithSpecial_MatchesPrefixOfSecondEntry() {
    public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfSecondEntry() {
        insertSpecialCase("Apps & Notifications");
        loader = new DatabaseResultLoader(mContext, "No", mSiteMapManager);
        assertThat(loader.loadInBackground().size()).isEqualTo(1);
    }

    @Test
    public void testSpecialCaseTwoWords_FirstWordMatches_RanksHigher() {
    public void testDeDupe_noDuplicates_originalListReturn() {
        // Three elements with unique titles and summaries
        List<SearchResult> results = new ArrayList();
        IntentPayload intentPayload = new IntentPayload(new Intent());

        SearchResult.Builder builder = new SearchResult.Builder();
        builder.addTitle(titleOne)
                .addSummary(summaryOne)
                .addPayload(intentPayload);
        SearchResult resultOne = builder.build();
        results.add(resultOne);

        builder.addTitle(titleTwo)
                .addSummary(summaryTwo);
        SearchResult resultTwo = builder.build();
        results.add(resultTwo);

        builder.addTitle(titleThree)
                .addSummary(summaryThree);
        SearchResult resultThree = builder.build();
        results.add(resultThree);

        loader = new DatabaseResultLoader(mContext, "", null);
        loader.removeDuplicates(results);
        assertThat(results.size()).isEqualTo(3);
        assertThat(results.get(0)).isEqualTo(resultOne);
        assertThat(results.get(1)).isEqualTo(resultTwo);
        assertThat(results.get(2)).isEqualTo(resultThree);
    }

    @Test
    public void testDeDupe_oneDuplicate_duplicateRemoved() {
        List<SearchResult> results = new ArrayList();
        IntentPayload intentPayload = new IntentPayload(new Intent());

        SearchResult.Builder builder = new SearchResult.Builder();
        builder.addTitle(titleOne)
                .addSummary(summaryOne)
                .addRank(0)
                .addPayload(intentPayload);
        SearchResult resultOne = builder.build();
        results.add(resultOne);

        // Duplicate of the first element
        builder.addTitle(titleOne)
                .addSummary(summaryOne)
                .addRank(1);
        SearchResult resultTwo = builder.build();
        results.add(resultTwo);

        // Unique
        builder.addTitle(titleThree)
                .addSummary(summaryThree);
        SearchResult resultThree = builder.build();
        results.add(resultThree);

        loader = new DatabaseResultLoader(mContext, "", null);
        loader.removeDuplicates(results);
        assertThat(results.size()).isEqualTo(2);
        assertThat(results.get(0)).isEqualTo(resultOne);
        assertThat(results.get(1)).isEqualTo(resultThree);
    }

    @Test
    public void testDeDupe_firstDupeInline_secondDuplicateRemoved() {
        List<SearchResult> results = new ArrayList();
        InlineSwitchPayload inlinePayload = new InlineSwitchPayload("", 0,
                null);
        IntentPayload intentPayload = new IntentPayload(new Intent());

        SearchResult.Builder builder = new SearchResult.Builder();
        // Inline result
        builder.addTitle(titleOne)
                .addSummary(summaryOne)
                .addRank(0)
                .addPayload(inlinePayload);
        SearchResult resultOne = builder.build();
        results.add(resultOne);

        // Duplicate of first result, but Intent Result. Should be removed.
        builder.addTitle(titleOne)
                .addSummary(summaryOne)
                .addRank(1)
                .addPayload(intentPayload);
        SearchResult resultTwo = builder.build();
        results.add(resultTwo);

        // Unique
        builder.addTitle(titleThree)
                .addSummary(summaryThree);
        SearchResult resultThree = builder.build();
        results.add(resultThree);

        loader = new DatabaseResultLoader(mContext, "", null);
        loader.removeDuplicates(results);
        assertThat(results.size()).isEqualTo(2);
        assertThat(results.get(0)).isEqualTo(resultOne);
        assertThat(results.get(1)).isEqualTo(resultThree);
    }

    @Test
    public void testDeDupe_secondDupeInline_firstDuplicateRemoved() {
        /*
         * Create a list as follows:
         * (5) Intent Four
         * (4) Inline Two
         * (3) Intent Three
         * (2) Intent Two
         * (1) Intent One
         *
         * After removing duplicates:
         * (4) Intent Four
         * (3) Inline Two
         * (2) Intent Three
         * (1) Intent One
         */
        List<SearchResult> results = new ArrayList();
        InlineSwitchPayload inlinePayload = new InlineSwitchPayload("", 0,
                null);
        IntentPayload intentPayload = new IntentPayload(new Intent());


        SearchResult.Builder builder = new SearchResult.Builder();
        // Intent One
        builder.addTitle(titleOne)
                .addSummary(summaryOne)
                .addPayload(intentPayload);
        SearchResult resultOne = builder.build();
        results.add(resultOne);

        // Intent Two
        builder.addTitle(titleTwo)
                .addSummary(summaryTwo)
                .addPayload(intentPayload);
        SearchResult resultTwo = builder.build();
        results.add(resultTwo);

        // Intent Three
        builder.addTitle(titleThree)
                .addSummary(summaryThree);
        SearchResult resultThree = builder.build();
        results.add(resultThree);

        // Inline Two
        builder.addTitle(titleTwo)
                .addSummary(summaryTwo)
                .addPayload(inlinePayload);
        SearchResult resultFour = builder.build();
        results.add(resultFour);

        // Intent Four
        builder.addTitle(titleFour)
                .addSummary(summaryOne)
                .addPayload(intentPayload);
        SearchResult resultFive = builder.build();
        results.add(resultFive);

        loader = new DatabaseResultLoader(mContext, "", null);
        loader.removeDuplicates(results);
        assertThat(results.size()).isEqualTo(4);
        assertThat(results.get(0)).isEqualTo(resultOne);
        assertThat(results.get(1)).isEqualTo(resultThree);
        assertThat(results.get(2)).isEqualTo(resultFour);
        assertThat(results.get(3)).isEqualTo(resultFive);
    }

    @Test
    public void testSpecialCaseTwoWords_firstWordMatches_ranksHigher() {
        final String caseOne = "Apple pear";
        final String caseTwo = "Banana apple";
        insertSpecialCase(caseOne);