com.android.talkback.labeling.LabelProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.android.talkback.labeling.LabelProvider.java

Source

/*
 * Copyright (C) 2013 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 com.android.talkback.labeling;

import android.annotation.TargetApi;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Build;
import android.support.v4.os.UserManagerCompat;
import android.text.TextUtils;
import android.util.Log;
import com.android.talkback.BuildConfig;
import com.android.utils.LogUtils;
import com.android.utils.labeling.LabelsTable;

import java.util.Locale;

/**
 * A content provider for accessing TalkBack custom label data.
 * <p>
 * The following operations are supported at each URI:
 * <ul>
 * <li>{@code AUTHORITY/labels}: query and insert.
 * <li>{@code AUTHORITY/labels/#}: query, update, and delete.
 * </ul>
 */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class LabelProvider extends ContentProvider {
    public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".providers.LabelProvider";
    static final String LABELS_PATH = "labels";
    static final Uri LABELS_CONTENT_URI = new Uri.Builder().scheme("content").authority(AUTHORITY).path(LABELS_PATH)
            .build();
    private static final Uri LABELS_ID_CONTENT_URI = Uri.withAppendedPath(LABELS_CONTENT_URI, "#");
    private static final String PACKAGE_SUMMARY_PATH = "packageSummary";
    private static final Uri PACKAGE_SUMMARY_URI = new Uri.Builder().scheme("content").authority(AUTHORITY)
            .path(PACKAGE_SUMMARY_PATH).build();

    /* Codes for URI matching */
    static final int LABELS = 1;
    static final int LABELS_ID = 2;
    private static final int PACKAGE_SUMMARY = 3;

    private static final String UNKNOWN_URI_FORMAT_STRING = "Unknown URI: %s";
    private static final String NULL_URI_FORMAT_STRING = "URI is null";

    static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        sUriMatcher.addURI(AUTHORITY, LABELS_CONTENT_URI.getPath(), LABELS);
        sUriMatcher.addURI(AUTHORITY, LABELS_ID_CONTENT_URI.getPath(), LABELS_ID);
        sUriMatcher.addURI(AUTHORITY, PACKAGE_SUMMARY_URI.getPath(), PACKAGE_SUMMARY);
    }

    private SQLiteDatabase mDatabase;

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    /**
     * Inserts a label in the labels database.
     *
     * @param uri The content URI for labels.
     * @param values The values to insert for the new label.
     * @return The URI of the newly inserted label,
     *         or {@code null} if the insert failed.
     */
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        if (uri == null) {
            LogUtils.log(this, Log.WARN, NULL_URI_FORMAT_STRING);
            return null;
        }

        if (!UserManagerCompat.isUserUnlocked(getContext())) {
            return null;
        }

        switch (sUriMatcher.match(uri)) {
        case LABELS:
            initializeDatabaseIfNull();

            if (values == null) {
                return null;
            }

            if (values.containsKey(LabelsTable.KEY_ID)) {
                LogUtils.log(this, Log.WARN, "Label ID must be assigned by the database.");
                return null;
            }

            long rowId = mDatabase.insert(LabelsTable.TABLE_NAME, null, values);

            if (rowId < 0) {
                LogUtils.log(this, Log.WARN, "Failed to insert label.");
                return null;
            } else {
                return ContentUris.withAppendedId(LABELS_CONTENT_URI, rowId);
            }
        default:
            LogUtils.log(this, Log.WARN, UNKNOWN_URI_FORMAT_STRING, uri);
            return null;
        }
    }

    /**
     * Queries for a label or multiple labels in the labels database.
     *
     * @param uri The URI representing the type of query to perform:
     *            {@code LABELS_CONTENT_URI} for a subset of all labels,
     *            {@code LABELS_ID_CONTENT_URI} for a specific label, or
     *            {@code PACKAGE_SUMMARY} for a label count per package.
     * @param projection The columns to return.
     * @param selection The WHERE clause for the query.
     * @param selectionArgs The arguments for the WHERE clause of the query.
     * @param sortOrder the ORDER BY clause for the query.
     * @return A cursor representing the data resulting from the query, or]
     *         {@code null} if the query failed to execute.
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        if (uri == null) {
            LogUtils.log(this, Log.WARN, NULL_URI_FORMAT_STRING);
            return null;
        }

        if (!UserManagerCompat.isUserUnlocked(getContext())) {
            return null;
        }

        final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
        queryBuilder.setTables(LabelsTable.TABLE_NAME);

        String groupBy = null;

        switch (sUriMatcher.match(uri)) {
        case LABELS:
            if (TextUtils.isEmpty(sortOrder)) {
                sortOrder = LabelsTable.KEY_ID;
            }
            break;
        case LABELS_ID:
            final String labelIdString = uri.getLastPathSegment();
            final int labelId;
            try {
                labelId = Integer.parseInt(labelIdString);
            } catch (NumberFormatException e) {
                LogUtils.log(this, Log.WARN, UNKNOWN_URI_FORMAT_STRING, uri);
                return null;
            }

            final String where = String.format(Locale.ROOT, "%s = %d", LabelsTable.KEY_ID, labelId);
            queryBuilder.appendWhere(where);
            break;
        case PACKAGE_SUMMARY:
            projection = new String[] { LabelsTable.KEY_PACKAGE_NAME, "COUNT(*)" };
            groupBy = LabelsTable.KEY_PACKAGE_NAME;
            sortOrder = LabelsTable.KEY_PACKAGE_NAME;
            break;
        default:
            LogUtils.log(this, Log.WARN, UNKNOWN_URI_FORMAT_STRING, uri);
            return null;
        }

        initializeDatabaseIfNull();

        return queryBuilder.query(mDatabase, projection, selection, selectionArgs, groupBy, null /* having */,
                sortOrder);
    }

    /**
     * Updates a label in the labels database.
     *
     * @param uri The URI matching {code LABELS_ID_CONTENT_URI} that represents
     *            the specific label to update.
     * @param values The values to use to update the label.
     * @param selection The WHERE clause for the query.
     * @param selectionArgs The arguments for the WHERE clause of the query.
     * @return The number of rows affected.
     */
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        if (uri == null) {
            LogUtils.log(this, Log.WARN, NULL_URI_FORMAT_STRING);
            return 0;
        }

        if (!UserManagerCompat.isUserUnlocked(getContext())) {
            return 0;
        }

        switch (sUriMatcher.match(uri)) {
        case LABELS: {
            initializeDatabaseIfNull();

            int result = mDatabase.update(LabelsTable.TABLE_NAME, values, selection, selectionArgs);
            getContext().getContentResolver().notifyChange(uri, null /* observer */);
            return result;
        }
        case LABELS_ID: {
            initializeDatabaseIfNull();

            final String labelIdString = uri.getLastPathSegment();
            final int labelId;
            try {
                labelId = Integer.parseInt(labelIdString);
            } catch (NumberFormatException e) {
                LogUtils.log(this, Log.WARN, UNKNOWN_URI_FORMAT_STRING, uri);
                return 0;
            }

            final String where = String.format(Locale.ROOT, "%s = %d", LabelsTable.KEY_ID, labelId);
            final int result = mDatabase.update(LabelsTable.TABLE_NAME, values,
                    combineSelectionAndWhere(selection, where), selectionArgs);

            getContext().getContentResolver().notifyChange(uri, null /* observer */);

            return result;
        }
        default:
            LogUtils.log(this, Log.WARN, UNKNOWN_URI_FORMAT_STRING, uri);
            return 0;
        }
    }

    /**
     * Deletes a label in the labels database.
     *
     * @param uri The URI matching {code LABELS_ID_CONTENT_URI} that represents
     *            the specific label to delete.
     * @param selection The WHERE clause for the query.
     * @param selectionArgs The arguments for the WHERE clause of the query.
     * @return The number of rows affected.
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        if (uri == null) {
            LogUtils.log(this, Log.WARN, NULL_URI_FORMAT_STRING);
            return 0;
        }

        if (!UserManagerCompat.isUserUnlocked(getContext())) {
            return 0;
        }

        switch (sUriMatcher.match(uri)) {
        case LABELS: {
            initializeDatabaseIfNull();

            int result = mDatabase.delete(LabelsTable.TABLE_NAME, selection, selectionArgs);
            getContext().getContentResolver().notifyChange(uri, null /* observer */);
            return result;
        }
        case LABELS_ID: {
            initializeDatabaseIfNull();

            final String labelIdString = uri.getLastPathSegment();
            final int labelId;
            try {
                labelId = Integer.parseInt(labelIdString);
            } catch (NumberFormatException e) {
                LogUtils.log(this, Log.WARN, UNKNOWN_URI_FORMAT_STRING, uri);
                return 0;
            }

            final String where = String.format(Locale.ROOT, "%s = %d", LabelsTable.KEY_ID, labelId);
            final int result = mDatabase.delete(LabelsTable.TABLE_NAME, combineSelectionAndWhere(selection, where),
                    selectionArgs);

            getContext().getContentResolver().notifyChange(uri, null /* observer */);

            return result;
        }
        default:
            LogUtils.log(this, Log.WARN, UNKNOWN_URI_FORMAT_STRING, uri);
            return 0;
        }
    }

    @Override
    public void shutdown() {
        if (mDatabase != null) {
            mDatabase.close();
        }
    }

    /**
     * Joins a selection clause with a where clause to form a larger selection
     * clause that represents the AND of the two clauses.
     *
     * @param selection The selection clause.
     * @param where The where clause.
     * @return The joined clause.
     */
    private String combineSelectionAndWhere(String selection, final String where) {
        if (TextUtils.isEmpty(where)) {
            return selection;
        } else if (TextUtils.isEmpty(selection)) {
            return where;
        }

        return String.format(Locale.ROOT, "(%s) AND (%s)", where, selection);
    }

    /**
     * Initializes the database (if not already initialized) when used.
     * <p>
     * Note: the database is automatically cleaned up by the kernel when the
     * process terminates.
     */
    private void initializeDatabaseIfNull() {
        if (mDatabase == null) {
            mDatabase = new LabelsDatabaseOpenHelper(getContext()).getWritableDatabase();
        }
    }

    /**
     * A helper for managing a SQLite database that stores label data.
     */
    private final class LabelsDatabaseOpenHelper extends SQLiteOpenHelper {
        private static final String DATABASE_NAME = "labelsDatabase.db";

        /*
         * If the database structure is modified and this value is changed, be
         * sure to implement the onUpgrade method for the database and each
         * relevant table that it includes.
         */
        private static final int DATABASE_VERSION = 3;

        public LabelsDatabaseOpenHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            LabelsTable.onCreate(db);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            LabelsTable.onUpgrade(db, oldVersion, newVersion);
        }
    }
}