Skip to content

Commit

Permalink
Refactored LottieCompositionFactory APIs (#830)
Browse files Browse the repository at this point in the history
Replaced existing `LottieComposition.Factory` methods with a new `LottieCompositionFactory`
The main purpose of this is to provide a clean way to handle exceptions. Today, parsing exceptions are thrown on a background thread and impossible to handle. This replaces that with a new `LottieResult` which mimics a typical `Result` class in functional programming. It either has a result or an exception.

The composition methods now return a `LottieTask<LottieResult<LottieComposition>>>`.
You can add/remove listeners on the LottieTask. The listener will be called with the result that contains either the exception or error.

This also deprecates all `LottieComposition.Factory` methods because they all have an analogous `LottieCompositionFactory` API.

This PR also extracts LottieAnimationView's cache into a separate class and added tests which found an off by one error.

#710
  • Loading branch information
gpeal authored Jul 16, 2018
1 parent 4ea04fd commit c32f18e
Show file tree
Hide file tree
Showing 20 changed files with 934 additions and 244 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# 2.6.0
### Features and Improvements
* Deprecated `LottieComposition.Factory` in favor of LottieCompositionFactory.
* The new factory methods make it easier to catch exceptions by separating out success and
failure handlers. Previously, catching exceptions was impossible and would crash your app.
* [Sample App] Added the ability to load a file from assets.

# 2.5.7
Expand All @@ -12,15 +15,13 @@
* Fixed a potential dangling Choreographer callback ([#775](https://githubcom/airbnb/lottie-android/pull/775))

# 2.5.5
# Bugs Fixed
* Fixed end times for layers/animations. Before, if the layer/animation out frame was 20, it would fully render frame 20. This is incorrect. The last rendered frame should be 19.999... in this case. This should make Lottie reflect After Effects more accurately. However, if you are getting the frame in onAnimationEnd or onAnimationRepeat, it will be one less than it used to be.
* Added support for base64 encoded images directly in the json instead of the filename. They are 33% larger than their equivalent image file but enables you to have images with a single file.
* Fixed a lint error about KeyPath visibility.
* A few min/max progress bug fixes.
* Prevent autoPlay from starting before the animation was attached to the window. This caused animations in a RecyclerView to start playing before they were on screen.

# 2.5.4
# Bugs Fixed
* You can now call playAnimation() from onAnimationEnd
* Min/Max frames are clipped to the composition start/end
* setProgress takes into account start and end frame
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import android.view.inputmethod.InputConnection
import android.widget.FrameLayout
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
import java.util.*

Expand All @@ -25,20 +26,17 @@ class LottieFontViewGroup @JvmOverloads constructor(

init {
isFocusableInTouchMode = true
LottieComposition.Factory.fromAssetFileName(context, "Mobilo/BlinkingCursor.json"
) { composition ->
if (composition == null) {
return@fromAssetFileName
}
cursorView.layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
cursorView.setComposition(composition)
cursorView.repeatCount = LottieDrawable.INFINITE
cursorView.playAnimation()
addView(cursorView)
}
LottieCompositionFactory.fromAsset(context, "Mobilo/BlinkingCursor.json")
.addListener {
cursorView.layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
cursorView.setComposition(it)
cursorView.repeatCount = LottieDrawable.INFINITE
cursorView.playAnimation()
addView(cursorView)
}
}

private fun addSpace() {
Expand Down Expand Up @@ -154,14 +152,11 @@ class LottieFontViewGroup @JvmOverloads constructor(
if (compositionMap.containsKey(fileName)) {
addComposition(compositionMap[fileName]!!)
} else {
LottieComposition.Factory.fromAssetFileName(context, fileName
) { composition ->
if (composition == null) {
return@fromAssetFileName
}
compositionMap.put(fileName, composition)
addComposition(composition)
}
LottieCompositionFactory.fromAsset(context, fileName)
.addListener {
compositionMap.put(fileName, it)
addComposition(it)
}
}

return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.OnLifecycleEvent
import android.util.Log
import com.airbnb.lottie.L
import com.airbnb.lottie.samples.model.AnimationData
import com.airbnb.lottie.samples.model.AnimationResponse
import io.reactivex.Observable
Expand Down Expand Up @@ -59,7 +60,7 @@ class LottiefilesViewModel(application: Application) : AndroidViewModel(applicat
responses.add(it)
animationDataList.value = flatten(animationDataList.value, it.data)
}, {
Log.d("Gabe", "e#\t", it);
Log.d(L.TAG, "e#\t", it);
}, {
loading.value = false
}))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import android.support.transition.TransitionManager
import android.support.v4.app.Fragment
import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.*
import android.widget.EditText
import androidx.view.children
Expand Down Expand Up @@ -146,7 +147,8 @@ class PlayerFragment : Fragment() {
onCompositionLoaded(it)
})
viewModel.error.observe(this, Observer {
Snackbar.make(coordinatorLayout, R.string.composition_load_error, Snackbar.LENGTH_LONG)
Snackbar.make(coordinatorLayout, R.string.composition_load_error, Snackbar.LENGTH_LONG).show()
Log.w(L.TAG, "Error loading composition.", viewModel.error.value);
})
viewModel.fetchAnimation(args)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import android.net.Uri
import android.os.Handler
import android.os.Looper
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.samples.model.CompositionArgs
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
Expand Down Expand Up @@ -87,17 +88,13 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
}

private fun handleJsonResponse(jsonString: String) {
try {
LottieComposition.Factory.fromJsonString(jsonString, {
if (it == null) {
error.value = IllegalArgumentException("Unable to parse composition")
} else {
composition.value = CompositionData(it)
LottieCompositionFactory.fromJsonString(jsonString)
.addListener {
this.composition.value = CompositionData(it)
}
.addFailureListener {
this.error.value = it
}
})
} catch (e: RuntimeException) {
error.value = e
}
}

@SuppressLint("CheckResult")
Expand All @@ -114,7 +111,8 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
if (zipEntry.name.contains("__MACOSX")) {
zis.closeEntry()
} else if (zipEntry.name.contains(".json")) {
val composition = LottieComposition.Factory.fromInputStreamSync(zis, false)
val result = LottieCompositionFactory.fromJsonInputStreamSync(zis, false)
val composition = result.value
if (composition == null) {
throw IllegalArgumentException("Unable to parse composition")
} else {
Expand Down Expand Up @@ -160,22 +158,22 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
return
}

LottieComposition.Factory.fromInputStream(fis) {
if (it == null) {
error.value = IllegalArgumentException("Er")
} else {
composition.value = CompositionData(it)
}
}
LottieCompositionFactory.fromJsonInputStream(fis)
.addListener {
this.composition.value = CompositionData(it)
}
.addFailureListener {
this.error.value = it
}
}

private fun fetchAnimationByAsset(asset: String) {
LottieComposition.Factory.fromAssetFileName(getApplication(), asset) {
if (it == null) {
error.value = IllegalArgumentException("Error loading asset " + asset)
} else {
composition.value = CompositionData(it)
}
}
LottieCompositionFactory.fromAsset(getApplication(), asset)
.addListener {
composition.value = CompositionData(it)
}
.addFailureListener {
error.value = it
}
}
}
Loading

0 comments on commit c32f18e

Please sign in to comment.