SupportSQLiteCompat.java

/*
 * Copyright 2021 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 androidx.sqlite.db;

import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;

import java.io.File;
import java.util.List;

/**
 * Helper for accessing features in {@link SupportSQLiteOpenHelper}.
 *
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public final class SupportSQLiteCompat {
    private SupportSQLiteCompat() { }
    /**
     * Class for accessing functions that require SDK version 16 and higher.
     *
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @RequiresApi(16)
    public static final class Api16Impl {

        /**
         * Cancels the operation and signals the cancellation listener. If the operation has not yet
         * started, then it will be canceled as soon as it does.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static void cancel(@NonNull CancellationSignal cancellationSignal) {
            cancellationSignal.cancel();
        }

        /**
         * Creates a cancellation signal, initially not canceled.
         *
         * @return a new cancellation signal
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        @NonNull
        public static CancellationSignal createCancellationSignal() {
            return new CancellationSignal();
        }

        /**
         * Deletes a database including its journal file and other auxiliary files
         * that may have been created by the database engine.
         *
         * @param file The database file path.
         * @return True if the database was successfully deleted.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        @SuppressWarnings("StreamFiles")
        public static boolean deleteDatabase(@NonNull File file) {
            return SQLiteDatabase.deleteDatabase(file);
        }

        /**
         * Runs the provided SQL and returns a cursor over the result set.
         *
         * @param sql the SQL query. The SQL string must not be ; terminated
         * @param selectionArgs You may include ?s in where clause in the query,
         *     which will be replaced by the values from selectionArgs. The
         *     values will be bound as Strings.
         * @param editTable the name of the first table, which is editable
         * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
         * If the operation is canceled, then {@link OperationCanceledException} will be thrown
         * when the query is executed.
         * @param cursorFactory the cursor factory to use, or null for the default factory
         * @return A {@link Cursor} object, which is positioned before the first entry. Note that
         * {@link Cursor}s are not synchronized, see the documentation for more details.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        @NonNull
        public static Cursor rawQueryWithFactory(@NonNull SQLiteDatabase sQLiteDatabase,
                @NonNull String sql, @NonNull String[] selectionArgs,
                @NonNull String editTable, @NonNull CancellationSignal cancellationSignal,
                @NonNull SQLiteDatabase.CursorFactory cursorFactory) {
            return sQLiteDatabase.rawQueryWithFactory(cursorFactory, sql, selectionArgs, editTable,
                    cancellationSignal);
        }

        /**
         * Sets whether foreign key constraints are enabled for the database.
         *
         * @param enable True to enable foreign key constraints, false to disable them.
         *
         * @throws IllegalStateException if the are transactions is in progress
         * when this method is called.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static void setForeignKeyConstraintsEnabled(@NonNull SQLiteDatabase sQLiteDatabase,
                boolean enable) {
            sQLiteDatabase.setForeignKeyConstraintsEnabled(enable);
        }

        /**
         * This method disables the features enabled by
         * {@link SQLiteDatabase#enableWriteAheadLogging()}.
         *
         * @throws IllegalStateException if there are transactions in progress at the
         * time this method is called.  WAL mode can only be changed when there are no
         * transactions in progress.
         *
         * @see SQLiteDatabase#enableWriteAheadLogging
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static void disableWriteAheadLogging(@NonNull SQLiteDatabase sQLiteDatabase) {
            sQLiteDatabase.disableWriteAheadLogging();
        }

        /**
         * Returns true if write-ahead logging has been enabled for this database.
         *
         * @return True if write-ahead logging has been enabled for this database.
         *
         * @see SQLiteDatabase#enableWriteAheadLogging
         * @see SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static boolean isWriteAheadLoggingEnabled(@NonNull SQLiteDatabase sQLiteDatabase) {
            return sQLiteDatabase.isWriteAheadLoggingEnabled();
        }

        /**
         * Sets {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag if {@code enabled} is {@code
         * true}, unsets otherwise.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static void setWriteAheadLoggingEnabled(@NonNull SQLiteOpenHelper sQLiteOpenHelper,
                boolean enabled) {
            sQLiteOpenHelper.setWriteAheadLoggingEnabled(enabled);
        }

        private Api16Impl() {}
    }

    /**
     * Helper for accessing functions that require SDK version 19 and higher.
     *
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @RequiresApi(19)
    public static final class Api19Impl {
        /**
         * Return the URI at which notifications of changes in this Cursor's data
         * will be delivered.
         *
         * @return Returns a URI that can be used with
         * {@link ContentResolver#registerContentObserver(android.net.Uri, boolean, ContentObserver)
         * ContentResolver.registerContentObserver} to find out about changes to this Cursor's
         * data. May be null if no notification URI has been set.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        @NonNull
        public static Uri getNotificationUri(@NonNull Cursor cursor) {
            return cursor.getNotificationUri();
        }


        /**
         * Returns true if this is a low-RAM device.  Exactly whether a device is low-RAM
         * is ultimately up to the device configuration, but currently it generally means
         * something with 1GB or less of RAM.  This is mostly intended to be used by apps
         * to determine whether they should turn off certain features that require more RAM.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static boolean isLowRamDevice(@NonNull ActivityManager activityManager) {
            return activityManager.isLowRamDevice();
        }

        private Api19Impl() {}
    }

    /**
     * Helper for accessing functions that require SDK version 21 and higher.
     *
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @RequiresApi(21)
    public static final class Api21Impl {

        /**
         * Returns the absolute path to the directory on the filesystem.
         *
         * @return The path of the directory holding application files that will not
         *         be automatically backed up to remote storage.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        @NonNull
        public static File getNoBackupFilesDir(@NonNull Context context) {
            return context.getNoBackupFilesDir();
        }

        private Api21Impl() {}
    }

    /**
     * Helper for accessing functions that require SDK version 23 and higher.
     *
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @RequiresApi(23)
    public static final class Api23Impl {

        /**
         * Sets a {@link Bundle} that will be returned by {@link Cursor#getExtras()}.
         *
         * @param extras {@link Bundle} to set, or null to set an empty bundle.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static void setExtras(@NonNull Cursor cursor, @NonNull Bundle extras) {
            cursor.setExtras(extras);
        }

        private Api23Impl() {}
    }

    /**
     * Helper for accessing functions that require SDK version 29 and higher.
     *
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @RequiresApi(29)
    public static final class Api29Impl {

        /**
         * Similar to {@link Cursor#setNotificationUri(ContentResolver, Uri)}, except this version
         * allows to watch multiple content URIs for changes.
         *
         * @param cr The content resolver from the caller's context. The listener attached to
         * this resolver will be notified.
         * @param uris The content URIs to watch.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public static void setNotificationUris(@NonNull Cursor cursor, @NonNull ContentResolver cr,
                @NonNull List<Uri> uris) {
            cursor.setNotificationUris(cr, uris);
        }

        /**
         * Return the URIs at which notifications of changes in this Cursor's data
         * will be delivered, as previously set by {@link #setNotificationUris}.
         *
         * @return Returns URIs that can be used with
         * {@link ContentResolver#registerContentObserver(android.net.Uri, boolean, ContentObserver)
         * ContentResolver.registerContentObserver} to find out about changes to this Cursor's
         * data. May be null if no notification URI has been set.
         *
         * @hide
         */
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        @NonNull
        public static List<Uri> getNotificationUris(@NonNull Cursor cursor) {
            return cursor.getNotificationUris();
        }

        private Api29Impl() {}
    }

}