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

Commit b49995d4 authored by Fabrice Di Meglio's avatar Fabrice Di Meglio
Browse files

Introduce the SearchIndexablesProvider and its friends

- enable retrieval of search data to be indexed

Change-Id: I4a16408dec3d3305bf20107fb11eb2d1aee7fad5
parent 5b568aa0
Loading
Loading
Loading
Loading
+117 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.provider;

import android.content.Context;

import java.util.Locale;

/**
 * The Indexable data for Search. This abstract class defines the common parts for all search
 * indexable data.
 *
 * @hide
 */
public abstract class SearchIndexableData {

    /**
     * The context for the data. Will usually allow to retrieve some resources.
     *
     * @see Context
     */
    public Context context;

    /**
     * The locale for the data
     */
    public Locale locale;

    /**
     * The rank for the data. This is application specific.
     */
    public int rank;

    /**
     * The class name associated with the data. Generally this is a Fragment class name for
     * referring where the data is coming from and for launching the associated Fragment for
     * displaying the data. This is used only when the data is provided "locally".
     *
     * If the data is provided "externally", the relevant information come from the
     * {@link SearchIndexableData#intentAction} and {@link SearchIndexableData#intentTargetPackage}
     * and {@link SearchIndexableData#intentTargetClass}.
     *
     * @see SearchIndexableData#intentAction
     * @see SearchIndexableData#intentTargetPackage
     * @see SearchIndexableData#intentTargetClass
     */
    public String className;

    /**
     * The package name for retrieving the icon associated with the data.
     *
     * @see SearchIndexableData#iconResId
     */
    public String packageName;

    /**
     * The icon resource ID associated with the data.
     *
     * @see SearchIndexableData#packageName
     */
    public int iconResId;

    /**
     * The Intent action associated with the data. This is used when the
     * {@link SearchIndexableData#className} is not relevant.
     *
     * @see SearchIndexableData#intentTargetPackage
     * @see SearchIndexableData#intentTargetClass
     */
    public String intentAction;

    /**
     * The Intent target package associated with the data.
     *
     * @see SearchIndexableData#intentAction
     * @see SearchIndexableData#intentTargetClass
     */
    public String intentTargetPackage;

    /**
     * The Intent target class associated with the data.
     *
     * @see SearchIndexableData#intentAction
     * @see SearchIndexableData#intentTargetPackage
     */
    public String intentTargetClass;

    /**
     * Default constructor.
     */
    public SearchIndexableData() {
    }

    /**
     * Constructor with a {@link Context}.
     *
     * @param ctx the Context
     */
    public SearchIndexableData(Context ctx) {
        context = ctx;
        locale = Locale.getDefault();
    }
}
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.provider;

import android.content.Context;

/**
 * Search Indexable Resource.
 *
 * This class wraps a set of reference information representing data that can be indexed from a
 * resource which would typically be a {@link android.preference.PreferenceScreen}.
 *
 * xmlResId: the resource ID of a {@link android.preference.PreferenceScreen} XML file.
 *
 * @see SearchIndexableData
 * @see android.preference.PreferenceScreen
 *
 * @hide
 */
public class SearchIndexableResource extends SearchIndexableData {

    /**
     * Resource ID of the associated {@link android.preference.PreferenceScreen} XML file.
     */
    public int xmlResId;

    /**
     * Constructor.
     *
     * @param rank the rank of the data.
     * @param xmlResId the resource ID of a {@link android.preference.PreferenceScreen} XML file.
     * @param className the class name associated with the data (generally a
     *                  {@link android.app.Fragment}).
     * @param iconResId the resource ID associated with the data.
     */
    public SearchIndexableResource(int rank, int xmlResId, String className, int iconResId) {
        this.rank = rank;
        this.xmlResId = xmlResId;
        this.className = className;
        this.iconResId = iconResId;
    }

    /**
     * Constructor.
     *
     * @param context the Context associated with the data.
     */
    public SearchIndexableResource(Context context) {
        super(context);
    }
}
 No newline at end of file
+179 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.provider;

import android.content.ContentResolver;

/**
 * Describe the contract for an Indexable data.
 *
 * @hide
 */
public class SearchIndexablesContract {

    /**
     * Intent action used to identify {@link SearchIndexablesProvider}
     * instances. This is used in the {@code <intent-filter>} of a {@code <provider>}.
     */
    public static final String PROVIDER_INTERFACE =
            "android.content.action.SEARCH_INDEXABLES_PROVIDER";

    private static final String SETTINGS = "settings";

    /**
     * Indexable references name.
     */
    public static final String INDEXABLES_XML_RES = "indexables_xml_res";

    /**
     * ContentProvider path for indexable xml resources.
     */
    public static final String INDEXABLES_XML_RES_PATH = SETTINGS + "/" + INDEXABLES_XML_RES;

    /**
     * Indexable raw data name.
     */
    public static final String INDEXABLES_RAW = "indexables_raw";

    /**
     * ContentProvider path for indexable raw data.
     */
    public static final String INDEXABLES_RAW_PATH = SETTINGS + "/" + INDEXABLES_RAW;

    /**
     * Indexable xml resources colums.
     */
    public static final String[] INDEXABLES_XML_RES_COLUMNS = new String[] {
            XmlResource.COLUMN_RANK,
            XmlResource.COLUMN_XML_RESID,
            XmlResource.COLUMN_CLASS_NAME,
            XmlResource.COLUMN_ICON_RESID,
            XmlResource.COLUMN_INTENT_ACTION,
            XmlResource.COLUMN_INTENT_TARGET_PACKAGE,
            XmlResource.COLUMN_INTENT_TARGET_CLASS
    };

    /**
     * Indexable raw data colums.
     */
    public static final String[] INDEXABLES_RAW_COLUMNS = new String[] {
            RawData.COLUMN_RANK,
            RawData.COLUMN_TITLE,
            RawData.COLUMN_SUMMARY,
            RawData.COLUMN_KEYWORDS,
            RawData.COLUMN_SCREEN_TITLE,
            RawData.COLUMN_CLASS_NAME,
            RawData.COLUMN_ICON_RESID,
            RawData.COLUMN_INTENT_ACTION,
            RawData.COLUMN_INTENT_TARGET_PACKAGE,
            RawData.COLUMN_INTENT_TARGET_CLASS,
    };

    /**
     * Constants related to a {@link SearchIndexableResource}.
     *
     * This is a description of
     */
    public static final class XmlResource extends BaseColumns {
        private XmlResource() {
        }

        public static final String MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
                "/" + INDEXABLES_XML_RES;

        /**
         * XML resource ID for the {@link android.preference.PreferenceScreen} to load and index.
         */
        public static final String COLUMN_XML_RESID = "xmlResId";
    }

    /**
     * Constants related to a {@link SearchIndexableData}.
     *
     * This is the raw data that is stored into an Index. This is related to
     * {@link android.preference.Preference} and its attributes like
     * {@link android.preference.Preference#getTitle()},
     * {@link android.preference.Preference#getSummary()}, etc.
     *
     */
    public static final class RawData extends BaseColumns {
        private RawData() {
        }

        public static final String MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
                "/" + INDEXABLES_RAW;

        /**
         * Title's raw data.
         */
        public static final String COLUMN_TITLE = "title";

        /**
         * Summary's raw data.
         */
        public static final String COLUMN_SUMMARY = "summary";

        /**
         * Keywords' raw data.
         */
        public static final String COLUMN_KEYWORDS = "keywords";

        /**
         * Fragment's title associated with the raw data.
         */
        public static final String COLUMN_SCREEN_TITLE = "screenTitle";
    }

    /**
     * The base columns.
     */
    private static class BaseColumns {
        private BaseColumns() {
        }

        /**
         * Rank of the data. This is an integer used for ranking the search results. This is
         * application specific.
         */
        public static final String COLUMN_RANK = "rank";

        /**
         * Class name associated with the data (usually a Fragment class name).
         */
        public static final String COLUMN_CLASS_NAME = "className";

        /**
         * Icon resource ID for the data.
         */
        public static final String COLUMN_ICON_RESID = "iconResId";

        /**
         * Intent action associated with the data.
         */
        public static final String COLUMN_INTENT_ACTION = "intentAction";

        /**
         * Intent target package associated with the data.
         */
        public static final String COLUMN_INTENT_TARGET_PACKAGE = "intentTargetPackage";

        /**
         * Intent target class associated with the data.
         */
        public static final String COLUMN_INTENT_TARGET_CLASS = "intentTargetClass";
    }
}
+174 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.provider;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;

/**
 * Base class for a search indexable provider. Such provider offers data to be indexed either
 * as a reference to an XML file (like a {@link android.preference.PreferenceScreen}) or either
 * as some raw data.
 *
 * @see SearchIndexableResource
 * @see SearchIndexableData
 * @see SearchIndexablesContract
 *
 * To create a search indexables provider, extend this class, then implement the abstract methods,
 * and add it to your manifest like this:
 *
 * <pre class="prettyprint">&lt;manifest&gt;
 *    ...
 *    &lt;application&gt;
 *        ...
 *        &lt;provider
 *            android:name="com.example.MyIndexablesProvider"
 *            android:authorities="com.example.myindexablesprovider"
 *            android:exported="true"
 *            android:grantUriPermissions="true"
 *            android:permission="android.permission.READ_SEARCH_INDEXABLES"
 *            &lt;intent-filter&gt;
 *                &lt;action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" /&gt;
 *            &lt;/intent-filter&gt;
 *        &lt;/provider&gt;
 *        ...
 *    &lt;/application&gt;
 *&lt;/manifest&gt;</pre>
 * <p>
 * When defining your provider, you must protect it with
 * {@link android.Manifest.permission#READ_SEARCH_INDEXABLES}, which is a permission only the system
 * can obtain.
 * </p>
 *
 * @hide
 */
public abstract class SearchIndexablesProvider extends ContentProvider {
    private static final String TAG = "IndexablesProvider";

    private String mAuthority;
    private UriMatcher mMatcher;

    private static final int MATCH_RES_CODE = 1;
    private static final int MATCH_RAW_CODE = 2;

    /**
     * Implementation is provided by the parent class.
     */
    @Override
    public void attachInfo(Context context, ProviderInfo info) {
        mAuthority = info.authority;

        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_XML_RES_PATH,
                MATCH_RES_CODE);
        mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_RAW_PATH,
                MATCH_RAW_CODE);

        // Sanity check our setup
        if (!info.exported) {
            throw new SecurityException("Provider must be exported");
        }
        if (!info.grantUriPermissions) {
            throw new SecurityException("Provider must grantUriPermissions");
        }
        if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(info.readPermission)) {
            throw new SecurityException("Provider must be protected by READ_SEARCH_INDEXABLES");
        }

        super.attachInfo(context, info);
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                        String sortOrder) {
        switch (mMatcher.match(uri)) {
            case MATCH_RES_CODE:
                return queryXmlResources(null);
            case MATCH_RAW_CODE:
                return queryRawData(null);
            default:
                throw new UnsupportedOperationException("Unknown Uri " + uri);
        }
    }

    /**
     * Returns all {@link android.provider.SearchIndexablesContract.XmlResource}.
     *
     * Those are usually xml resource ID to some {@link android.preference.PreferenceScreen}.
     *
     * @param projection list of {@link android.provider.SearchIndexablesContract.XmlResource}
     *                   columns to put into the cursor. If {@code null} all supported columns
     *                   should be included.
     */
    public abstract Cursor queryXmlResources(String[] projection);

    /**
     * Returns all {@link android.provider.SearchIndexablesContract.RawData}.
     *
     * Those are raw indexable data.
     *
     * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns
     *                   to put into the cursor. If {@code null} all supported columns should be
     *                   included.
     */
    public abstract Cursor queryRawData(String[] projection);

    @Override
    public String getType(Uri uri) {
        switch (mMatcher.match(uri)) {
            case MATCH_RES_CODE:
                return SearchIndexablesContract.XmlResource.MIME_TYPE;
            case MATCH_RAW_CODE:
                return SearchIndexablesContract.RawData.MIME_TYPE;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }

    /**
     * Implementation is provided by the parent class. Throws by default, and
     * cannot be overriden.
     */
    @Override
    public final Uri insert(Uri uri, ContentValues values) {
        throw new UnsupportedOperationException("Insert not supported");
    }

    /**
     * Implementation is provided by the parent class. Throws by default, and
     * cannot be overriden.
     */
    @Override
    public final int delete(Uri uri, String selection, String[] selectionArgs) {
        throw new UnsupportedOperationException("Delete not supported");
    }

    /**
     * Implementation is provided by the parent class. Throws by default, and
     * cannot be overriden.
     */
    @Override
    public final int update(
            Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        throw new UnsupportedOperationException("Update not supported");
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -2399,6 +2399,12 @@
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="signature" />

    <!-- Internal permission to allows an application to read indexable data.
        @hide -->
    <permission android:name="android.permission.READ_SEARCH_INDEXABLES"
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="signature|system" />

    <!-- Allows applications to set a live wallpaper.
         @hide XXX Change to signature once the picker is moved to its
         own apk as Ghod Intended. -->