PendingIntentCompat.java

/*
 * Copyright (C) 2022 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.core.app;

import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_MUTABLE;

import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;

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

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/** Helper for accessing features in {@link PendingIntent}. */
public final class PendingIntentCompat {

    @IntDef(
            flag = true,
            value = {
                PendingIntent.FLAG_ONE_SHOT,
                PendingIntent.FLAG_NO_CREATE,
                PendingIntent.FLAG_CANCEL_CURRENT,
                PendingIntent.FLAG_UPDATE_CURRENT,
                Intent.FILL_IN_ACTION,
                Intent.FILL_IN_DATA,
                Intent.FILL_IN_CATEGORIES,
                Intent.FILL_IN_COMPONENT,
                Intent.FILL_IN_PACKAGE,
                Intent.FILL_IN_SOURCE_BOUNDS,
                Intent.FILL_IN_SELECTOR,
                Intent.FILL_IN_CLIP_DATA
            })
    @Retention(RetentionPolicy.SOURCE)
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public @interface Flags {}

    /**
     * Retrieves a {@link PendingIntent} with mandatory mutability flag set on supported platform
     * versions. The caller provides the flag as combination of all the other values except
     * mutability flag. This method combines mutability flag when necessary. See {@link
     * PendingIntent#getActivities(Context, int, Intent[], int, Bundle)}.
     */
    public static @NonNull PendingIntent getActivities(
            @NonNull Context context,
            int requestCode,
            @NonNull @SuppressLint("ArrayReturn") Intent[] intents,
            @Flags int flags,
            @NonNull Bundle options,
            boolean isMutable) {
        if (Build.VERSION.SDK_INT >= 16) {
            return Api16Impl.getActivities(
                    context, requestCode, intents, addMutabilityFlags(isMutable, flags), options);
        } else {
            return PendingIntent.getActivities(context, requestCode, intents, flags);
        }
    }

    /**
     * Retrieves a {@link PendingIntent} with mandatory mutability flag set on supported platform
     * versions. The caller provides the flag as combination of all the other values except
     * mutability flag. This method combines mutability flag when necessary. See {@link
     * PendingIntent#getActivities(Context, int, Intent[], int, Bundle)}.
     */
    public static @NonNull PendingIntent getActivities(
            @NonNull Context context,
            int requestCode,
            @NonNull @SuppressLint("ArrayReturn") Intent[] intents,
            @Flags int flags,
            boolean isMutable) {
        return PendingIntent.getActivities(
                context, requestCode, intents, addMutabilityFlags(isMutable, flags));
    }

    /**
     * Retrieves a {@link PendingIntent} with mandatory mutability flag set on supported platform
     * versions. The caller provides the flag as combination of all the other values except
     * mutability flag. This method combines mutability flag when necessary. See {@link
     * PendingIntent#getActivity(Context, int, Intent, int)}.
     */
    public static @NonNull PendingIntent getActivity(
            @NonNull Context context,
            int requestCode,
            @NonNull Intent intent,
            @Flags int flags,
            boolean isMutable) {
        return PendingIntent.getActivity(
                context, requestCode, intent, addMutabilityFlags(isMutable, flags));
    }

    /**
     * Retrieves a {@link PendingIntent} with mandatory mutability flag set on supported platform
     * versions. The caller provides the flag as combination of all the other values except
     * mutability flag. This method combines mutability flag when necessary. See {@link
     * PendingIntent#getActivity(Context, int, Intent, int, Bundle)}.
     */
    public static @NonNull PendingIntent getActivity(
            @NonNull Context context,
            int requestCode,
            @NonNull Intent intent,
            @Flags int flags,
            @NonNull Bundle options,
            boolean isMutable) {
        if (Build.VERSION.SDK_INT >= 16) {
            return Api16Impl.getActivity(
                    context, requestCode, intent, addMutabilityFlags(isMutable, flags), options);
        } else {
            return PendingIntent.getActivity(context, requestCode, intent, flags);
        }
    }

    /**
     * Retrieves a {@link PendingIntent} with mandatory mutability flag set on supported platform
     * versions. The caller provides the flag as combination of all the other values except
     * mutability flag. This method combines mutability flag when necessary. See {@link
     * PendingIntent#getBroadcast(Context, int, Intent, int)}.
     */
    public static @NonNull PendingIntent getBroadcast(
            @NonNull Context context,
            int requestCode,
            @NonNull Intent intent,
            @Flags int flags,
            boolean isMutable) {
        return PendingIntent.getBroadcast(
                context, requestCode, intent, addMutabilityFlags(isMutable, flags));
    }

    /**
     * Retrieves a {@link PendingIntent} with mandatory mutability flag set on supported platform
     * versions. The caller provides the flag as combination of all the other values except
     * mutability flag. This method combines mutability flag when necessary. See {@link
     * PendingIntent#getForegroundService(Context, int, Intent, int)} .
     */
    @RequiresApi(26)
    public static @NonNull PendingIntent getForegroundService(
            @NonNull Context context,
            int requestCode,
            @NonNull Intent intent,
            @Flags int flags,
            boolean isMutable) {
        return Api26Impl.getForegroundService(
                context, requestCode, intent, addMutabilityFlags(isMutable, flags));
    }

    /**
     * Retrieves a {@link PendingIntent} with mandatory mutability flag set on supported platform
     * versions. The caller provides the flag as combination of all the other values except
     * mutability flag. This method combines mutability flag when necessary. See {@link
     * PendingIntent#getService(Context, int, Intent, int)}.
     */
    public static @NonNull PendingIntent getService(
            @NonNull Context context,
            int requestCode,
            @NonNull Intent intent,
            @Flags int flags,
            boolean isMutable) {
        return PendingIntent.getService(
                context, requestCode, intent, addMutabilityFlags(isMutable, flags));
    }

    private static int addMutabilityFlags(boolean isMutable, int flags) {
        if (isMutable) {
            if (Build.VERSION.SDK_INT >= 31) {
                flags |= FLAG_MUTABLE;
            }
        } else {
            if (Build.VERSION.SDK_INT >= 23) {
                flags |= FLAG_IMMUTABLE;
            }
        }

        return flags;
    }

    private PendingIntentCompat() {}

    @RequiresApi(16)
    private static class Api16Impl {
        private Api16Impl() {}

        @DoNotInline
        public static @NonNull PendingIntent getActivities(
                @NonNull Context context,
                int requestCode,
                @NonNull @SuppressLint("ArrayReturn") Intent[] intents,
                @Flags int flags,
                @NonNull Bundle options) {
            return PendingIntent.getActivities(context, requestCode, intents, flags, options);
        }

        @DoNotInline
        public static @NonNull PendingIntent getActivity(
                @NonNull Context context,
                int requestCode,
                @NonNull Intent intent,
                @Flags int flags,
                @NonNull Bundle options) {
            return PendingIntent.getActivity(context, requestCode, intent, flags, options);
        }
    }

    @RequiresApi(26)
    private static class Api26Impl {
        private Api26Impl() {}

        @DoNotInline
        public static PendingIntent getForegroundService(
                Context context, int requestCode, Intent intent, int flags) {
            return PendingIntent.getForegroundService(context, requestCode, intent, flags);
        }
    }
}