Enable configuring a global idling resource
diff --git a/.gitignore b/.gitignore
index 1df66c6..ff3303c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@
.idea/modules.xml
.idea/codeStyleSettings.xml
.idea/compiler.xml
+.idea/androidTestResultsUserPreferences.xml
# Gradle
.idea/**/gradle.xml
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java b/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java
index a5d9da3..577599f 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java
@@ -33,10 +33,15 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -58,6 +63,7 @@
* parse tasks prior to the cache getting populated.
*/
private static final Map<String, LottieTask<LottieComposition>> taskCache = new HashMap<>();
+ private static final Set<LottieTaskIdleListener> taskIdleListeners = new HashSet<>();
/**
* reference magic bytes for zip compressed files.
@@ -84,6 +90,21 @@
}
/**
+ * Use this to register a callback for when the composition factory is idle or not.
+ * This can be used to provide data to an espresso idling resource.
+ * Refer to FragmentVisibilityTests and its LottieIdlingResource in the Lottie repo for
+ * an example.
+ */
+ public static void registerLottieTaskIdleListener(LottieTaskIdleListener listener) {
+ taskIdleListeners.add(listener);
+ listener.onIdleChanged(taskCache.size() == 0);
+ }
+
+ public static void unregisterLottieTaskIdleListener(LottieTaskIdleListener listener) {
+ taskIdleListeners.remove(listener);
+ }
+
+ /**
* Fetch an animation from an http url. Once it is downloaded once, Lottie will cache the file to disk for
* future use. Because of this, you may call `fromUrl` ahead of time to warm the cache if you think you
* might need an animation in the future.
@@ -596,10 +617,16 @@
task.addListener(result -> {
taskCache.remove(cacheKey);
resultAlreadyCalled.set(true);
+ if (taskCache.size() == 0) {
+ notifyTaskCacheIdleListeners(true);
+ }
});
task.addFailureListener(result -> {
taskCache.remove(cacheKey);
resultAlreadyCalled.set(true);
+ if (taskCache.size() == 0) {
+ notifyTaskCacheIdleListeners(true);
+ }
});
// It is technically possible for the task to finish and for the listeners to get called
// before this code runs. If this happens, the task will be put in taskCache but never removed.
@@ -607,8 +634,18 @@
// for long enough for the task to finish and call the listeners. Unlikely but not impossible.
if (!resultAlreadyCalled.get()) {
taskCache.put(cacheKey, task);
+ if (taskCache.size() == 1) {
+ notifyTaskCacheIdleListeners(false);
+ }
}
}
return task;
}
+
+ private static void notifyTaskCacheIdleListeners(boolean idle) {
+ List<LottieTaskIdleListener> listeners = new ArrayList<>(taskIdleListeners);
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).onIdleChanged(idle);
+ }
+ }
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieTaskIdleListener.java b/lottie/src/main/java/com/airbnb/lottie/LottieTaskIdleListener.java
new file mode 100644
index 0000000..819ded0
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieTaskIdleListener.java
@@ -0,0 +1,5 @@
+package com.airbnb.lottie;
+
+public interface LottieTaskIdleListener {
+ void onIdleChanged(boolean idle);
+}
diff --git a/sample/src/androidTest/java/com/airbnb/lottie/samples/FragmentVisibilityTests.kt b/sample/src/androidTest/java/com/airbnb/lottie/samples/FragmentVisibilityTests.kt
index e4b586f..ca15b50 100644
--- a/sample/src/androidTest/java/com/airbnb/lottie/samples/FragmentVisibilityTests.kt
+++ b/sample/src/androidTest/java/com/airbnb/lottie/samples/FragmentVisibilityTests.kt
@@ -9,6 +9,7 @@
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
+import androidx.annotation.IdRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
@@ -29,6 +30,7 @@
import com.airbnb.lottie.LottieDrawable
import com.airbnb.lottie.model.LottieCompositionCache
import com.nhaarman.mockitokotlin2.mock
+import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -40,9 +42,18 @@
@LargeTest
class FragmentVisibilityTests {
+ lateinit var idlingResource: LottieIdlingResource
+
@Before
fun setup() {
LottieCompositionCache.getInstance().clear()
+ idlingResource = LottieIdlingResource()
+ IdlingRegistry.getInstance().register(idlingResource)
+ }
+
+ @After
+ fun teardown() {
+ IdlingRegistry.getInstance().unregister(idlingResource)
}
@Test
@@ -60,7 +71,6 @@
return object : RecyclerView.ViewHolder(LottieAnimationView(parent.context).apply {
id = R.id.animation_view
setAnimation(R.raw.heart)
- IdlingRegistry.getInstance().register(LottieIdlingResource(this))
}) {}
}
@@ -96,10 +106,6 @@
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.auto_play, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
launchFragmentInContainer<TestFragment>()
onView(withId(R.id.animation_view)).check(matches(isAnimating()))
@@ -118,7 +124,6 @@
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val animationView = requireView().findViewById<LottieAnimationView>(R.id.animation_view)
animationView.pauseAnimation()
- IdlingRegistry.getInstance().register(LottieIdlingResource(animationView))
}
}
launchFragmentInContainer<TestFragment>()
@@ -131,10 +136,6 @@
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.auto_play_gone, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
val scenario = launchFragmentInContainer<TestFragment>()
@@ -153,7 +154,6 @@
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
AlertDialog.Builder(requireContext()).setTitle("This is a dialog").show()
}
}
@@ -181,7 +181,6 @@
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
animationView = view.findViewById(R.id.animation_view)
animationView.addAnimatorListener(animationListener)
- IdlingRegistry.getInstance().register(LottieIdlingResource(animationView))
}
}
@@ -226,7 +225,6 @@
repeatMode = LottieDrawable.RESTART
setAnimation(R.raw.heart)
playAnimation()
- IdlingRegistry.getInstance().register(LottieIdlingResource(this))
}
}
}
@@ -244,10 +242,6 @@
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.no_auto_play, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
launchFragmentInContainer<TestFragment>()
onView(withId(R.id.animation_view)).check(matches(isNotAnimating()))
@@ -259,10 +253,6 @@
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.auto_play, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
val scenario = launchFragmentInContainer<TestFragment>()
@@ -287,7 +277,6 @@
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val animationView = view.findViewById<LottieAnimationView>(R.id.animation_view)
animationView.playAnimation()
- IdlingRegistry.getInstance().register(LottieIdlingResource(animationView))
}
}
launchFragmentInContainer<TestFragment>()
@@ -304,7 +293,6 @@
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val animationView = view.findViewById<LottieAnimationView>(R.id.animation_view)
animationView.playAnimation()
- IdlingRegistry.getInstance().register(LottieIdlingResource(animationView))
}
}
@@ -326,7 +314,6 @@
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<View>(R.id.container).isVisible = false
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
}
}
@@ -373,7 +360,6 @@
setAnimation(R.raw.heart)
playAnimation()
animationWasPlayed = true
- IdlingRegistry.getInstance().register(LottieIdlingResource(this, name = "Lottie ${Random.nextFloat()}"))
}
}
}
@@ -435,7 +421,6 @@
setAnimation(R.raw.heart)
playAnimation()
animationWasPlayed = true
- IdlingRegistry.getInstance().register(LottieIdlingResource(this, name = "Lottie ${Random.nextFloat()}"))
}
}
}
@@ -469,10 +454,6 @@
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.auto_play, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
val scenario = launchFragmentInContainer<TestFragment>()
diff --git a/sample/src/androidTest/java/com/airbnb/lottie/samples/LottieIdlingResource.kt b/sample/src/androidTest/java/com/airbnb/lottie/samples/LottieIdlingResource.kt
index 42bf31e..0fdf100 100644
--- a/sample/src/androidTest/java/com/airbnb/lottie/samples/LottieIdlingResource.kt
+++ b/sample/src/androidTest/java/com/airbnb/lottie/samples/LottieIdlingResource.kt
@@ -1,22 +1,22 @@
package com.airbnb.lottie.samples
-import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.IdlingResource
-import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieCompositionFactory
-class LottieIdlingResource(animationView: LottieAnimationView? = null, private val name: String = "Lottie") : IdlingResource {
-
- init {
- animationView?.addLottieOnCompositionLoadedListener {
- isIdle = true
- callback?.onTransitionToIdle()
- IdlingRegistry.getInstance().unregister(this)
- }
- }
+class LottieIdlingResource(private val name: String = "Lottie") : IdlingResource {
private var callback: IdlingResource.ResourceCallback? = null
private var isIdle = false
+ init {
+ LottieCompositionFactory.registerLottieTaskIdleListener { idle ->
+ isIdle = idle
+ if (idle) {
+ callback?.onTransitionToIdle()
+ }
+ }
+ }
+
override fun getName() = name
override fun isIdleNow() = isIdle