public class

IconCompat

extends CustomVersionedParcelable

 java.lang.Object

androidx.versionedparcelable.CustomVersionedParcelable

↳androidx.core.graphics.drawable.IconCompat

Gradle dependencies

compile group: 'androidx.core', name: 'core', version: '1.15.0-alpha02'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.15.0-alpha02

Artifact androidx.core:core:1.15.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.core:core com.android.support:support-compat

Androidx class mapping:

androidx.core.graphics.drawable.IconCompat android.support.v4.graphics.drawable.IconCompat

Overview

Helper for accessing features in android.graphics.drawable.Icon.

Summary

Fields
public byte[]mData

public intmInt1

public intmInt2

public ParcelablemParcelable

public java.lang.StringmString1

public ColorStateListmTintList

public java.lang.StringmTintModeStr

public intmType

public static final intTYPE_ADAPTIVE_BITMAP

An icon that was created using IconCompat.createWithAdaptiveBitmap(Bitmap).

public static final intTYPE_BITMAP

An icon that was created using IconCompat.createWithBitmap(Bitmap).

public static final intTYPE_DATA

An icon that was created using IconCompat.createWithData(byte[], int, int).

public static final intTYPE_RESOURCE

An icon that was created using IconCompat.createWithResource(Context, int).

public static final intTYPE_UNKNOWN

Value returned when the type of an cannot be determined.

public static final intTYPE_URI

An icon that was created using IconCompat.createWithContentUri(String).

public static final intTYPE_URI_ADAPTIVE_BITMAP

An icon that was created using IconCompat.createWithAdaptiveBitmapContentUri(String).

Constructors
publicIconCompat()

Used for VersionedParcelable.

Methods
public voidaddToShortcutIntent(Intent outIntent, Drawable badge, Context c)

public voidcheckResource(Context context)

public static IconCompatcreateFromBundle(Bundle bundle)

Extracts an icon from a bundle that was added using IconCompat.toBundle().

public static IconCompatcreateFromIcon(Context context, Icon icon)

Creates an IconCompat from an Icon.

public static IconCompatcreateFromIcon(Icon icon)

Creates an IconCompat from an Icon.

public static IconCompatcreateFromIconOrNullIfZeroResId(Icon icon)

Creates an IconCompat from an Icon, or returns null if the given Icon is created from resource 0.

public static IconCompatcreateWithAdaptiveBitmap(Bitmap bits)

Create an Icon pointing to a bitmap in memory that follows the icon design guideline defined by .

public static IconCompatcreateWithAdaptiveBitmapContentUri(java.lang.String uri)

Create an Icon pointing to an image file specified by URI.

public static IconCompatcreateWithAdaptiveBitmapContentUri(Uri uri)

Create an Icon pointing to an image file specified by URI.

public static IconCompatcreateWithBitmap(Bitmap bits)

Create an Icon pointing to a bitmap in memory.

public static IconCompatcreateWithContentUri(java.lang.String uri)

Create an Icon pointing to an image file specified by URI.

public static IconCompatcreateWithContentUri(Uri uri)

Create an Icon pointing to an image file specified by URI.

public static IconCompatcreateWithData(byte[] data[], int offset, int length)

Create an Icon pointing to a compressed bitmap stored in a byte array.

public static IconCompatcreateWithResource(Context context, int resId)

Create an Icon pointing to a drawable resource.

public static IconCompatcreateWithResource(Resources r, java.lang.String pkg, int resId)

public BitmapgetBitmap()

Gets the bitmap used to create this icon.

public intgetResId()

Gets the drawable resource id used to create this icon.

public java.lang.StringgetResPackage()

Gets the package used to create this icon.

public intgetType()

Gets the type of the icon provided.

public UrigetUri()

Gets the uri used to create this icon.

public java.io.InputStreamgetUriInputStream(Context context)

Create an input stream for bitmap by resolving corresponding content uri.

public DrawableloadDrawable(Context context)

Returns a Drawable that can be used to draw the image inside this Icon, constructing it if necessary.

public voidonPostParceling()

Called immediately after this object has been deserialized, can be used to handle any custom fields that cannot be easily annotated.

public voidonPreParceling(boolean isStream)

Called immediately before this object is going to be serialized, can be used to handle any custom fields that cannot be easily annotated.

public IconCompatsetTint(int tint)

Store a color to use whenever this Icon is drawn.

public IconCompatsetTintList(ColorStateList tintList)

Store a color to use whenever this Icon is drawn.

public IconCompatsetTintMode(PorterDuff.Mode mode)

Store a blending mode to use whenever this Icon is drawn.

public BundletoBundle()

Adds this Icon to a Bundle that can be read back with the same parameters to IconCompat.createFromBundle(Bundle).

public IcontoIcon()

public IcontoIcon(Context context)

Convert this compat object to object.

public java.lang.StringtoString()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Fields

public static final int TYPE_UNKNOWN

Value returned when the type of an cannot be determined.

public static final int TYPE_BITMAP

An icon that was created using IconCompat.createWithBitmap(Bitmap).

public static final int TYPE_RESOURCE

An icon that was created using IconCompat.createWithResource(Context, int).

public static final int TYPE_DATA

An icon that was created using IconCompat.createWithData(byte[], int, int).

public static final int TYPE_URI

An icon that was created using IconCompat.createWithContentUri(String).

public static final int TYPE_ADAPTIVE_BITMAP

An icon that was created using IconCompat.createWithAdaptiveBitmap(Bitmap).

public static final int TYPE_URI_ADAPTIVE_BITMAP

An icon that was created using IconCompat.createWithAdaptiveBitmapContentUri(String).

public int mType

public byte[] mData

public Parcelable mParcelable

public int mInt1

public int mInt2

public ColorStateList mTintList

public java.lang.String mTintModeStr

public java.lang.String mString1

Constructors

public IconCompat()

Used for VersionedParcelable.

Methods

public static IconCompat createWithResource(Context context, int resId)

Create an Icon pointing to a drawable resource.

Parameters:

context: The context for the application whose resources should be used to resolve the given resource ID.
resId: ID of the drawable resource

See also: android.graphics.drawable.Icon

public static IconCompat createWithResource(Resources r, java.lang.String pkg, int resId)

public static IconCompat createWithBitmap(Bitmap bits)

Create an Icon pointing to a bitmap in memory.

Parameters:

bits: A valid android.graphics.Bitmap object

See also: android.graphics.drawable.Icon

public static IconCompat createWithAdaptiveBitmap(Bitmap bits)

Create an Icon pointing to a bitmap in memory that follows the icon design guideline defined by .

Parameters:

bits: A valid android.graphics.Bitmap object

See also: android.graphics.drawable.Icon

public static IconCompat createWithData(byte[] data[], int offset, int length)

Create an Icon pointing to a compressed bitmap stored in a byte array.

Parameters:

data: Byte array storing compressed bitmap data of a type that can decode (see ).
offset: Offset into data at which the bitmap data starts
length: Length of the bitmap data

See also: android.graphics.drawable.Icon

public static IconCompat createWithContentUri(java.lang.String uri)

Create an Icon pointing to an image file specified by URI.

Parameters:

uri: A uri referring to local content:// or file:// image data.

See also: android.graphics.drawable.Icon

public static IconCompat createWithContentUri(Uri uri)

Create an Icon pointing to an image file specified by URI.

Parameters:

uri: A uri referring to local content:// or file:// image data.

See also: android.graphics.drawable.Icon

public static IconCompat createWithAdaptiveBitmapContentUri(java.lang.String uri)

Create an Icon pointing to an image file specified by URI. Image file should follow the icon design guideline defined by .

Parameters:

uri: A uri referring to local content:// or file:// image data.

See also: android.graphics.drawable.Icon

public static IconCompat createWithAdaptiveBitmapContentUri(Uri uri)

Create an Icon pointing to an image file specified by URI. Image file should follow the icon design guideline defined by .

Parameters:

uri: A uri referring to local content:// or file:// image data.

See also: android.graphics.drawable.Icon

public int getType()

Gets the type of the icon provided.

Note that new types may be added later, so callers should guard against other types being returned.

public java.lang.String getResPackage()

Gets the package used to create this icon.

Only valid for icons of type TYPE_RESOURCE. Note: This package may not be available if referenced in the future, and it is up to the caller to ensure safety if this package is re-used and/or persisted.

public int getResId()

Gets the drawable resource id used to create this icon.

Only valid for icons of type TYPE_RESOURCE. Note: This resource may not be available if the application changes at all, and it is up to the caller to ensure safety if this resource is re-used and/or persisted.

public Bitmap getBitmap()

Gets the bitmap used to create this icon.

Only valid for icons of type TYPE_BITMAP. Note: This bitmap may not be available in the future, and it is up to the caller to ensure safety if this bitmap is re-used and/or persisted.

public Uri getUri()

Gets the uri used to create this icon.

Only valid for icons of type TYPE_URI. Note: This uri may not be available in the future, and it is up to the caller to ensure safety if this uri is re-used and/or persisted.

public IconCompat setTint(int tint)

Store a color to use whenever this Icon is drawn.

Parameters:

tint: a color, as in Drawable

Returns:

this same object, for use in chained construction

public IconCompat setTintList(ColorStateList tintList)

Store a color to use whenever this Icon is drawn.

Parameters:

tintList: as in Drawable, null to remove tint

Returns:

this same object, for use in chained construction

public IconCompat setTintMode(PorterDuff.Mode mode)

Store a blending mode to use whenever this Icon is drawn.

Parameters:

mode: a blending mode, as in Drawable, may be null

Returns:

this same object, for use in chained construction

public Icon toIcon()

Deprecated: Use IconCompat.toIcon(Context) to generate the object.

public Icon toIcon(Context context)

Convert this compat object to object.

Returns:

object

public void checkResource(Context context)

public Drawable loadDrawable(Context context)

Returns a Drawable that can be used to draw the image inside this Icon, constructing it if necessary.

Parameters:

context: in which to load the drawable; used to access Resources, for example.

Returns:

A fresh instance of a drawable for this image, yours to keep.

public java.io.InputStream getUriInputStream(Context context)

Create an input stream for bitmap by resolving corresponding content uri.

public void addToShortcutIntent(Intent outIntent, Drawable badge, Context c)

public Bundle toBundle()

Adds this Icon to a Bundle that can be read back with the same parameters to IconCompat.createFromBundle(Bundle).

public java.lang.String toString()

public void onPreParceling(boolean isStream)

Called immediately before this object is going to be serialized, can be used to handle any custom fields that cannot be easily annotated.

public void onPostParceling()

Called immediately after this object has been deserialized, can be used to handle any custom fields that cannot be easily annotated.

public static IconCompat createFromBundle(Bundle bundle)

Extracts an icon from a bundle that was added using IconCompat.toBundle().

public static IconCompat createFromIcon(Context context, Icon icon)

Creates an IconCompat from an Icon.

public static IconCompat createFromIcon(Icon icon)

Creates an IconCompat from an Icon.

public static IconCompat createFromIconOrNullIfZeroResId(Icon icon)

Creates an IconCompat from an Icon, or returns null if the given Icon is created from resource 0.

Source

/*
 * Copyright (C) 2017 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.graphics.drawable;

import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Shader;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.util.ObjectsCompat;
import androidx.core.util.Preconditions;
import androidx.versionedparcelable.CustomVersionedParcelable;
import androidx.versionedparcelable.NonParcelField;
import androidx.versionedparcelable.ParcelField;
import androidx.versionedparcelable.VersionedParcelize;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;

/**
 * Helper for accessing features in {@link android.graphics.drawable.Icon}.
 */
@VersionedParcelize(allowSerialization = true, ignoreParcelables = true, isCustom = true,
        jetifyAs = "android.support.v4.graphics.drawable.IconCompat")
public class IconCompat extends CustomVersionedParcelable {

    private static final String TAG = "IconCompat";

    /**
     * Value returned when the type of an {@link Icon} cannot be determined.
     */
    public static final int TYPE_UNKNOWN  = -1;
    /**
     * An icon that was created using {@link #createWithBitmap(Bitmap)}.
     */
    public static final int TYPE_BITMAP   = Icon.TYPE_BITMAP;
    /**
     * An icon that was created using {@link #createWithResource}.
     */
    public static final int TYPE_RESOURCE = Icon.TYPE_RESOURCE;
    /**
     * An icon that was created using {@link #createWithData(byte[], int, int)}.
     */
    public static final int TYPE_DATA     = Icon.TYPE_DATA;
    /**
     * An icon that was created using {@link #createWithContentUri}.
     */
    public static final int TYPE_URI      = Icon.TYPE_URI;
    /**
     * An icon that was created using {@link #createWithAdaptiveBitmap}.
     */
    public static final int TYPE_ADAPTIVE_BITMAP = Icon.TYPE_ADAPTIVE_BITMAP;

    /**
     * An icon that was created using {@link #createWithAdaptiveBitmapContentUri}.
     */
    public static final int TYPE_URI_ADAPTIVE_BITMAP = Icon.TYPE_URI_ADAPTIVE_BITMAP;

    /**
     */
    @RestrictTo(LIBRARY)
    @IntDef({TYPE_UNKNOWN, TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP,
            TYPE_URI_ADAPTIVE_BITMAP})
    @Retention(RetentionPolicy.SOURCE)
    public @interface IconType {
    }

    // Ratio of expected size to actual icon size
    private static final float ADAPTIVE_ICON_INSET_FACTOR = 1 / 4f;
    private static final float DEFAULT_VIEW_PORT_SCALE = 1 / (1 + 2 * ADAPTIVE_ICON_INSET_FACTOR);
    private static final float ICON_DIAMETER_FACTOR = 176f / 192;
    private static final float BLUR_FACTOR = 0.5f / 48;
    private static final float KEY_SHADOW_OFFSET_FACTOR = 1f / 48;

    private static final int KEY_SHADOW_ALPHA = 61;
    private static final int AMBIENT_SHADOW_ALPHA = 30;

    @VisibleForTesting
    static final String EXTRA_TYPE = "type";
    @VisibleForTesting
    static final String EXTRA_OBJ = "obj";
    @VisibleForTesting
    static final String EXTRA_INT1 = "int1";
    @VisibleForTesting
    static final String EXTRA_INT2 = "int2";
    @VisibleForTesting
    static final String EXTRA_TINT_LIST = "tint_list";
    @VisibleForTesting
    static final String EXTRA_TINT_MODE = "tint_mode";
    @VisibleForTesting
    static final String EXTRA_STRING1 = "string1";

    /**
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @ParcelField(value = 1,
            defaultValue = "androidx.core.graphics.drawable.IconCompat.TYPE_UNKNOWN")
    public int mType = TYPE_UNKNOWN;

    // To avoid adding unnecessary overhead, we have a few basic objects that get repurposed
    // based on the value of mType.

    // TYPE_BITMAP: Bitmap
    // TYPE_ADAPTIVE_BITMAP: Bitmap
    // TYPE_RESOURCE: String
    // TYPE_URI: String
    // TYPE_DATA: DataBytes
    @NonParcelField
    Object          mObj1;

    /**
     */
    @Nullable
    @RestrictTo(LIBRARY)
    @ParcelField(value = 2, defaultValue = "null")
    public byte[]          mData = null;
    /**
     */
    @Nullable
    @RestrictTo(LIBRARY)
    @ParcelField(value = 3, defaultValue = "null")
    public Parcelable      mParcelable = null;

    // TYPE_RESOURCE: resId
    // TYPE_DATA: data offset
    /**
     */
    @RestrictTo(LIBRARY)
    @ParcelField(value = 4, defaultValue = "0")
    public int             mInt1 = 0;

    // TYPE_DATA: data length
    /**
     */
    @RestrictTo(LIBRARY)
    @ParcelField(value = 5, defaultValue = "0")
    public int             mInt2 = 0;

    /**
     */
    @Nullable
    @RestrictTo(LIBRARY)
    @ParcelField(value = 6, defaultValue = "null")
    public ColorStateList  mTintList = null;

    static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN; // SRC_IN
    @NonParcelField
    PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
    /**
     */
    @Nullable
    @RestrictTo(LIBRARY)
    @ParcelField(value = 7, defaultValue = "null")
    public String mTintModeStr = null;

    /**
     */
    @Nullable
    @RestrictTo(LIBRARY)
    @ParcelField(value = 8, defaultValue = "null")
    public String mString1;

    /**
     * Create an Icon pointing to a drawable resource.
     * @param context The context for the application whose resources should be used to resolve the
     *                given resource ID.
     * @param resId ID of the drawable resource
     * @see android.graphics.drawable.Icon#createWithResource(Context, int)
     */
    @NonNull
    public static IconCompat createWithResource(@NonNull Context context, @DrawableRes int resId) {
        ObjectsCompat.requireNonNull(context);
        return createWithResource(context.getResources(), context.getPackageName(), resId);
    }

    /**
     */
    @NonNull
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public static IconCompat createWithResource(@Nullable Resources r, @NonNull String pkg,
            @DrawableRes int resId) {
        ObjectsCompat.requireNonNull(pkg);
        if (resId == 0) {
            throw new IllegalArgumentException("Drawable resource ID must not be 0");
        }
        final IconCompat rep = new IconCompat(TYPE_RESOURCE);
        rep.mInt1 = resId;
        if (r != null) {
            try {
                rep.mObj1 = r.getResourceName(resId);
            } catch (Resources.NotFoundException e) {
                throw new IllegalArgumentException("Icon resource cannot be found");
            }
        } else {
            rep.mObj1 = pkg;
        }
        rep.mString1 = pkg;
        return rep;
    }

    /**
     * Create an Icon pointing to a bitmap in memory.
     * @param bits A valid {@link android.graphics.Bitmap} object
     * @see android.graphics.drawable.Icon#createWithBitmap(Bitmap)
     */
    @NonNull
    public static IconCompat createWithBitmap(@NonNull Bitmap bits) {
        ObjectsCompat.requireNonNull(bits);
        final IconCompat rep = new IconCompat(TYPE_BITMAP);
        rep.mObj1 = bits;
        return rep;
    }

    /**
     * Create an Icon pointing to a bitmap in memory that follows the icon design guideline defined
     * by {@link android.graphics.drawable.AdaptiveIconDrawable}.
     * @param bits A valid {@link android.graphics.Bitmap} object
     * @see android.graphics.drawable.Icon#createWithAdaptiveBitmap(Bitmap)
     */
    @NonNull
    public static IconCompat createWithAdaptiveBitmap(@NonNull Bitmap bits) {
        ObjectsCompat.requireNonNull(bits);
        final IconCompat rep = new IconCompat(TYPE_ADAPTIVE_BITMAP);
        rep.mObj1 = bits;
        return rep;
    }

    /**
     * Create an Icon pointing to a compressed bitmap stored in a byte array.
     * @param data Byte array storing compressed bitmap data of a type that
     *             {@link android.graphics.BitmapFactory}
     *             can decode (see {@link android.graphics.Bitmap.CompressFormat}).
     * @param offset Offset into <code>data</code> at which the bitmap data starts
     * @param length Length of the bitmap data
     * @see android.graphics.drawable.Icon#createWithData(byte[], int, int)
     */
    @NonNull
    public static IconCompat createWithData(@NonNull byte[] data, int offset, int length) {
        ObjectsCompat.requireNonNull(data);
        final IconCompat rep = new IconCompat(TYPE_DATA);
        rep.mObj1 = data;
        rep.mInt1 = offset;
        rep.mInt2 = length;
        return rep;
    }

    /**
     * Create an Icon pointing to an image file specified by URI.
     *
     * @param uri A uri referring to local content:// or file:// image data.
     * @see android.graphics.drawable.Icon#createWithContentUri(String)
     */
    @NonNull
    public static IconCompat createWithContentUri(@NonNull String uri) {
        ObjectsCompat.requireNonNull(uri);
        final IconCompat rep = new IconCompat(TYPE_URI);
        rep.mObj1 = uri;
        return rep;
    }

    /**
     * Create an Icon pointing to an image file specified by URI.
     *
     * @param uri A uri referring to local content:// or file:// image data.
     * @see android.graphics.drawable.Icon#createWithContentUri(String)
     */
    @NonNull
    public static IconCompat createWithContentUri(@NonNull Uri uri) {
        ObjectsCompat.requireNonNull(uri);
        return createWithContentUri(uri.toString());
    }

    /**
     * Create an Icon pointing to an image file specified by URI. Image file should follow the icon
     * design guideline defined by {@link AdaptiveIconDrawable}.
     *
     * @param uri A uri referring to local content:// or file:// image data.
     * @see android.graphics.drawable.Icon#createWithAdaptiveBitmapContentUri(String)
     */
    @NonNull
    public static IconCompat createWithAdaptiveBitmapContentUri(@NonNull String uri) {
        ObjectsCompat.requireNonNull(uri);
        final IconCompat rep = new IconCompat(TYPE_URI_ADAPTIVE_BITMAP);
        rep.mObj1 = uri;
        return rep;
    }

    /**
     * Create an Icon pointing to an image file specified by URI. Image file should follow the icon
     * design guideline defined by {@link AdaptiveIconDrawable}.
     *
     * @param uri A uri referring to local content:// or file:// image data.
     * @see android.graphics.drawable.Icon#createWithAdaptiveBitmapContentUri(String)
     */
    @NonNull
    public static IconCompat createWithAdaptiveBitmapContentUri(@NonNull Uri uri) {
        ObjectsCompat.requireNonNull(uri);
        return createWithAdaptiveBitmapContentUri(uri.toString());
    }

    /**
     * Used for VersionedParcelable.
     */
    @RestrictTo(LIBRARY)
    public IconCompat() {
    }

    IconCompat(int mType) {
        this.mType = mType;
    }

    /**
     * Gets the type of the icon provided.
     * <p>
     * Note that new types may be added later, so callers should guard against other
     * types being returned.
     */
    @IconType
    public int getType() {
        if (mType == TYPE_UNKNOWN && Build.VERSION.SDK_INT >= 23) {
            return Api23Impl.getType(mObj1);
        }
        return mType;
    }

    /**
     * Gets the package used to create this icon.
     * <p>
     * Only valid for icons of type TYPE_RESOURCE.
     * Note: This package may not be available if referenced in the future, and it is
     * up to the caller to ensure safety if this package is re-used and/or persisted.
     */
    @NonNull
    public String getResPackage() {
        if (mType == TYPE_UNKNOWN && Build.VERSION.SDK_INT >= 23) {
            return Api23Impl.getResPackage(mObj1);
        }
        if (mType != TYPE_RESOURCE) {
            throw new IllegalStateException("called getResPackage() on " + this);
        }
        // Before aosp/1307777, we don't put the package name to mString1. Try to get the
        // package name from the full resource name string. Note that this is not always the same
        // as "the package used to create this icon" and this was what aosp/1307777 tried to fix.
        if (mString1 == null || TextUtils.isEmpty(mString1)) {
            return ((String) mObj1).split(":", -1)[0];
        } else {
            // The name of the getResPackage() API is a bit confusing. It actually returns
            // the app package name rather than the package name in the resource table.
            return mString1;
        }
    }

    /**
     * Gets the drawable resource id used to create this icon.
     * <p>
     * Only valid for icons of type TYPE_RESOURCE.
     * Note: This resource may not be available if the application changes at all, and it is
     * up to the caller to ensure safety if this resource is re-used and/or persisted.
     */
    @DrawableRes
    public int getResId() {
        if (mType == TYPE_UNKNOWN && Build.VERSION.SDK_INT >= 23) {
            return Api23Impl.getResId(mObj1);
        }
        if (mType != TYPE_RESOURCE) {
            throw new IllegalStateException("called getResId() on " + this);
        }
        return mInt1;
    }

    /**
     * Gets the bitmap used to create this icon.
     * <p>
     * Only valid for icons of type TYPE_BITMAP.
     * Note: This bitmap may not be available in the future, and it is
     * up to the caller to ensure safety if this bitmap is re-used and/or persisted.
     *
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @Nullable
    public Bitmap getBitmap() {
        if (mType == TYPE_UNKNOWN && Build.VERSION.SDK_INT >= 23) {
            if (mObj1 instanceof Bitmap) {
                return (Bitmap) mObj1;
            }
            return null;
        }
        if (mType == TYPE_BITMAP) {
            return (Bitmap) mObj1;
        } else if (mType == TYPE_ADAPTIVE_BITMAP) {
            return createLegacyIconFromAdaptiveIcon((Bitmap) mObj1, true);
        } else {
            throw new IllegalStateException("called getBitmap() on " + this);
        }
    }

    /**
     * Gets the uri used to create this icon.
     * <p>
     * Only valid for icons of type TYPE_URI.
     * Note: This uri may not be available in the future, and it is
     * up to the caller to ensure safety if this uri is re-used and/or persisted.
     */
    @NonNull
    public Uri getUri() {
        if (mType == TYPE_UNKNOWN && Build.VERSION.SDK_INT >= 23) {
            return Api23Impl.getUri(mObj1);
        }
        if (mType != TYPE_URI && mType != TYPE_URI_ADAPTIVE_BITMAP) {
            throw new IllegalStateException("called getUri() on " + this);
        }
        return Uri.parse((String) mObj1);
    }

    /**
     * Store a color to use whenever this Icon is drawn.
     *
     * @param tint a color, as in {@link Drawable#setTint(int)}
     * @return this same object, for use in chained construction
     */
    @NonNull
    public IconCompat setTint(@ColorInt int tint) {
        return setTintList(ColorStateList.valueOf(tint));
    }

    /**
     * Store a color to use whenever this Icon is drawn.
     *
     * @param tintList as in {@link Drawable#setTintList(ColorStateList)}, null to remove tint
     * @return this same object, for use in chained construction
     */
    @NonNull
    public IconCompat setTintList(@Nullable ColorStateList tintList) {
        mTintList = tintList;
        return this;
    }

    /**
     * Store a blending mode to use whenever this Icon is drawn.
     *
     * @param mode a blending mode, as in {@link Drawable#setTintMode(PorterDuff.Mode)}, may be null
     * @return this same object, for use in chained construction
     */
    @NonNull
    public IconCompat setTintMode(@Nullable PorterDuff.Mode mode) {
        mTintMode = mode;
        return this;
    }

    /**
     * @deprecated Use {@link #toIcon(Context)} to generate the {@link Icon} object.
     */
    @RequiresApi(23)
    @Deprecated
    @NonNull
    public Icon toIcon() {
        return toIcon(null);
    }

    /**
     * Convert this compat object to {@link Icon} object.
     *
     * @return {@link Icon} object
     */
    @RequiresApi(23)
    @NonNull
    public Icon toIcon(@Nullable Context context) {
        if (Build.VERSION.SDK_INT >= 23) {
            return Api23Impl.toIcon(this, context);
        } else {
            throw new UnsupportedOperationException(
                    "This method is only supported on API level 23+");
        }
    }

    /**
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public void checkResource(@NonNull Context context) {
        if (mType == TYPE_RESOURCE && mObj1 != null) {
            String fullResName = (String) mObj1;
            if (!fullResName.contains(":")) {
                return;
            }
            // Do some splitting to parse out each of the components.
            String resName = fullResName.split(":", -1)[1];
            String resType = resName.split("/", -1)[0];
            resName = resName.split("/", -1)[1];
            String resPackage = fullResName.split(":", -1)[0];
            if ("0_resource_name_obfuscated".equals(resName)) {
                // All obfuscated resources have the same name, so not going to look up the
                // resource identifier from the resource name.
                Log.i(TAG, "Found obfuscated resource, not trying to update resource id for it");
                return;
            }
            String appPackage = getResPackage();
            Resources res = getResources(context, appPackage);
            int id = res.getIdentifier(resName, resType, resPackage);
            if (mInt1 != id) {
                Log.i(TAG, "Id has changed for " + appPackage + " " + fullResName);
                mInt1 = id;
            }
        }
    }

    /**
     * Returns a Drawable that can be used to draw the image inside this Icon, constructing it
     * if necessary.
     *
     * @param context {@link android.content.Context Context} in which to load the drawable; used
     *                to access {@link android.content.res.Resources Resources}, for example.
     * @return A fresh instance of a drawable for this image, yours to keep.
     */
    @Nullable
    public Drawable loadDrawable(@NonNull Context context) {
        checkResource(context);
        if (Build.VERSION.SDK_INT >= 23) {
            return Api23Impl.loadDrawable(toIcon(context), context);
        }
        final Drawable result = loadDrawableInner(context);
        if (result != null && (mTintList != null || mTintMode != DEFAULT_TINT_MODE)) {
            result.mutate();
            DrawableCompat.setTintList(result, mTintList);
            DrawableCompat.setTintMode(result, mTintMode);
        }
        return result;
    }

    /**
     * Do the heavy lifting of loading the drawable, but stop short of applying any tint.
     */
    private Drawable loadDrawableInner(Context context) {
        switch (mType) {
            case TYPE_BITMAP:
                return new BitmapDrawable(context.getResources(), (Bitmap) mObj1);
            case TYPE_ADAPTIVE_BITMAP:
                return new BitmapDrawable(context.getResources(),
                        createLegacyIconFromAdaptiveIcon((Bitmap) mObj1, false));
            case TYPE_RESOURCE:
                // figure out where to load resources from
                String resPackage = getResPackage();
                if (TextUtils.isEmpty(resPackage)) {
                    // if none is specified, try the given context
                    resPackage = context.getPackageName();
                }
                Resources res = getResources(context, resPackage);
                try {
                    return ResourcesCompat.getDrawable(res, mInt1, context.getTheme());
                } catch (RuntimeException e) {
                    Log.e(TAG, String.format("Unable to load resource 0x%08x from pkg=%s",
                            mInt1,
                            mObj1),
                            e);
                }
                break;
            case TYPE_DATA:
                return new BitmapDrawable(context.getResources(),
                        BitmapFactory.decodeByteArray((byte[]) mObj1, mInt1, mInt2)
                );
            case TYPE_URI:
                InputStream is = getUriInputStream(context);
                if (is != null) {
                    return new BitmapDrawable(context.getResources(),
                            BitmapFactory.decodeStream(is));
                }
                break;
            case TYPE_URI_ADAPTIVE_BITMAP:
                is = getUriInputStream(context);
                if (is != null) {
                    if (Build.VERSION.SDK_INT >= 26) {
                        return Api26Impl.createAdaptiveIconDrawable(null,
                                new BitmapDrawable(context.getResources(),
                                        BitmapFactory.decodeStream(is)));
                    } else {
                        return new BitmapDrawable(context.getResources(),
                                createLegacyIconFromAdaptiveIcon(
                                        BitmapFactory.decodeStream(is), false));
                    }
                }
                break;
        }
        return null;
    }

    /**
     * Create an input stream for bitmap by resolving corresponding content uri.
     *
     */
    @Nullable
    @RestrictTo(LIBRARY_GROUP)
    public InputStream getUriInputStream(@NonNull Context context) {
        final Uri uri = getUri();
        final String scheme = uri.getScheme();
        if (ContentResolver.SCHEME_CONTENT.equals(scheme)
                || ContentResolver.SCHEME_FILE.equals(scheme)) {
            try {
                return context.getContentResolver().openInputStream(uri);
            } catch (Exception e) {
                Log.w(TAG, "Unable to load image from URI: " + uri, e);
            }
        } else {
            try {
                return new FileInputStream(new File((String) mObj1));
            } catch (FileNotFoundException e) {
                Log.w(TAG, "Unable to load image from path: " + uri, e);
            }
        }
        return null;
    }

    @SuppressWarnings("deprecation")
    static Resources getResources(Context context, String resPackage) {
        if ("android".equals(resPackage)) {
            return Resources.getSystem();
        } else {
            final PackageManager pm = context.getPackageManager();
            try {
                ApplicationInfo ai = pm.getApplicationInfo(
                        resPackage, PackageManager.MATCH_UNINSTALLED_PACKAGES);
                if (ai != null) {
                    return pm.getResourcesForApplication(ai);
                } else {
                    return null;
                }
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, String.format("Unable to find pkg=%s for icon",
                        resPackage), e);
                return null;
            }
        }
    }


    /**
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @SuppressWarnings("deprecation")
    public void addToShortcutIntent(@NonNull Intent outIntent, @Nullable Drawable badge,
            @NonNull Context c) {
        checkResource(c);
        Bitmap icon;
        switch (mType) {
            case TYPE_BITMAP:
                icon = (Bitmap) mObj1;
                if (badge != null) {
                    // Do not modify the original icon when applying a badge
                    icon = icon.copy(icon.getConfig(), true);
                }
                break;
            case TYPE_ADAPTIVE_BITMAP:
                icon = createLegacyIconFromAdaptiveIcon((Bitmap) mObj1, true);
                break;
            case TYPE_RESOURCE:
                try {
                    Context context = c.createPackageContext(getResPackage(), 0);
                    if (badge == null) {
                        outIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
                                Intent.ShortcutIconResource.fromContext(context, mInt1));
                        return;
                    } else {
                        Drawable dr = ContextCompat.getDrawable(context, mInt1);
                        if (dr.getIntrinsicWidth() <= 0 || dr.getIntrinsicHeight() <= 0) {
                            int size = ((ActivityManager) context.getSystemService(
                                    Context.ACTIVITY_SERVICE)).getLauncherLargeIconSize();
                            icon = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
                        } else {
                            icon = Bitmap.createBitmap(dr.getIntrinsicWidth(),
                                    dr.getIntrinsicHeight(),
                                    Bitmap.Config.ARGB_8888);
                        }
                        dr.setBounds(0, 0, icon.getWidth(), icon.getHeight());
                        dr.draw(new Canvas(icon));
                    }
                } catch (PackageManager.NameNotFoundException e) {
                    throw new IllegalArgumentException("Can't find package " + mObj1, e);
                }
                break;
            default:
                throw new IllegalArgumentException("Icon type not supported for intent shortcuts");
        }
        if (badge != null) {
            // Badge the icon
            int w = icon.getWidth();
            int h = icon.getHeight();
            badge.setBounds(w / 2, h / 2, w, h);
            badge.draw(new Canvas(icon));
        }
        outIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
    }

    /**
     * Adds this Icon to a Bundle that can be read back with the same parameters
     * to {@link #createFromBundle(Bundle)}.
     */
    @NonNull
    public Bundle toBundle() {
        Bundle bundle = new Bundle();
        switch (mType) {
            case TYPE_BITMAP:
            case TYPE_ADAPTIVE_BITMAP:
                bundle.putParcelable(EXTRA_OBJ, (Bitmap) mObj1);
                break;
            case TYPE_UNKNOWN:
                // When unknown just wrapping an Icon.
                bundle.putParcelable(EXTRA_OBJ, (Parcelable) mObj1);
                break;
            case TYPE_RESOURCE:
            case TYPE_URI:
            case TYPE_URI_ADAPTIVE_BITMAP:
                bundle.putString(EXTRA_OBJ, (String) mObj1);
                break;
            case TYPE_DATA:
                bundle.putByteArray(EXTRA_OBJ, (byte[]) mObj1);
                break;
            default:
                throw new IllegalArgumentException("Invalid icon");
        }
        bundle.putInt(EXTRA_TYPE, mType);
        bundle.putInt(EXTRA_INT1, mInt1);
        bundle.putInt(EXTRA_INT2, mInt2);
        bundle.putString(EXTRA_STRING1, mString1);
        if (mTintList != null) {
            bundle.putParcelable(EXTRA_TINT_LIST, mTintList);
        }
        if (mTintMode != DEFAULT_TINT_MODE) {
            bundle.putString(EXTRA_TINT_MODE, mTintMode.name());
        }
        return bundle;
    }

    @NonNull
    @Override
    public String toString() {
        if (mType == TYPE_UNKNOWN) {
            return String.valueOf(mObj1);
        }
        final StringBuilder sb = new StringBuilder("Icon(typ=").append(typeToString(mType));
        switch (mType) {
            case TYPE_BITMAP:
            case TYPE_ADAPTIVE_BITMAP:
                sb.append(" size=")
                        .append(((Bitmap) mObj1).getWidth())
                        .append("x")
                        .append(((Bitmap) mObj1).getHeight());
                break;
            case TYPE_RESOURCE:
                sb.append(" pkg=")
                        .append(mString1)
                        .append(" id=")
                        .append(String.format("0x%08x", getResId()));
                break;
            case TYPE_DATA:
                sb.append(" len=").append(mInt1);
                if (mInt2 != 0) {
                    sb.append(" off=").append(mInt2);
                }
                break;
            case TYPE_URI:
            case TYPE_URI_ADAPTIVE_BITMAP:
                sb.append(" uri=").append(mObj1);
                break;
        }
        if (mTintList != null) {
            sb.append(" tint=");
            sb.append(mTintList);
        }
        if (mTintMode != DEFAULT_TINT_MODE) {
            sb.append(" mode=").append(mTintMode);
        }
        sb.append(")");
        return sb.toString();
    }

    @Override
    public void onPreParceling(boolean isStream) {
        mTintModeStr = mTintMode.name();
        switch (mType) {
            case TYPE_UNKNOWN:
                if (isStream) {
                    // We can't determine how to serialize this icon, so throw so the caller knows.
                    throw new IllegalArgumentException("Can't serialize Icon created with "
                            + "IconCompat#createFromIcon");
                } else {
                    mParcelable = (Parcelable) mObj1;
                }
                break;
            case TYPE_ADAPTIVE_BITMAP:
            case TYPE_BITMAP:
                if (isStream) {
                    Bitmap bitmap = (Bitmap) mObj1;
                    ByteArrayOutputStream data = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.PNG, 90, data);
                    mData = data.toByteArray();
                } else {
                    mParcelable = (Parcelable) mObj1;
                }
                break;
            case TYPE_URI:
            case TYPE_URI_ADAPTIVE_BITMAP:
                mData = mObj1.toString().getBytes(Charset.forName("UTF-16"));
                break;
            case TYPE_RESOURCE:
                mData = ((String) mObj1).getBytes(Charset.forName("UTF-16"));
                break;
            case TYPE_DATA:
                mData = (byte[]) mObj1;
                break;
        }
    }

    @Override
    public void onPostParceling() {
        mTintMode = PorterDuff.Mode.valueOf(mTintModeStr);
        switch (mType) {
            case TYPE_UNKNOWN:
                if (mParcelable != null) {
                    mObj1 = mParcelable;
                } else {
                    throw new IllegalArgumentException("Invalid icon");
                }
                break;
            case TYPE_ADAPTIVE_BITMAP:
            case TYPE_BITMAP:
                if (mParcelable != null) {
                    mObj1 = mParcelable;
                } else {
                    // This is data now.
                    mObj1 = mData;
                    mType = TYPE_DATA;
                    mInt1 = 0;
                    mInt2 = mData.length;
                }
                break;
            case TYPE_URI:
            case TYPE_URI_ADAPTIVE_BITMAP:
            case TYPE_RESOURCE:
                mObj1 = new String(mData, Charset.forName("UTF-16"));
                // Slice, which may contain a IconCompat object, supports serialization to file.
                // In the old format, we don't store the app package name separately. To keep
                // the backward-compatibility, we have no choice but read the package name from the
                // full resource name string.
                if (mType == TYPE_RESOURCE) {
                    if (mString1 == null) {
                        mString1 = ((String) mObj1).split(":", -1)[0];
                    }
                }
                break;
            case TYPE_DATA:
                mObj1 = mData;
                break;
        }
    }

    private static String typeToString(int x) {
        switch (x) {
            case TYPE_BITMAP: return "BITMAP";
            case TYPE_ADAPTIVE_BITMAP: return "BITMAP_MASKABLE";
            case TYPE_DATA: return "DATA";
            case TYPE_RESOURCE: return "RESOURCE";
            case TYPE_URI: return "URI";
            case TYPE_URI_ADAPTIVE_BITMAP: return "URI_MASKABLE";
            default: return "UNKNOWN";
        }
    }

    /**
     * Extracts an icon from a bundle that was added using {@link #toBundle()}.
     */
    @SuppressWarnings("deprecation")
    public static @Nullable IconCompat createFromBundle(@NonNull Bundle bundle) {
        int type = bundle.getInt(EXTRA_TYPE);
        IconCompat icon = new IconCompat(type);
        icon.mInt1 = bundle.getInt(EXTRA_INT1);
        icon.mInt2 = bundle.getInt(EXTRA_INT2);
        icon.mString1 = bundle.getString(EXTRA_STRING1);
        if (bundle.containsKey(EXTRA_TINT_LIST)) {
            icon.mTintList = bundle.getParcelable(EXTRA_TINT_LIST);
        }
        if (bundle.containsKey(EXTRA_TINT_MODE)) {
            icon.mTintMode = PorterDuff.Mode.valueOf(
                    bundle.getString(EXTRA_TINT_MODE));
        }
        switch (type) {
            case TYPE_BITMAP:
            case TYPE_ADAPTIVE_BITMAP:
            case TYPE_UNKNOWN:
                icon.mObj1 = bundle.getParcelable(EXTRA_OBJ);
                break;
            case TYPE_RESOURCE:
            case TYPE_URI:
            case TYPE_URI_ADAPTIVE_BITMAP:
                icon.mObj1 = bundle.getString(EXTRA_OBJ);
                break;
            case TYPE_DATA:
                icon.mObj1 = bundle.getByteArray(EXTRA_OBJ);
                break;
            default:
                Log.w(TAG, "Unknown type " + type);
                return null;
        }
        return icon;
    }

    /**
     * Creates an IconCompat from an Icon.
     */
    @RequiresApi(23)
    @Nullable
    public static IconCompat createFromIcon(@NonNull Context context, @NonNull Icon icon) {
        Preconditions.checkNotNull(icon);
        return Api23Impl.createFromIcon(context, icon);
    }

    /**
     * Creates an IconCompat from an Icon.
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @RequiresApi(23)
    @Nullable
    public static IconCompat createFromIcon(@NonNull Icon icon) {
        return Api23Impl.createFromIconInner(icon);
    }

    /**
     * Creates an IconCompat from an Icon, or returns null if the given Icon is created from
     * resource 0.
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @RequiresApi(23)
    @Nullable
    public static IconCompat createFromIconOrNullIfZeroResId(@NonNull Icon icon) {
        if (Api23Impl.getType(icon) == TYPE_RESOURCE && Api23Impl.getResId(icon) == 0) {
            return null;
        }
        return Api23Impl.createFromIconInner(icon);
    }

    /**
     * Converts a bitmap following the adaptive icon guide lines, into a bitmap following the
     * shortcut icon guide lines.
     * The returned bitmap will always have same width and height and clipped to a circle.
     *
     * @param addShadow set to {@code true} only for legacy shortcuts and {@code false} otherwise
     */
    @VisibleForTesting
    static Bitmap createLegacyIconFromAdaptiveIcon(Bitmap adaptiveIconBitmap, boolean addShadow) {
        int size = (int) (DEFAULT_VIEW_PORT_SCALE * Math.min(adaptiveIconBitmap.getWidth(),
                adaptiveIconBitmap.getHeight()));

        Bitmap icon = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(icon);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

        float center = size * 0.5f;
        float radius = center * ICON_DIAMETER_FACTOR;

        if (addShadow) {
            // Draw key shadow
            float blur = BLUR_FACTOR * size;
            paint.setColor(Color.TRANSPARENT);
            paint.setShadowLayer(blur, 0, KEY_SHADOW_OFFSET_FACTOR * size, KEY_SHADOW_ALPHA << 24);
            canvas.drawCircle(center, center, radius, paint);

            // Draw ambient shadow
            paint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
            canvas.drawCircle(center, center, radius, paint);
            paint.clearShadowLayer();
        }

        // Draw the clipped icon
        paint.setColor(Color.BLACK);
        BitmapShader shader = new BitmapShader(adaptiveIconBitmap, Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);
        Matrix shift = new Matrix();
        shift.setTranslate(-(adaptiveIconBitmap.getWidth() - size) / 2.0f,
                -(adaptiveIconBitmap.getHeight() - size) / 2.0f);
        shader.setLocalMatrix(shift);
        paint.setShader(shader);
        canvas.drawCircle(center, center, radius, paint);

        canvas.setBitmap(null);
        return icon;
    }

    @RequiresApi(28)
    static class Api28Impl {
        private Api28Impl() {
            // This class is not instantiable.
        }

        static String getResPackage(Object icon) {
            return ((Icon) icon).getResPackage();
        }

        static int getType(Object icon) {
            return ((Icon) icon).getType();
        }

        static int getResId(Object icon) {
            return ((Icon) icon).getResId();
        }

        static Uri getUri(Object icon) {
            return ((Icon) icon).getUri();
        }
    }

    @RequiresApi(26)
    static class Api26Impl {
        private Api26Impl() {
            // This class is not instantiable.
        }

        static Drawable createAdaptiveIconDrawable(Drawable backgroundDrawable,
                Drawable foregroundDrawable) {
            return new AdaptiveIconDrawable(backgroundDrawable, foregroundDrawable);
        }

        static Icon createWithAdaptiveBitmap(Bitmap bits) {
            return Icon.createWithAdaptiveBitmap(bits);
        }
    }

    @RequiresApi(30)
    static class Api30Impl {
        private Api30Impl() {
            // This class is not instantiable.
        }

        static Icon createWithAdaptiveBitmapContentUri(Uri uri) {
            return Icon.createWithAdaptiveBitmapContentUri(uri);
        }

    }

    @RequiresApi(23)
    static class Api23Impl {
        private Api23Impl() {
            // This class is not instantiable.
        }

        @Nullable
        static IconCompat createFromIcon(@NonNull Context context, @NonNull Icon icon) {
            switch (getType(icon)) {
                case TYPE_RESOURCE:
                    String resPackage = getResPackage(icon);
                    try {
                        return createWithResource(getResources(context, resPackage), resPackage,
                                getResId(icon));
                    } catch (Resources.NotFoundException e) {
                        throw new IllegalArgumentException("Icon resource cannot be found");
                    }
                case TYPE_URI:
                    return createWithContentUri(getUri(icon));
                case TYPE_URI_ADAPTIVE_BITMAP:
                    return createWithAdaptiveBitmapContentUri(getUri(icon));
            }
            IconCompat iconCompat = new IconCompat(TYPE_UNKNOWN);
            iconCompat.mObj1 = icon;
            return iconCompat;
        }

        /**
         * Gets the type of the icon provided.
         * <p>
         * Note that new types may be added later, so callers should guard against other
         * types being returned. Returns {@link #TYPE_UNKNOWN} when the type cannot be
         * determined.
         */
        @IconType
        static int getType(@NonNull Object icon) {
            if (Build.VERSION.SDK_INT >= 28) {
                return Api28Impl.getType(icon);
            } else {
                try {
                    return (int) icon.getClass().getMethod("getType").invoke(icon);
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "Unable to get icon type " + icon, e);
                    return TYPE_UNKNOWN;
                } catch (InvocationTargetException e) {
                    Log.e(TAG, "Unable to get icon type " + icon, e);
                    return TYPE_UNKNOWN;
                } catch (NoSuchMethodException e) {
                    Log.e(TAG, "Unable to get icon type " + icon, e);
                    return TYPE_UNKNOWN;
                }
            }
        }

        /**
         * Gets the package used to create this icon.
         * <p>
         * Only valid for icons of type TYPE_RESOURCE.
         * Note: This package may not be available if referenced in the future, and it is
         * up to the caller to ensure safety if this package is re-used and/or persisted.
         * Returns {@code null} when the value cannot be gotten.
         */
        @Nullable
        static String getResPackage(@NonNull Object icon) {
            if (Build.VERSION.SDK_INT >= 28) {
                return Api28Impl.getResPackage(icon);
            } else {
                try {
                    return (String) icon.getClass().getMethod("getResPackage").invoke(icon);
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "Unable to get icon package", e);
                    return null;
                } catch (InvocationTargetException e) {
                    Log.e(TAG, "Unable to get icon package", e);
                    return null;
                } catch (NoSuchMethodException e) {
                    Log.e(TAG, "Unable to get icon package", e);
                    return null;
                }
            }
        }

        /**
         * Used internally to avoid casting to Icon class in code accessible to SDK < 23.
         */
        static IconCompat createFromIconInner(@NonNull Object icon) {
            Preconditions.checkNotNull(icon);
            switch (getType(icon)) {
                case TYPE_RESOURCE:
                    return createWithResource(null, getResPackage(icon), getResId(icon));
                case TYPE_URI:
                    return createWithContentUri(getUri(icon));
                case TYPE_URI_ADAPTIVE_BITMAP:
                    return createWithAdaptiveBitmapContentUri(getUri(icon));
            }
            IconCompat iconCompat = new IconCompat(TYPE_UNKNOWN);
            iconCompat.mObj1 = icon;
            return iconCompat;
        }

        /**
         * Gets the resource used to create this icon.
         * <p>
         * Only valid for icons of type TYPE_RESOURCE.
         * Note: This resource may not be available if the application changes at all, and it is
         * up to the caller to ensure safety if this resource is re-used and/or persisted.
         * Returns {@code 0} if the id cannot be gotten.
         */
        @IdRes
        @DrawableRes
        static int getResId(@NonNull Object icon) {
            if (Build.VERSION.SDK_INT >= 28) {
                return Api28Impl.getResId(icon);
            } else {
                try {
                    return (int) icon.getClass().getMethod("getResId").invoke(icon);
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "Unable to get icon resource", e);
                    return 0;
                } catch (InvocationTargetException e) {
                    Log.e(TAG, "Unable to get icon resource", e);
                    return 0;
                } catch (NoSuchMethodException e) {
                    Log.e(TAG, "Unable to get icon resource", e);
                    return 0;
                }
            }
        }

        /**
         * Gets the uri used to create this icon.
         * <p>
         * Only valid for icons of type TYPE_URI.
         * Note: This uri may not be available in the future, and it is
         * up to the caller to ensure safety if this uri is re-used and/or persisted.
         * Returns {@code null} if the uri cannot be gotten.
         */
        @Nullable
        static Uri getUri(@NonNull Object icon) {
            if (Build.VERSION.SDK_INT >= 28) {
                return Api28Impl.getUri(icon);
            } else {
                try {
                    return (Uri) icon.getClass().getMethod("getUri").invoke(icon);
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "Unable to get icon uri", e);
                    return null;
                } catch (InvocationTargetException e) {
                    Log.e(TAG, "Unable to get icon uri", e);
                    return null;
                } catch (NoSuchMethodException e) {
                    Log.e(TAG, "Unable to get icon uri", e);
                    return null;
                }
            }
        }

        static Icon toIcon(IconCompat iconCompat, Context context) {
            Icon icon;
            switch (iconCompat.mType) {
                case TYPE_UNKNOWN:
                    // When type is unknown we are just wrapping an icon.
                    return (Icon) iconCompat.mObj1;
                case TYPE_BITMAP:
                    icon = Icon.createWithBitmap((Bitmap) iconCompat.mObj1);
                    break;
                case TYPE_ADAPTIVE_BITMAP:
                    if (Build.VERSION.SDK_INT >= 26) {
                        icon = Api26Impl.createWithAdaptiveBitmap((Bitmap) iconCompat.mObj1);
                    } else {
                        icon = Icon.createWithBitmap(
                                createLegacyIconFromAdaptiveIcon((Bitmap) iconCompat.mObj1, false));
                    }
                    break;
                case TYPE_RESOURCE:
                    icon = Icon.createWithResource(iconCompat.getResPackage(), iconCompat.mInt1);
                    break;
                case TYPE_DATA:
                    icon = Icon.createWithData((byte[]) iconCompat.mObj1, iconCompat.mInt1,
                            iconCompat.mInt2);
                    break;
                case TYPE_URI:
                    icon = Icon.createWithContentUri((String) iconCompat.mObj1);
                    break;
                case TYPE_URI_ADAPTIVE_BITMAP:
                    if (Build.VERSION.SDK_INT >= 30) {
                        icon = Api30Impl.createWithAdaptiveBitmapContentUri(iconCompat.getUri());
                        break;
                    }
                    if (context == null) {
                        throw new IllegalArgumentException(
                                "Context is required to resolve the file uri of the icon: "
                                        + iconCompat.getUri());
                    }
                    InputStream is = iconCompat.getUriInputStream(context);
                    if (is == null) {
                        throw new IllegalStateException(
                                "Cannot load adaptive icon from uri: " + iconCompat.getUri());
                    }
                    if (Build.VERSION.SDK_INT >= 26) {
                        icon = Api26Impl.createWithAdaptiveBitmap(BitmapFactory.decodeStream(is));
                    } else {
                        icon = Icon.createWithBitmap(createLegacyIconFromAdaptiveIcon(
                                BitmapFactory.decodeStream(is), false));
                    }
                    break;
                default:
                    throw new IllegalArgumentException("Unknown type");
            }
            if (iconCompat.mTintList != null) {
                icon.setTintList(iconCompat.mTintList);
            }
            if (iconCompat.mTintMode != DEFAULT_TINT_MODE) {
                icon.setTintMode(iconCompat.mTintMode);
            }
            return icon;
        }

        static Drawable loadDrawable(Icon icon, Context context) {
            return icon.loadDrawable(context);
        }
    }
}