public class

ResourceResolvers

extends java.lang.Object

 java.lang.Object

↳androidx.wear.protolayout.renderer.inflater.ResourceResolvers

Gradle dependencies

compile group: 'androidx.wear.protolayout', name: 'protolayout-renderer', version: '1.2.0'

  • groupId: androidx.wear.protolayout
  • artifactId: protolayout-renderer
  • version: 1.2.0

Artifact androidx.wear.protolayout:protolayout-renderer:1.2.0 it located at Google repository (https://maven.google.com/)

Overview

Class for resolving resources. Delegates the actual work to different types of resolver classes, and allows each type of resolver to be configured individually, as well as instantiation from common resolver implementations.

Summary

Methods
public static ResourceResolvers.Builderbuilder(ResourceProto.Resources protoResources)

Get an empty builder to build ResourceResolvers with.

public booleancanImageBeTinted(java.lang.String protoResourceId)

public TriggerProto.TriggergetAnimationTrigger(java.lang.String protoResourceId)

Get the animation trigger for the given animated image resource id

public DynamicProto.DynamicFloatgetBoundProgress(java.lang.String protoResourceId)

Get the animation bound progress for the given animated image resource id

public <any>getDrawable(java.lang.String protoResourceId)

Get the drawable corresponding to the given resource ID.

protected <any>getDrawableForImageResource(ResourceProto.ImageResource imageResource)

Get the drawable for the known ImageResource.

public DrawablegetPlaceholderDrawableOrThrow(java.lang.String protoResourceId)

Returns the placeholder drawable for the resource specified by protoResourceId.

protected java.lang.StringgetPlaceholderResourceId(java.lang.String originalResourceId)

public booleanhasPlaceholderDrawable(java.lang.String protoResourceId)

Returns whether the resource specified by protoResourceId has a placeholder resource associated with it.

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

Methods

public static ResourceResolvers.Builder builder(ResourceProto.Resources protoResources)

Get an empty builder to build ResourceResolvers with.

public boolean hasPlaceholderDrawable(java.lang.String protoResourceId)

Returns whether the resource specified by protoResourceId has a placeholder resource associated with it.

public Drawable getPlaceholderDrawableOrThrow(java.lang.String protoResourceId)

Returns the placeholder drawable for the resource specified by protoResourceId.

See also: ResourceResolvers.hasPlaceholderDrawable(String)

public <any> getDrawable(java.lang.String protoResourceId)

Get the drawable corresponding to the given resource ID.

public TriggerProto.Trigger getAnimationTrigger(java.lang.String protoResourceId)

Get the animation trigger for the given animated image resource id

public DynamicProto.DynamicFloat getBoundProgress(java.lang.String protoResourceId)

Get the animation bound progress for the given animated image resource id

protected <any> getDrawableForImageResource(ResourceProto.ImageResource imageResource)

Get the drawable for the known ImageResource. Can return null if there's no resolver for the image resource.

public boolean canImageBeTinted(java.lang.String protoResourceId)

protected java.lang.String getPlaceholderResourceId(java.lang.String originalResourceId)

Source

/*
 * 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.wear.protolayout.renderer.inflater;

import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.wear.protolayout.expression.proto.DynamicProto.DynamicFloat;
import androidx.wear.protolayout.proto.ResourceProto;
import androidx.wear.protolayout.proto.ResourceProto.AndroidAnimatedImageResourceByResId;
import androidx.wear.protolayout.proto.ResourceProto.AndroidImageResourceByContentUri;
import androidx.wear.protolayout.proto.ResourceProto.AndroidImageResourceByResId;
import androidx.wear.protolayout.proto.ResourceProto.AndroidSeekableAnimatedImageResourceByResId;
import androidx.wear.protolayout.proto.ResourceProto.InlineImageResource;
import androidx.wear.protolayout.proto.TriggerProto.Trigger;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

/**
 * Class for resolving resources. Delegates the actual work to different types of resolver classes,
 * and allows each type of resolver to be configured individually, as well as instantiation from
 * common resolver implementations.
 */
public class ResourceResolvers {
    @NonNull private final ResourceProto.Resources mProtoResources;

    @Nullable
    private final AndroidImageResourceByResIdResolver mAndroidImageResourceByResIdResolver;

    @Nullable
    private final AndroidAnimatedImageResourceByResIdResolver
            mAndroidAnimatedImageResourceByResIdResolver;

    @Nullable
    private final AndroidSeekableAnimatedImageResourceByResIdResolver
            mAndroidSeekableAnimatedImageResourceByResIdResolver;

    @Nullable private final InlineImageResourceResolver mInlineImageResourceResolver;

    @Nullable
    private final AndroidImageResourceByContentUriResolver
            mAndroidImageResourceByContentUriResolver;

    ResourceResolvers(
            @NonNull ResourceProto.Resources protoResources,
            @Nullable AndroidImageResourceByResIdResolver androidImageResourceByResIdResolver,
            @Nullable
                    AndroidAnimatedImageResourceByResIdResolver
                            androidAnimatedImageResourceByResIdResolver,
            @Nullable
                    AndroidSeekableAnimatedImageResourceByResIdResolver
                            androidSeekableAnimatedImageResourceByResIdResolver,
            @Nullable InlineImageResourceResolver inlineImageResourceResolver,
            @Nullable AndroidImageResourceByContentUriResolver androidContentUriResolver) {
        this.mProtoResources = protoResources;
        this.mAndroidImageResourceByResIdResolver = androidImageResourceByResIdResolver;
        this.mAndroidAnimatedImageResourceByResIdResolver =
                androidAnimatedImageResourceByResIdResolver;
        this.mAndroidSeekableAnimatedImageResourceByResIdResolver =
                androidSeekableAnimatedImageResourceByResIdResolver;
        this.mInlineImageResourceResolver = inlineImageResourceResolver;
        this.mAndroidImageResourceByContentUriResolver = androidContentUriResolver;
    }

    /** Exception thrown when accessing resources. */
    public static final class ResourceAccessException extends Exception {
        public ResourceAccessException(@NonNull String description) {
            super(description);
        }

        public ResourceAccessException(@NonNull String description, @NonNull Exception cause) {
            super(description, cause);
        }
    }

    /** Interface that can provide a Drawable for an AndroidImageResourceByResId */
    public interface AndroidImageResourceByResIdResolver {
        /**
         * Should immediately return the drawable specified by {@code resource}.
         *
         * @throws ResourceAccessException If the drawable cannot be found
         */
        @NonNull
        Drawable getDrawableOrThrow(@NonNull AndroidImageResourceByResId resource)
                throws ResourceAccessException;
    }

    /** Interface that can provide a Drawable for an AndroidAnimatedImageResourceByResId */
    public interface AndroidAnimatedImageResourceByResIdResolver {
        /**
         * Should immediately return the drawable specified by {@code resource}.
         *
         * @throws ResourceAccessException If the drawable cannot be found.
         */
        @NonNull
        Drawable getDrawableOrThrow(@NonNull AndroidAnimatedImageResourceByResId resource)
                throws ResourceAccessException;
    }

    /** Interface that can provide a Drawable for an AndroidSeekableAnimatedImageResourceByResId */
    public interface AndroidSeekableAnimatedImageResourceByResIdResolver {
        /**
         * Should immediately return the drawable specified by {@code resource}.
         *
         * @throws ResourceAccessException If the drawable cannot be found.
         */
        @NonNull
        Drawable getDrawableOrThrow(@NonNull AndroidSeekableAnimatedImageResourceByResId resource)
                throws ResourceAccessException;
    }

    /** Interface that can provide a Drawable for an InlineImageResource */
    public interface InlineImageResourceResolver {
        /**
         * Should immediately return the drawable specified by {@code resource}.
         *
         * @throws ResourceAccessException If the drawable cannot be found,.
         */
        @NonNull
        Drawable getDrawableOrThrow(@NonNull InlineImageResource resource)
                throws ResourceAccessException;
    }

    /** Interface that can provide a Drawable for an AndroidContentUriResource. */
    public interface AndroidImageResourceByContentUriResolver {
        /** Get the drawable as specified by {@code resource}, to be loaded asynchronously. */
        @NonNull
        ListenableFuture<Drawable> getDrawable(@NonNull AndroidImageResourceByContentUri resource);
    }

    /** Get an empty builder to build {@link ResourceResolvers} with. */
    @NonNull
    public static Builder builder(@NonNull ResourceProto.Resources protoResources) {
        return new Builder(protoResources);
    }

    /**
     * Returns whether the resource specified by {@code protoResourceId} has a placeholder resource
     * associated with it.
     */
    public boolean hasPlaceholderDrawable(@NonNull String protoResourceId) {
        return getPlaceholderResourceId(protoResourceId) != null;
    }

    /**
     * Returns the placeholder drawable for the resource specified by {@code protoResourceId}.
     *
     * @throws ResourceAccessException If the specified resource does not have a placeholder
     *     associated, or the placeholder could not be loaded.
     * @throws IllegalArgumentException If the specified resource, or its placeholder, does not
     *     exist.
     * @see ResourceResolvers#hasPlaceholderDrawable(String)
     */
    @NonNull
    public Drawable getPlaceholderDrawableOrThrow(@NonNull String protoResourceId)
            throws ResourceAccessException {
        String placeholderResourceId = getPlaceholderResourceId(protoResourceId);

        if (placeholderResourceId == null) {
            throw new ResourceAccessException(
                    "Resource " + protoResourceId + " does not have a placeholder resource.");
        }

        ResourceProto.ImageResource placeholderImageResource =
                mProtoResources.getIdToImageMap().get(placeholderResourceId);

        if (placeholderImageResource == null) {
            throw new IllegalArgumentException(
                    "Resource " + placeholderResourceId + " is not defined in resources bundle");
        }

        Drawable placeHolderDrawable =
                getDrawableForImageResourceSynchronously(placeholderImageResource);
        if (placeHolderDrawable != null) {
            return placeHolderDrawable;
        }

        if (placeholderImageResource.hasAndroidContentUri()) {
            throw new ResourceAccessException("Content URI images cannot be used as placeholders");
        }

        throw new ResourceAccessException("Can't find resolver for image resource.");
    }

    /** Get the drawable corresponding to the given resource ID. */
    @NonNull
    public ListenableFuture<Drawable> getDrawable(@NonNull String protoResourceId) {
        ResourceProto.ImageResource imageResource =
                mProtoResources.getIdToImageMap().get(protoResourceId);

        if (imageResource == null) {
            return Futures.immediateFailedFuture(
                    new IllegalArgumentException(
                            "Resource " + protoResourceId + " is not defined in resources bundle"));
        }

        @Nullable
        ListenableFuture<Drawable> drawableFutureOrNull =
                getDrawableForImageResource(imageResource);
        if (drawableFutureOrNull == null) {
            return Futures.immediateFailedFuture(
                    new ResourceAccessException(
                            "Can't find resolver for image resource " + protoResourceId));
        }
        return drawableFutureOrNull;
    }

    /**
     * Get the animation trigger for the given animated image resource id
     *
     * @throws IllegalArgumentException If the resource is not an animated resource.
     */
    @Nullable
    public Trigger getAnimationTrigger(@NonNull String protoResourceId) {
        ResourceProto.ImageResource imageResource =
                mProtoResources.getIdToImageMap().get(protoResourceId);
        if (imageResource != null && imageResource.hasAndroidAnimatedResourceByResId()) {
            return imageResource.getAndroidAnimatedResourceByResId().getStartTrigger();
        }
        throw new IllegalArgumentException(
                "Resource "
                        + protoResourceId
                        + " is not an animated resource, thus no animation trigger");
    }

    /**
     * Get the animation bound progress for the given animated image resource id
     *
     * @throws IllegalArgumentException If the resource is not a seekable animated resource.
     */
    @Nullable
    public DynamicFloat getBoundProgress(@NonNull String protoResourceId) {
        ResourceProto.ImageResource imageResource =
                mProtoResources.getIdToImageMap().get(protoResourceId);
        if (imageResource != null && imageResource.hasAndroidSeekableAnimatedResourceByResId()) {
            return imageResource.getAndroidSeekableAnimatedResourceByResId().getProgress();
        }
        throw new IllegalArgumentException(
                "Resource "
                        + protoResourceId
                        + " is not a seekable animated resource, thus no bound progress to a"
                        + " DynamicFloat");
    }

    @Nullable
    Drawable getDrawableForImageResourceSynchronously(
            @NonNull ResourceProto.ImageResource imageResource) throws ResourceAccessException {
        if (imageResource.hasAndroidAnimatedResourceByResId()
                && mAndroidAnimatedImageResourceByResIdResolver != null) {
            AndroidAnimatedImageResourceByResIdResolver resolver =
                    mAndroidAnimatedImageResourceByResIdResolver;
            return resolver.getDrawableOrThrow(imageResource.getAndroidAnimatedResourceByResId());
        }

        if (imageResource.hasAndroidSeekableAnimatedResourceByResId()
                && mAndroidSeekableAnimatedImageResourceByResIdResolver != null) {
            AndroidSeekableAnimatedImageResourceByResIdResolver resolver =
                    mAndroidSeekableAnimatedImageResourceByResIdResolver;
            return resolver.getDrawableOrThrow(
                    imageResource.getAndroidSeekableAnimatedResourceByResId());
        }

        if (imageResource.hasAndroidResourceByResId()
                && mAndroidImageResourceByResIdResolver != null) {
            AndroidImageResourceByResIdResolver resolver = mAndroidImageResourceByResIdResolver;
            return resolver.getDrawableOrThrow(imageResource.getAndroidResourceByResId());
        }

        if (imageResource.hasInlineResource() && mInlineImageResourceResolver != null) {
            InlineImageResourceResolver resolver = mInlineImageResourceResolver;
            return resolver.getDrawableOrThrow(imageResource.getInlineResource());
        }

        return null;
    }

    /**
     * Get the drawable for the known ImageResource. Can return null if there's no resolver for the
     * image resource.
     */
    @Nullable
    protected ListenableFuture<Drawable> getDrawableForImageResource(
            @NonNull ResourceProto.ImageResource imageResource) {
        try {
            Drawable drawable = getDrawableForImageResourceSynchronously(imageResource);
            if (drawable != null) {
                return Futures.immediateFuture(drawable);
            }
        } catch (ResourceAccessException e) {
            return Futures.immediateFailedFuture(e);
        }

        if (imageResource.hasAndroidContentUri()
                && mAndroidImageResourceByContentUriResolver != null) {
            AndroidImageResourceByContentUriResolver resolver =
                    mAndroidImageResourceByContentUriResolver;
            return resolver.getDrawable(imageResource.getAndroidContentUri());
        }

        // Can't find resolver for image resource.
        return null;
    }

    public boolean canImageBeTinted(@NonNull String protoResourceId) {
        // Only Android image resources can be tinted for now. This is because we don't really know
        // what is in an inline image.
        ResourceProto.ImageResource imageResource =
                mProtoResources.getIdToImageMap().get(protoResourceId);

        if (imageResource == null) {
            throw new IllegalArgumentException(
                    "Resource " + protoResourceId + " is not defined in resources bundle");
        }

        if (imageResource.hasAndroidResourceByResId()
                || imageResource.hasAndroidAnimatedResourceByResId()
                || imageResource.hasAndroidSeekableAnimatedResourceByResId()) {
            return true;
        }

        return false;
    }

    @Nullable
    protected String getPlaceholderResourceId(@NonNull String originalResourceId) {
        ResourceProto.ImageResource imageResource =
                mProtoResources.getIdToImageMap().get(originalResourceId);

        if (imageResource == null) {
            throw new IllegalArgumentException(
                    "Resource " + originalResourceId + " is not defined in resources bundle");
        }

        return null;
    }

    /** Builder for ResourceResolvers */
    public static final class Builder {
        @NonNull private final ResourceProto.Resources mProtoResources;
        @Nullable private AndroidImageResourceByResIdResolver mAndroidImageResourceByResIdResolver;

        @Nullable
        private AndroidAnimatedImageResourceByResIdResolver
                mAndroidAnimatedImageResourceByResIdResolver;

        @Nullable
        private AndroidSeekableAnimatedImageResourceByResIdResolver
                mAndroidSeekableAnimatedImageResourceByResIdResolver;

        @Nullable private InlineImageResourceResolver mInlineImageResourceResolver;

        @Nullable
        private AndroidImageResourceByContentUriResolver mAndroidImageResourceByContentUriResolver;

        Builder(@NonNull ResourceProto.Resources protoResources) {
            this.mProtoResources = protoResources;
        }

        /** Set the resource loader for {@link AndroidImageResourceByResIdResolver} resources. */
        @NonNull
        @SuppressLint("MissingGetterMatchingBuilder")
        public Builder setAndroidImageResourceByResIdResolver(
                @NonNull AndroidImageResourceByResIdResolver resolver) {
            mAndroidImageResourceByResIdResolver = resolver;
            return this;
        }

        /**
         * Set the resource loader for {@link AndroidAnimatedImageResourceByResIdResolver}
         * resources.
         */
        @NonNull
        @SuppressLint("MissingGetterMatchingBuilder")
        public Builder setAndroidAnimatedImageResourceByResIdResolver(
                @NonNull AndroidAnimatedImageResourceByResIdResolver resolver) {
            mAndroidAnimatedImageResourceByResIdResolver = resolver;
            return this;
        }

        /**
         * Set the resource loader for {@link AndroidSeekableAnimatedImageResourceByResIdResolver}
         * resources.
         */
        @NonNull
        @SuppressLint("MissingGetterMatchingBuilder")
        public Builder setAndroidSeekableAnimatedImageResourceByResIdResolver(
                @NonNull AndroidSeekableAnimatedImageResourceByResIdResolver resolver) {
            mAndroidSeekableAnimatedImageResourceByResIdResolver = resolver;
            return this;
        }

        /** Set the resource loader for {@link InlineImageResourceResolver} resources. */
        @NonNull
        @SuppressLint("MissingGetterMatchingBuilder")
        public Builder setInlineImageResourceResolver(
                @NonNull InlineImageResourceResolver resolver) {
            mInlineImageResourceResolver = resolver;
            return this;
        }

        @NonNull
        @SuppressLint("MissingGetterMatchingBuilder")
        public Builder setAndroidImageResourceByContentUriResolver(
                @NonNull AndroidImageResourceByContentUriResolver resolver) {
            mAndroidImageResourceByContentUriResolver = resolver;
            return this;
        }

        /** Build a {@link ResourceResolvers} instance. */
        @NonNull
        public ResourceResolvers build() {
            return new ResourceResolvers(
                    mProtoResources,
                    mAndroidImageResourceByResIdResolver,
                    mAndroidAnimatedImageResourceByResIdResolver,
                    mAndroidSeekableAnimatedImageResourceByResIdResolver,
                    mInlineImageResourceResolver,
                    mAndroidImageResourceByContentUriResolver);
        }
    }
}