< Summary

Class:DCL.Helpers.LazyTextureObserver
Assembly:LazyTextureObserver
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Helpers/LazyTextureObserver/LazyTextureObserver.cs
Covered lines:73
Uncovered lines:3
Coverable lines:76
Total lines:230
Line coverage:96% (73 of 76)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
LazyTextureObserver(...)0%110100%
LazyTextureObserver()0%110100%
AddListener(...)0%550100%
RemoveListener(...)0%330100%
RefreshWithUri(...)0%440100%
RefreshWithTexture(...)0%440100%
TryToLoadAndDispatch()0%220100%
DispatchTextureByInjectedReference()0%330100%
DispatchLoadedTexture()0%220100%
Dispose()0%110100%
CreateWithUri(...)0%2100%
CreateWithTexture(...)0%110100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Helpers/LazyTextureObserver/LazyTextureObserver.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Configuration;
 4using DCL;
 5using UnityEngine;
 6using UnityEngine.Assertions;
 7
 8namespace DCL.Helpers
 9{
 10    public interface ILazyTextureObserver
 11    {
 12        /// <summary>
 13        /// Adds a listener for this LazyTextureObserver.
 14        ///
 15        /// The listener will be called when anyone reloads the texture by calling any of the refresh methods.
 16        /// Also, calling this method will ensure the texture starts loading lazily if it was not loaded yet.
 17        /// </summary>
 18        /// <param name="listener"></param>
 19        public void AddListener(Action<Texture2D> listener);
 20
 21        /// <summary>
 22        /// Remove the given listener. If no listeners want this texture anymore, it will be unloaded.
 23        ///
 24        /// Note that as this method uses the AssetPromiseKeeper_Texture service,
 25        /// if any other systems use the same texture it will not be unloaded.
 26        /// </summary>
 27        /// <param name="listener"></param>
 28        public void RemoveListener(Action<Texture2D> listener);
 29
 30        /// <summary>
 31        /// Set the texture to be loaded from the given URI when any listener is subscribed.
 32        /// If we already have listeners, the loading will start immediately.
 33        ///
 34        /// The uri method has precedence over the texture method.
 35        /// </summary>
 36        /// <param name="uri"></param>
 37        void RefreshWithUri(string uri);
 38
 39        /// <summary>
 40        /// Sets the listened texture as the passed texture object.
 41        ///
 42        /// If we already have listeners, they will be called immediately with the new texture.
 43        /// New listeners will receive the new texture.
 44        ///
 45        /// The uri method has precedence over the texture method.
 46        /// </summary>
 47        /// <param name="texture"></param>
 48        void RefreshWithTexture(Texture2D texture);
 49    }
 50
 51    /// <summary>
 52    /// The LazyTextureObserver class is a reactive texture loader wrapper.
 53    /// <br/> <br/>
 54    /// This class is useful for cases where a texture needs to be used, but it has to be lazy loaded,
 55    /// and can be changed by an external party.
 56    /// <br/> <br/>
 57    /// <ul>
 58    /// <li>Calling <b>RefreshWithUri</b> and <b>RefreshWithTexture</b> will only load the texture if someone is registe
 59    /// <li>If <b>AddListener</b> is called and the texture is not loaded, it will start being loaded.</li><br/>
 60    /// <li>If <b>RemoveListener</b> is called and no more listeners need this image, the image will be unloaded.</li><b
 61    /// </ul>
 62    /// All existing listeners will be keep informed of <b>RefreshWithX</b> calls to update the texture if it changes.
 63    /// </summary>
 64    public class LazyTextureObserver : ILazyTextureObserver
 65    {
 66        enum State
 67        {
 68            NONE,
 69            IN_PROGRESS,
 70            LOADED
 71        }
 72
 73        private State state = State.NONE;
 74        private Action<Texture2D> OnLoaded;
 75
 59376        private HashSet<Action<Texture2D>> subscriptions = new HashSet<Action<Texture2D>>();
 77
 78        private Texture2D texture;
 79        private string uri;
 80        private ITextureLoader textureLoader;
 81
 116682        public LazyTextureObserver () : this(new TextureLoader()) { }
 83
 59384        internal LazyTextureObserver (ITextureLoader textureLoader)
 85        {
 59386            this.textureLoader = textureLoader;
 87
 59388            textureLoader.OnSuccess += x =>
 89            {
 790                state = State.LOADED;
 791                OnLoaded?.Invoke(x);
 792            };
 93
 59394            textureLoader.OnFail += () =>
 95            {
 196                state = State.NONE;
 197                uri = null;
 198            };
 59399        }
 100
 101        public void AddListener(Action<Texture2D> listener)
 102        {
 157103            Assert.IsNotNull(listener, "Listener can't be null!");
 104
 156105            if (!subscriptions.Contains(listener))
 106            {
 146107                subscriptions.Add(listener);
 146108                this.OnLoaded += listener;
 109            }
 110
 111            // First, check if we did set a texture directly and return it if so.
 156112            if ( texture != null )
 113            {
 4114                listener.Invoke(texture);
 4115                return;
 116            }
 117
 118            // If not, we try to load the texture if not already loaded.
 152119            if ( state == State.NONE )
 120            {
 144121                TryToLoadAndDispatch();
 144122                return;
 123            }
 124
 125            // From now on we assume that the promise exists.
 126            // --
 127            // It must be in progress or available by now. If its available we just return it for this listener only.
 128            // If not, the texture promise flow will call all the listeners when it finishes.
 8129            if ( state == State.LOADED )
 130            {
 4131                listener.Invoke(textureLoader.GetTexture());
 132            }
 8133        }
 134
 135        public void RemoveListener(Action<Texture2D> listener)
 136        {
 211137            Assert.IsNotNull(listener, "Listener can't be null!");
 138
 139            // Not using assert here because this case is more probable, I want to fail silently in this case.
 210140            if (!subscriptions.Contains(listener))
 84141                return;
 142
 126143            this.OnLoaded -= listener;
 126144            subscriptions.Remove(listener);
 145
 126146            if ( subscriptions.Count == 0 )
 147            {
 118148                textureLoader.Unload();
 118149                state = State.NONE;
 150            }
 126151        }
 152
 153        public void RefreshWithUri(string uri)
 154        {
 16155            if (string.IsNullOrEmpty(uri) || uri == this.uri)
 2156                return;
 157
 14158            this.uri = uri;
 14159            this.texture = null;
 160
 14161            if ( subscriptions.Count > 0 )
 4162                TryToLoadAndDispatch();
 14163        }
 164
 165        public void RefreshWithTexture(Texture2D texture)
 166        {
 8167            if (texture == null || texture == this.texture)
 1168                return;
 169
 7170            textureLoader.Unload();
 7171            state = State.NONE;
 172
 7173            this.uri = null;
 7174            this.texture = texture;
 175
 7176            if ( subscriptions.Count > 0 )
 2177                TryToLoadAndDispatch();
 7178        }
 179
 180        private void TryToLoadAndDispatch()
 181        {
 150182            if (DispatchTextureByInjectedReference())
 2183                return;
 184
 148185            DispatchLoadedTexture();
 148186        }
 187
 188        private bool DispatchTextureByInjectedReference()
 189        {
 150190            if (texture == null)
 148191                return false;
 192
 2193            state = State.LOADED;
 2194            OnLoaded?.Invoke(texture);
 2195            return true;
 196        }
 197
 198        private bool DispatchLoadedTexture()
 199        {
 148200            if (string.IsNullOrEmpty(uri))
 138201                return false;
 202
 10203            state = State.IN_PROGRESS;
 10204            textureLoader.Load(uri);
 10205            return true;
 206        }
 207
 208        public void Dispose()
 209        {
 12210            state = State.NONE;
 12211            textureLoader.Unload();
 12212            subscriptions.Clear();
 12213            OnLoaded = null;
 12214        }
 215
 216        public static LazyTextureObserver CreateWithUri(string uri)
 217        {
 0218            var result = new LazyTextureObserver();
 0219            result.RefreshWithUri(uri);
 0220            return result;
 221        }
 222
 223        public static LazyTextureObserver CreateWithTexture(Texture2D texture)
 224        {
 2225            var result = new LazyTextureObserver();
 2226            result.RefreshWithTexture(texture);
 2227            return result;
 228        }
 229    }
 230}