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

Commit be9f37cf authored by Jason Chiu's avatar Jason Chiu Committed by Android (Google) Code Review
Browse files

Merge "Skip indexing dynamic and injected raw data when page search is disabled" into sc-dev

parents cb157431 1ee19111
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -80,6 +80,10 @@ public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider {
    @CallSuper
    @CallSuper
    public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context, boolean enabled) {
    public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context, boolean enabled) {
        final List<SearchIndexableRaw> dynamicRaws = new ArrayList<>();
        final List<SearchIndexableRaw> dynamicRaws = new ArrayList<>();
        if (!isPageSearchEnabled(context)) {
            // Entire page should be suppressed, do not add dynamic raw data.
            return dynamicRaws;
        }
        final List<AbstractPreferenceController> controllers = getPreferenceControllers(context);
        final List<AbstractPreferenceController> controllers = getPreferenceControllers(context);
        if (controllers == null || controllers.isEmpty()) {
        if (controllers == null || controllers.isEmpty()) {
            return dynamicRaws;
            return dynamicRaws;
+65 −31
Original line number Original line Diff line number Diff line
@@ -55,20 +55,22 @@ import android.provider.SearchIndexablesContract;
import android.provider.SearchIndexablesProvider;
import android.provider.SearchIndexablesProvider;
import android.provider.SettingsSlicesContract;
import android.provider.SettingsSlicesContract;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.Log;
import android.util.Log;


import androidx.annotation.Nullable;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.slice.SliceViewManager;
import androidx.slice.SliceViewManager;


import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.CategoryManager;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.DashboardFragmentRegistry;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.Indexable;
@@ -78,6 +80,7 @@ import com.android.settingslib.search.SearchIndexableRaw;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collection;
import java.util.List;
import java.util.List;
import java.util.Map;


public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {


@@ -94,6 +97,9 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {


    private static final Collection<String> INVALID_KEYS;
    private static final Collection<String> INVALID_KEYS;


    // Search enabled states for injection (key: category key, value: search enabled)
    private Map<String, Boolean> mSearchEnabledByCategoryKeyMap;

    static {
    static {
        INVALID_KEYS = new ArraySet<>();
        INVALID_KEYS = new ArraySet<>();
        INVALID_KEYS.add(null);
        INVALID_KEYS.add(null);
@@ -102,6 +108,7 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {


    @Override
    @Override
    public boolean onCreate() {
    public boolean onCreate() {
        mSearchEnabledByCategoryKeyMap = new ArrayMap<>();
        return true;
        return true;
    }
    }


@@ -166,7 +173,18 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
    public Cursor queryDynamicRawData(String[] projection) {
    public Cursor queryDynamicRawData(String[] projection) {
        final Context context = getContext();
        final Context context = getContext();
        final List<SearchIndexableRaw> rawList = new ArrayList<>();
        final List<SearchIndexableRaw> rawList = new ArrayList<>();
        rawList.addAll(getDynamicSearchIndexableRawFromProvider(context));
        final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
                .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();

        for (SearchIndexableData bundle : bundles) {
            rawList.addAll(getDynamicSearchIndexableRawData(context, bundle));

            // Refresh the search enabled state for indexing injection raw data
            final Indexable.SearchIndexProvider provider = bundle.getSearchIndexProvider();
            if (provider instanceof BaseSearchIndexProvider) {
                refreshSearchEnabledState(context, (BaseSearchIndexProvider) provider);
            }
        }
        rawList.addAll(getInjectionIndexableRawData(context));
        rawList.addAll(getInjectionIndexableRawData(context));


        final MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
        final MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
@@ -355,39 +373,35 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
        return rawList;
        return rawList;
    }
    }


    private List<SearchIndexableRaw> getDynamicSearchIndexableRawFromProvider(Context context) {
    private List<SearchIndexableRaw> getDynamicSearchIndexableRawData(Context context,
        final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
            SearchIndexableData bundle) {
                .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
        final List<SearchIndexableRaw> rawList = new ArrayList<>();

        for (SearchIndexableData bundle : bundles) {
        final Indexable.SearchIndexProvider provider = bundle.getSearchIndexProvider();
        final Indexable.SearchIndexProvider provider = bundle.getSearchIndexProvider();
        final List<SearchIndexableRaw> providerRaws =
        final List<SearchIndexableRaw> providerRaws =
                provider.getDynamicRawDataToIndex(context, true /* enabled */);
                provider.getDynamicRawDataToIndex(context, true /* enabled */);

        if (providerRaws == null) {
        if (providerRaws == null) {
                continue;
            return new ArrayList<>();
        }
        }


        for (SearchIndexableRaw raw : providerRaws) {
        for (SearchIndexableRaw raw : providerRaws) {
            // The classname and intent information comes from the PreIndexData
            // The classname and intent information comes from the PreIndexData
            // This will be more clear when provider conversion is done at PreIndex time.
            // This will be more clear when provider conversion is done at PreIndex time.
            raw.className = bundle.getTargetClass().getName();
            raw.className = bundle.getTargetClass().getName();

        }
        }
            rawList.addAll(providerRaws);
        return providerRaws;
    }
    }


        return rawList;
    @VisibleForTesting
    }
    List<SearchIndexableRaw> getInjectionIndexableRawData(Context context) {

    private List<SearchIndexableRaw> getInjectionIndexableRawData(Context context) {
        final DashboardFeatureProvider dashboardFeatureProvider =
        final DashboardFeatureProvider dashboardFeatureProvider =
                FeatureFactory.getFactory(context).getDashboardFeatureProvider(context);
                FeatureFactory.getFactory(context).getDashboardFeatureProvider(context);

        final List<SearchIndexableRaw> rawList = new ArrayList<>();
        final List<SearchIndexableRaw> rawList = new ArrayList<>();
        final String currentPackageName = context.getPackageName();
        final String currentPackageName = context.getPackageName();
        for (DashboardCategory category : dashboardFeatureProvider.getAllCategories()) {
        for (DashboardCategory category : dashboardFeatureProvider.getAllCategories()) {
            if (mSearchEnabledByCategoryKeyMap.containsKey(category.key)
                    && !mSearchEnabledByCategoryKeyMap.get(category.key)) {
                Log.i(TAG, "Skip indexing category: " + category.key);
                continue;
            }
            for (Tile tile : category.getTiles()) {
            for (Tile tile : category.getTiles()) {
                if (!isEligibleForIndexing(currentPackageName, tile)) {
                if (!isEligibleForIndexing(currentPackageName, tile)) {
                    continue;
                    continue;
@@ -410,6 +424,30 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
        return rawList;
        return rawList;
    }
    }


    @VisibleForTesting
    void refreshSearchEnabledState(Context context, BaseSearchIndexProvider provider) {
        // Provider's class name is like "com.android.settings.Settings$1"
        String className = provider.getClass().getName();
        final int delimiter = className.lastIndexOf("$");
        if (delimiter > 0) {
            // Parse the outer class name of this provider
            className = className.substring(0, delimiter);
        }

        // Lookup the category key by the class name
        final String categoryKey = DashboardFragmentRegistry.PARENT_TO_CATEGORY_KEY_MAP
                .get(className);
        if (categoryKey == null) {
            return;
        }

        final DashboardCategory category = CategoryManager.get(context)
                .getTilesByCategory(context, categoryKey);
        if (category != null) {
            mSearchEnabledByCategoryKeyMap.put(category.key, provider.isPageSearchEnabled(context));
        }
    }

    @VisibleForTesting
    @VisibleForTesting
    boolean isEligibleForIndexing(String packageName, Tile tile) {
    boolean isEligibleForIndexing(String packageName, Tile tile) {
        if (TextUtils.equals(packageName, tile.getPackageName())
        if (TextUtils.equals(packageName, tile.getPackageName())
@@ -417,10 +455,6 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
            // Skip Settings injected items because they should be indexed in the sub-pages.
            // Skip Settings injected items because they should be indexed in the sub-pages.
            return false;
            return false;
        }
        }
        if (TextUtils.equals(tile.getCategory(), CategoryKey.CATEGORY_HOMEPAGE)) {
            // Skip homepage injected items since we would like to index their target activity.
            return false;
        }
        return true;
        return true;
    }
    }


+10 −0
Original line number Original line Diff line number Diff line
@@ -203,6 +203,16 @@ public class BaseSearchIndexProviderTest {
        assertThat(mIndexProvider.getDynamicRawDataToIndex(mContext, true)).isEmpty();
        assertThat(mIndexProvider.getDynamicRawDataToIndex(mContext, true)).isEmpty();
    }
    }


    @Test
    public void getDynamicRawDataToIndex_disablePageSearch_shouldReturnEmptyList() {
        List<AbstractPreferenceController> controllers = new ArrayList<>();
        controllers.add(new AvailablePreferenceController(mContext));
        doReturn(controllers).when(mIndexProvider).createPreferenceControllers(mContext);
        doReturn(false).when(mIndexProvider).isPageSearchEnabled(mContext);

        assertThat(mIndexProvider.getDynamicRawDataToIndex(mContext, true)).isEmpty();
    }

    @Test
    @Test
    public void getDynamicRawDataToIndex_hasDynamicRaw_shouldNotEmpty() {
    public void getDynamicRawDataToIndex_hasDynamicRaw_shouldNotEmpty() {
        List<AbstractPreferenceController> controllers = new ArrayList<>();
        List<AbstractPreferenceController> controllers = new ArrayList<>();
+83 −11
Original line number Original line Diff line number Diff line
package com.android.settings.search;
package com.android.settings.search;


import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertThat;


import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;


import android.Manifest;
import android.Manifest;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.database.Cursor;
import android.net.Uri;
import android.net.Uri;
import android.os.Bundle;
import android.provider.SearchIndexablesContract;
import android.provider.SearchIndexablesContract;


import com.android.settings.R;
import com.android.settings.R;
import com.android.settings.accounts.ManagedProfileSettings;
import com.android.settings.dashboard.CategoryManager;
import com.android.settings.homepage.TopLevelSettings;
import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.search.SearchIndexableData;
import com.android.settingslib.search.SearchIndexableData;


import org.junit.After;
import org.junit.After;
@@ -25,21 +35,28 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;


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


@RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@Config(shadows = SettingsSearchIndexablesProviderTest.ShadowCategoryManager.class)
public class SettingsSearchIndexablesProviderTest {
public class SettingsSearchIndexablesProviderTest {


    private static final String PACKAGE_NAME = "com.android.settings";
    private static final String PACKAGE_NAME = "com.android.settings";
    private static final String BASE_AUTHORITY = "content://" + PACKAGE_NAME + "/";
    private static final String BASE_AUTHORITY = "content://" + PACKAGE_NAME + "/";


    private Context mContext;
    private SettingsSearchIndexablesProvider mProvider;
    private SettingsSearchIndexablesProvider mProvider;
    private FakeFeatureFactory mFakeFeatureFactory;
    private FakeFeatureFactory mFakeFeatureFactory;


    @Before
    @Before
    public void setUp() {
    public void setUp() {
        mContext = RuntimeEnvironment.application;
        mProvider = spy(new SettingsSearchIndexablesProvider());
        mProvider = spy(new SettingsSearchIndexablesProvider());
        ProviderInfo info = new ProviderInfo();
        ProviderInfo info = new ProviderInfo();
        info.exported = true;
        info.exported = true;
@@ -55,10 +72,22 @@ public class SettingsSearchIndexablesProviderTest {
                        FakeSettingsFragment.SEARCH_INDEX_DATA_PROVIDER));
                        FakeSettingsFragment.SEARCH_INDEX_DATA_PROVIDER));
        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
        mFakeFeatureFactory.searchFeatureProvider = featureProvider;
        mFakeFeatureFactory.searchFeatureProvider = featureProvider;

        final ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.packageName = "pkg";
        activityInfo.name = "class";
        activityInfo.metaData = new Bundle();
        activityInfo.metaData.putString(META_DATA_PREFERENCE_TITLE, "title");
        final DashboardCategory category = new DashboardCategory("key");
        when(mFakeFeatureFactory.dashboardFeatureProvider.getAllCategories())
                .thenReturn(Arrays.asList(category));
        category.addTile(new ActivityTile(activityInfo, category.key));
        ShadowCategoryManager.setDashboardCategory(category);
    }
    }


    @After
    @After
    public void cleanUp() {
    public void cleanUp() {
        ShadowCategoryManager.reset();
        mFakeFeatureFactory.searchFeatureProvider = mock(SearchFeatureProvider.class);
        mFakeFeatureFactory.searchFeatureProvider = mock(SearchFeatureProvider.class);
    }
    }


@@ -121,29 +150,52 @@ public class SettingsSearchIndexablesProviderTest {
    }
    }


    @Test
    @Test
    public void testIsEligibleForIndexing_isSettingsInjectedItem_ShouldBeFalse() {
    public void refreshSearchEnabledState_classNotFoundInCategoryMap_hasInjectionRawData() {
        final ActivityInfo activityInfo = new ActivityInfo();
        mProvider.refreshSearchEnabledState(mContext,
        activityInfo.packageName = PACKAGE_NAME;
                ManagedProfileSettings.SEARCH_INDEX_DATA_PROVIDER);
        activityInfo.name = "class";
        final ActivityTile activityTile = new ActivityTile(activityInfo,
                CategoryKey.CATEGORY_SYSTEM);


        assertThat(mProvider.isEligibleForIndexing(PACKAGE_NAME, activityTile)).isFalse();
        assertThat(mProvider.getInjectionIndexableRawData(mContext)).isNotEmpty();
    }

    @Test
    public void refreshSearchEnabledState_noDashboardCategory_hasInjectionRawData() {
        ShadowCategoryManager.setDashboardCategory(null);

        mProvider.refreshSearchEnabledState(mContext,
                TopLevelSettings.SEARCH_INDEX_DATA_PROVIDER);

        assertThat(mProvider.getInjectionIndexableRawData(mContext)).isNotEmpty();
    }

    @Test
    public void refreshSearchEnabledState_pageSearchEnabled_hasInjectionRawData() {
        mProvider.refreshSearchEnabledState(mContext,
                NetworkDashboardFragment.SEARCH_INDEX_DATA_PROVIDER);

        assertThat(mProvider.getInjectionIndexableRawData(mContext)).isNotEmpty();
    }
    }


    @Test
    @Test
    public void testIsEligibleForIndexing_isHomepageInjectedItem_ShouldBeFalse() {
    public void refreshSearchEnabledState_pageSearchDisable_noInjectionRawData() {
        mProvider.refreshSearchEnabledState(mContext,
                TopLevelSettings.SEARCH_INDEX_DATA_PROVIDER);

        assertThat(mProvider.getInjectionIndexableRawData(mContext)).isEmpty();
    }

    @Test
    public void isEligibleForIndexing_isSettingsInjectedItem_shouldReturnFalse() {
        final ActivityInfo activityInfo = new ActivityInfo();
        final ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.packageName = "pkg";
        activityInfo.packageName = PACKAGE_NAME;
        activityInfo.name = "class";
        activityInfo.name = "class";
        final ActivityTile activityTile = new ActivityTile(activityInfo,
        final ActivityTile activityTile = new ActivityTile(activityInfo,
                CategoryKey.CATEGORY_HOMEPAGE);
                CategoryKey.CATEGORY_SYSTEM);


        assertThat(mProvider.isEligibleForIndexing(PACKAGE_NAME, activityTile)).isFalse();
        assertThat(mProvider.isEligibleForIndexing(PACKAGE_NAME, activityTile)).isFalse();
    }
    }


    @Test
    @Test
    public void testIsEligibleForIndexing_normalInjectedItem_ShouldBeTrue() {
    public void isEligibleForIndexing_normalInjectedItem_shouldReturnTrue() {
        final ActivityInfo activityInfo = new ActivityInfo();
        final ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.packageName = "pkg";
        activityInfo.packageName = "pkg";
        activityInfo.name = "class";
        activityInfo.name = "class";
@@ -152,4 +204,24 @@ public class SettingsSearchIndexablesProviderTest {


        assertThat(mProvider.isEligibleForIndexing(PACKAGE_NAME, activityTile)).isTrue();
        assertThat(mProvider.isEligibleForIndexing(PACKAGE_NAME, activityTile)).isTrue();
    }
    }

    @Implements(CategoryManager.class)
    public static class ShadowCategoryManager {

        private static DashboardCategory sCategory;

        @Resetter
        static void reset() {
            sCategory = null;
        }

        @Implementation
        public DashboardCategory getTilesByCategory(Context context, String categoryKey) {
            return sCategory;
        }

        static void setDashboardCategory(DashboardCategory category) {
            sCategory = category;
        }
    }
}
}