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

Commit ceb26936 authored by Chris Craik's avatar Chris Craik
Browse files

Change Resources.getDrawable nullability

Resources.getDrawable() was annotated @Nullable because there was one
very particular path where Bitmap decoding would fail and trigger a null
return.

As part of the switch to ImageDecoder, that path was changed to now throw
an IOException, which will result as a NotFoundException to the caller of
getDrawable.

This CL annotates that path as @NonNull to reduce pain of dealing with
@Nullable method that was very unlikely to be null in practice.

Also fixes many other missing nullability annotations, and relabel
many @Nullable paths that would never return null in practice as
@NonNull.

Bug: 69543526
Test: ResourcesTest

Change-Id: Ib01eca970c5c9969998ce5b265b120aa7048b41a
parent bc48bd8f
Loading
Loading
Loading
Loading
+5 −6
Original line number Diff line number Diff line
@@ -560,6 +560,7 @@ public abstract class Context {
     *
     * @param resId Resource id for the CharSequence text
     */
    @NonNull
    public final CharSequence getText(@StringRes int resId) {
        return getResources().getText(resId);
    }
@@ -616,12 +617,11 @@ public abstract class Context {
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
     * @return An object that can be used to draw this resource, or
     *         {@code null} if the resource could not be resolved.
     * @return An object that can be used to draw this resource.
     * @throws android.content.res.Resources.NotFoundException if the given ID
     *         does not exist.
     */
    @Nullable
    @NonNull
    public final Drawable getDrawable(@DrawableRes int id) {
        return getResources().getDrawable(id, getTheme());
    }
@@ -633,12 +633,11 @@ public abstract class Context {
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
     * @return A color state list, or {@code null} if the resource could not be
     *         resolved.
     * @return A color state list.
     * @throws android.content.res.Resources.NotFoundException if the given ID
     *         does not exist.
     */
    @Nullable
    @NonNull
    public final ColorStateList getColorStateList(@ColorRes int id) {
        return getResources().getColorStateList(id, getTheme());
    }
+11 −6
Original line number Diff line number Diff line
@@ -867,8 +867,9 @@ public class Resources {
     * @param theme The theme used to style the drawable attributes, may be {@code null}.
     * @return Drawable An object that can be used to draw this resource.
     * @throws NotFoundException Throws NotFoundException if the given ID does
     *             not exist.
     *             not exist, or cannot be decoded.
     */
    @NonNull
    public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
        final TypedValue value = obtainTempTypedValue();
        try {
@@ -980,7 +981,7 @@ public class Resources {
     *         or multiple colors that can be selected based on a state.
     * @deprecated Use {@link #getColorStateList(int, Theme)} instead.
     */
    @Nullable
    @NonNull
    @Deprecated
    public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
        final ColorStateList csl = getColorStateList(id, null);
@@ -1011,7 +1012,7 @@ public class Resources {
     * @return A themed ColorStateList object containing either a single solid
     *         color or multiple colors that can be selected based on a state.
     */
    @Nullable
    @NonNull
    public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
            throws NotFoundException {
        final TypedValue value = obtainTempTypedValue();
@@ -1024,7 +1025,7 @@ public class Resources {
        }
    }

    @Nullable
    @NonNull
    ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
            throws NotFoundException {
        return mResourcesImpl.loadColorStateList(this, value, id, theme);
@@ -1033,7 +1034,7 @@ public class Resources {
    /**
     * @hide
     */
    @Nullable
    @NonNull
    public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
        return mResourcesImpl.loadComplexColor(this, value, id, theme);
    }
@@ -1139,6 +1140,7 @@ public class Resources {
     *         
     * @see #getXml
     */
    @NonNull
    public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
        return loadXmlResourceParser(id, "layout");
    }
@@ -1163,6 +1165,7 @@ public class Resources {
     *         
     * @see #getXml
     */
    @NonNull
    public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException {
        return loadXmlResourceParser(id, "anim");
    }
@@ -1188,6 +1191,7 @@ public class Resources {
     *         
     * @see android.util.AttributeSet
     */
    @NonNull
    public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
        return loadXmlResourceParser(id, "xml");
    }
@@ -1203,8 +1207,8 @@ public class Resources {
     * @return InputStream Access to the resource data.
     *
     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
     * 
     */
    @NonNull
    public InputStream openRawResource(@RawRes int id) throws NotFoundException {
        final TypedValue value = obtainTempTypedValue();
        try {
@@ -1261,6 +1265,7 @@ public class Resources {
     *
     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
     */
    @NonNull
    public InputStream openRawResource(@RawRes int id, TypedValue value)
            throws NotFoundException {
        return mResourcesImpl.openRawResource(id, value);
+20 −19
Original line number Diff line number Diff line
@@ -544,7 +544,7 @@ public class ResourcesImpl {
        }
    }

    @Nullable
    @NonNull
    Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
            int density, @Nullable Resources.Theme theme)
            throws NotFoundException {
@@ -628,7 +628,7 @@ public class ResourcesImpl {
            } else if (isColorDrawable) {
                dr = new ColorDrawable(value.data);
            } else {
                dr = loadDrawableForCookie(wrapper, value, id, density, null);
                dr = loadDrawableForCookie(wrapper, value, id, density);
            }
            // DrawableContainer' constant state has drawables instances. In order to leave the
            // constant state intact in the cache, we need to create a new DrawableContainer after
@@ -755,8 +755,9 @@ public class ResourcesImpl {
    /**
     * Loads a drawable from XML or resources stream.
     */
    @NonNull
    private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
            int id, int density, @Nullable Resources.Theme theme) {
            int id, int density) {
        if (value.string == null) {
            throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
                    + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
@@ -775,22 +776,23 @@ public class ResourcesImpl {
            }
        }

        // For prelaod tracing.
        // For preload tracing.
        long startTime = 0;
        int startBitmapCount = 0;
        long startBitmapSize = 0;
        int startDrwableCount = 0;
        int startDrawableCount = 0;
        if (TRACE_FOR_DETAILED_PRELOAD) {
            startTime = System.nanoTime();
            startBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
            startBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
            startDrwableCount = sPreloadTracingNumLoadedDrawables;
            startDrawableCount = sPreloadTracingNumLoadedDrawables;
        }

        if (DEBUG_LOAD) {
            Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
        }


        final Drawable dr;

        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
@@ -805,7 +807,7 @@ public class ResourcesImpl {
                if (file.endsWith(".xml")) {
                    final XmlResourceParser rp = loadXmlResourceParser(
                            file, id, value.assetCookie, "drawable");
                    dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
                    dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
                    rp.close();
                } else {
                    final InputStream is = mAssets.openNonAsset(
@@ -840,7 +842,7 @@ public class ResourcesImpl {
                    final long loadedBitmapSize =
                            Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize;
                    final int loadedDrawables =
                            sPreloadTracingNumLoadedDrawables - startDrwableCount;
                            sPreloadTracingNumLoadedDrawables - startDrawableCount;

                    sPreloadTracingNumLoadedDrawables++;

@@ -916,6 +918,7 @@ public class ResourcesImpl {
     * first try to load CSL from the cache. If not found, try to get from the constant state.
     * Last, parse the XML and generate the CSL.
     */
    @NonNull
    private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
            TypedValue value, int id) {
        final long key = (((long) value.assetCookie) << 32) | value.data;
@@ -935,7 +938,6 @@ public class ResourcesImpl {
            complexColor = loadComplexColorForCookie(wrapper, value, id, theme);
        }

        if (complexColor != null) {
        complexColor.setBaseChangingConfigurations(value.changingConfigurations);

        if (mPreloading) {
@@ -946,7 +948,6 @@ public class ResourcesImpl {
        } else {
            cache.put(key, theme, complexColor.getConstantState());
        }
        }
        return complexColor;
    }

@@ -991,7 +992,7 @@ public class ResourcesImpl {
        return complexColor;
    }

    @Nullable
    @NonNull
    ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
            Resources.Theme theme)
            throws NotFoundException {
@@ -1051,7 +1052,7 @@ public class ResourcesImpl {
     *
     * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
     */
    @Nullable
    @NonNull
    private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
            Resources.Theme theme) {
        if (value.string == null) {
+10 −4
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package android.graphics;

import static android.graphics.BitmapFactory.Options.validate;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Trace;
@@ -518,8 +520,9 @@ public class BitmapFactory {
     *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
     *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
     */
    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
            InputStream is, Rect pad, Options opts) {
    @Nullable
    public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
            @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
        validate(opts);
        if (opts == null) {
            opts = new Options();
@@ -707,7 +710,9 @@ public class BitmapFactory {
     * <code>is.mark(1024)</code> would be called. As of
     * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
     */
    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
    @Nullable
    public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding,
            @Nullable Options opts) {
        // we don't throw in this case, thus allowing the caller to only check
        // the cache, and not force the image to be decoded.
        if (is == null) {
@@ -742,7 +747,8 @@ public class BitmapFactory {
     * Private helper function for decoding an InputStream natively. Buffers the input enough to
     * do a rewind as needed, and supplies temporary storage if necessary. is MUST NOT be null.
     */
    private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
    private static Bitmap decodeStreamInternal(@NonNull InputStream is,
            @Nullable Rect outPadding, @Nullable Options opts) {
        // ASSERT(is != null);
        byte [] tempStorage = null;
        if (opts != null) tempStorage = opts.inTempStorage;
+4 −3
Original line number Diff line number Diff line
@@ -1174,8 +1174,10 @@ public abstract class Drawable {
     *
     * @deprecated Prefer the version without an Options object.
     */
    public static Drawable createFromResourceStream(Resources res, TypedValue value,
            InputStream is, String srcName, BitmapFactory.Options opts) {
    @Nullable
    public static Drawable createFromResourceStream(@Nullable Resources res,
            @Nullable TypedValue value, @Nullable InputStream is, @Nullable String srcName,
            @Nullable BitmapFactory.Options opts) {
        if (is == null) {
            return null;
        }
@@ -1199,7 +1201,6 @@ public abstract class Drawable {
        // an application in compatibility mode, without scaling those down
        // to the compatibility density only to have them scaled back up when
        // drawn to the screen.
        if (opts == null) opts = new BitmapFactory.Options();
        opts.inScreenDensity = Drawable.resolveDensity(res, 0);
        Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
        if (bm != null) {