Updated PlayerFragment to MvRx
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
index 0a0a65d..de1c360 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
@@ -4,8 +4,6 @@
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.arch.lifecycle.Lifecycle
-import android.arch.lifecycle.Observer
-import android.arch.lifecycle.ViewModelProviders
import android.graphics.Color
import android.os.Bundle
import android.support.design.widget.BottomSheetBehavior
@@ -29,7 +27,9 @@
import com.airbnb.lottie.samples.views.BottomSheetItemView
import com.airbnb.lottie.samples.views.BottomSheetItemViewModel_
import com.airbnb.lottie.samples.views.ControlBarItemToggleView
-import com.airbnb.lottie.utils.MiscUtils
+import com.airbnb.mvrx.BaseMvRxFragment
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
import com.github.mikephil.charting.components.LimitLine
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.Entry
@@ -47,31 +47,10 @@
import kotlinx.android.synthetic.main.fragment_player.*
import kotlin.math.min
import kotlin.math.roundToInt
-import kotlin.properties.ObservableProperty
-import kotlin.reflect.KProperty
-private class UiState(private val callback: () -> Unit) {
-
- private inner class BooleanProperty(initialValue: Boolean) : ObservableProperty<Boolean>(initialValue) {
- override fun afterChange(property: KProperty<*>, oldValue: Boolean, newValue: Boolean) {
- callback()
- }
- }
-
- var controls by BooleanProperty(true)
- var controlBar by BooleanProperty(true)
- var renderGraph by BooleanProperty(false)
- var border by BooleanProperty(false)
- var backgroundColor by BooleanProperty(false)
- var scale by BooleanProperty(false)
- var speed by BooleanProperty(false)
- var trim by BooleanProperty(false)
-}
-
-class PlayerFragment : Fragment() {
+class PlayerFragment : BaseMvRxFragment() {
private val transition = AutoTransition().apply { duration = 175 }
- private val uiState = UiState { updateUiFromState() }
private val renderTimesBehavior by lazy {
BottomSheetBehavior.from(renderTimesBottomSheet).apply {
peekHeight = resources.getDimensionPixelSize(R.dimen.bottom_bar_peek_height)
@@ -98,10 +77,6 @@
color = Color.BLACK
}
}
- private var composition: LottieComposition? = null
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
- inflater.inflate(R.layout.fragment_player, container, false)
private val animatorListener = AnimatorListenerAdapter(
onStart = { playButton.isActivated = true },
@@ -109,20 +84,20 @@
playButton.isActivated = false
animationView.performanceTracker?.logRenderTimes()
updateRenderTimesPerLayer()
- updateWarnings()
},
onCancel = {
playButton.isActivated = false
- updateWarnings()
},
onRepeat = {
animationView.performanceTracker?.logRenderTimes()
updateRenderTimesPerLayer()
- updateWarnings()
}
)
- private val viewModel by lazy { ViewModelProviders.of(this).get(PlayerViewModel::class.java) }
+ private val viewModel: PlayerViewModel by fragmentViewModel()
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
+ inflater.inflate(R.layout.fragment_player, container, false)
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -135,45 +110,150 @@
lottieVersionView.text = getString(R.string.lottie_version, com.airbnb.lottie.BuildConfig.VERSION_NAME)
- val args = arguments?.getParcelable<CompositionArgs>(EXTRA_ANIMATION_ARGS) ?: throw IllegalArgumentException("No composition args specified")
+ val args = arguments?.getParcelable<CompositionArgs>(EXTRA_ANIMATION_ARGS)
+ ?: throw IllegalArgumentException("No composition args specified")
args.animationData?.bgColorInt()?.let {
backgroundButton1.setBackgroundColor(it)
animationContainer.setBackgroundColor(it)
invertColor(it)
}
- viewModel.composition.observe(this, Observer {
+ minFrameView.setOnClickListener {
+ val minFrameView = EditText(context)
+ minFrameView.setText(animationView.minFrame.toInt().toString())
+ AlertDialog.Builder(context)
+ .setTitle(R.string.min_frame_dialog)
+ .setView(minFrameView)
+ .setPositiveButton("Load") { _, _ ->
+ viewModel.setMinFrame(minFrameView.text.toString().toIntOrNull() ?: 0)
+ }
+ .setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
+ .show()
+ }
+
+ maxFrameView.setOnClickListener {
+ val maxFrameView = EditText(context)
+ maxFrameView.setText(animationView.maxFrame.toInt().toString())
+ AlertDialog.Builder(context)
+ .setTitle(R.string.max_frame_dialog)
+ .setView(maxFrameView)
+ .setPositiveButton("Load") { _, _ ->
+ viewModel.setMaxFrame(maxFrameView.text.toString().toIntOrNull() ?: 0)
+ }
+ .setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
+ .show()
+ }
+
+ viewModel.selectSubscribe(PlayerState::minFrame, PlayerState::maxFrame) { minFrame, maxFrame ->
+ animationView.setMinAndMaxFrame(minFrame, maxFrame)
+ // I think this is a lint bug. It complains about int being <ErrorType>
+ //noinspection StringFormatMatches
+ minFrameView.setText(resources.getString(R.string.min_frame, animationView.minFrame.toInt()))
+ //noinspection StringFormatMatches
+ maxFrameView.setText(resources.getString(R.string.max_frame, animationView.maxFrame.toInt()))
+ }
+
+ viewModel.fetchAnimation(args)
+ viewModel.asyncSubscribe(PlayerState::composition, onFail = {
+ Snackbar.make(coordinatorLayout, R.string.composition_load_error, Snackbar.LENGTH_LONG).show()
+ Log.w(L.TAG, "Error loading composition.", it);
+ }) {
loadingView.isVisible = false
onCompositionLoaded(it)
- })
- viewModel.error.observe(this, Observer {
- 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)
-
- borderToggle.setOnClickListener { uiState.border++ }
- backgroundColorToggle.setOnClickListener { uiState.backgroundColor++ }
- scaleToggle.setOnClickListener { uiState.scale++ }
- speedToggle.setOnClickListener { uiState.speed++ }
- trimToggle.setOnClickListener { uiState.trim++ }
- renderGraphToggle.setOnClickListener { uiState.renderGraph++ }
-
- closeBackgroundColorButton.setOnClickListener { uiState.backgroundColor = false }
- closeScaleButton.setOnClickListener { uiState.scale = false }
- closeSpeedButton.setOnClickListener { uiState.speed = false }
- closeTrimButton.setOnClickListener { uiState.trim = false }
-
- hardwareAccelerationToggle.setOnClickListener {
- animationView.useHardwareAcceleration(!animationView.useHardwareAcceleration)
- updateUiFromState()
}
- mergePathsToggle.setOnClickListener {
- animationView.enableMergePathsForKitKatAndAbove(!animationView.isMergePathsEnabledForKitKatAndAbove)
- updateUiFromState()
+ borderToggle.setOnClickListener { viewModel.toggleBorderVisible() }
+ viewModel.selectSubscribe(PlayerState::borderVisible) {
+ borderToggle.isActivated = it
+ borderToggle.setImageResource(
+ if (it) R.drawable.ic_border_on
+ else R.drawable.ic_border_off
+ )
+ animationView.setBackgroundResource(if (it) R.drawable.outline else 0)
}
+ viewModel.selectSubscribe(PlayerState::controlsVisible) { controlsContainer.animateVisible(it) }
+
+ viewModel.selectSubscribe(PlayerState::controlBarVisible) { controlBar.animateVisible(it) }
+
+ renderGraphToggle.setOnClickListener { viewModel.toggleRenderGraphVisible() }
+ viewModel.selectSubscribe(PlayerState::renderGraphVisible) {
+ renderGraphToggle.isActivated = it
+ renderTimesGraphContainer.animateVisible(it)
+ renderTimesPerLayerButton.animateVisible(it)
+ lottieVersionView.animateVisible(!it)
+ }
+
+ backgroundColorToggle.setOnClickListener { viewModel.toggleBackgroundColorVisible() }
+ closeBackgroundColorButton.setOnClickListener { viewModel.setBackgroundColorVisible(false) }
+ viewModel.selectSubscribe(PlayerState::backgroundColorVisible) {
+ backgroundColorToggle.isActivated = it
+ backgroundColorContainer.animateVisible(it)
+ }
+
+ scaleToggle.setOnClickListener { viewModel.toggleScaleVisible() }
+ closeScaleButton.setOnClickListener { viewModel.setScaleVisible(false) }
+ viewModel.selectSubscribe(PlayerState::scaleVisible) {
+ scaleToggle.isActivated = it
+ scaleContainer.animateVisible(it)
+ }
+
+ trimToggle.setOnClickListener { viewModel.toggleTrimVisible() }
+ closeTrimButton.setOnClickListener { viewModel.setTrimVisible(false) }
+ viewModel.selectSubscribe(PlayerState::trimVisible) {
+ trimToggle.isActivated = it
+ trimContainer.animateVisible(it)
+ }
+
+ hardwareAccelerationToggle.setOnClickListener { viewModel.toggleHardwareAcceleration() }
+ viewModel.selectSubscribe(PlayerState::useHardwareAcceleration) {
+ hardwareAccelerationToggle.isActivated = it
+ animationView.useHardwareAcceleration(it)
+ }
+
+ mergePathsToggle.setOnClickListener { viewModel.toggleMergePaths() }
+ viewModel.selectSubscribe(PlayerState::useMergePaths) {
+ animationView.enableMergePathsForKitKatAndAbove(it)
+ mergePathsToggle.isActivated = it
+ }
+
+ speedToggle.setOnClickListener { viewModel.toggleSpeedVisible() }
+ closeSpeedButton.setOnClickListener { viewModel.setSpeedVisible(false) }
+ viewModel.selectSubscribe(PlayerState::speedVisible) {
+ speedToggle.isActivated = it
+ speedContainer.isVisible = it
+ }
+ viewModel.selectSubscribe(PlayerState::speed) {
+ animationView.speed = it
+ speedButtonsContainer
+ .children
+ .filterIsInstance<ControlBarItemToggleView>()
+ .forEach { toggleView ->
+ toggleView.isActivated = toggleView.getText().replace("x", "").toFloat() == animationView.speed
+ }
+ }
+ speedButtonsContainer
+ .children
+ .filterIsInstance(ControlBarItemToggleView::class.java)
+ .forEach { child ->
+ child.setOnClickListener {
+ val speed = (it as ControlBarItemToggleView)
+ .getText()
+ .replace("x", "")
+ .toFloat()
+ viewModel.setSpeed(speed)
+ }
+ }
+
+
+ loopButton.setOnClickListener { viewModel.toggleLoop() }
+ viewModel.selectSubscribe(PlayerState::repeatCount) {
+ animationView.repeatCount = it
+ loopButton.isActivated = animationView.repeatCount == ValueAnimator.INFINITE
+ }
+
+ playButton.isActivated = animationView.isAnimating
+
seekBar.setOnSeekBarChangeListener(OnSeekBarChangeListenerAdapter(
onProgressChanged = { _, progress, _ ->
if (seekBar.isPressed && progress in 1..4) {
@@ -185,8 +265,6 @@
}
))
- animationView.repeatCount = ValueAnimator.INFINITE
-
animationView.addAnimatorUpdateListener {
currentFrameView.text = updateFramesAndDurationLabel(animationView)
@@ -196,13 +274,7 @@
animationView.addAnimatorListener(animatorListener)
playButton.setOnClickListener {
if (animationView.isAnimating) animationView.pauseAnimation() else animationView.resumeAnimation()
- updateUiFromState()
- }
-
- loopButton.setOnClickListener {
- val repeatCount = if (animationView.repeatCount == ValueAnimator.INFINITE) 0 else ValueAnimator.INFINITE
- animationView.repeatCount = repeatCount
- updateUiFromState()
+ postInvalidate()
}
scaleSeekBar.setOnSeekBarChangeListener(OnSeekBarChangeListenerAdapter(
@@ -215,39 +287,6 @@
}
))
- minFrame.setOnClickListener {
- val minFrameView = EditText(context)
- minFrameView.setText(animationView.minFrame.toInt().toString())
- AlertDialog.Builder(context)
- .setTitle(R.string.min_frame_dialog)
- .setView(minFrameView)
- .setPositiveButton("Load") { _, _ ->
- var frame = minFrameView.text.toString().toFloatOrNull() ?: 0f
- frame = MiscUtils.clamp(frame, composition?.startFrame ?: frame, animationView.maxFrame)
-
- animationView.setMinFrame(frame.toInt())
- updateUiFromState()
- }
- .setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
- .show()
- }
-
- maxFrame.setOnClickListener {
- val maxFrameView = EditText(context)
- maxFrameView.setText(animationView.maxFrame.toInt().toString())
- AlertDialog.Builder(context)
- .setTitle(R.string.max_frame_dialog)
- .setView(maxFrameView)
- .setPositiveButton("Load") { _, _ ->
- var frame = maxFrameView.text.toString().toFloatOrNull() ?: 0f
- frame = MiscUtils.clamp(frame, animationView.minFrame, composition?.endFrame ?: frame)
- animationView.setMaxFrame(frame.toInt())
- updateUiFromState()
- }
- .setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
- .show()
- }
-
arrayOf<BackgroundColorView>(
backgroundButton1,
backgroundButton2,
@@ -286,18 +325,6 @@
axisLeft.addLimitLine(ll2)
}
- speedButtonsContainer.children.forEach {
- if (it is ControlBarItemToggleView) {
- it.setOnClickListener {
- animationView.speed = (it as ControlBarItemToggleView)
- .getText()
- .replace("x", "")
- .toFloat()
- updateUiFromState()
- }
- }
- }
-
renderTimesPerLayerButton.setOnClickListener {
updateRenderTimesPerLayer()
renderTimesBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
@@ -309,9 +336,11 @@
renderTimesBehavior.state = BottomSheetBehavior.STATE_HIDDEN
warningsButton.setOnClickListener {
- updateWarnings()
- if (composition?.warnings?.isEmpty() != true) {
- warningsBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+ withState(viewModel) { state ->
+ if (state.composition()?.warnings?.isEmpty() != true) {
+ warningsBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+
+ }
}
}
@@ -328,20 +357,11 @@
keyPathsBehavior.state = BottomSheetBehavior.STATE_HIDDEN
}
keyPathsBehavior.state = BottomSheetBehavior.STATE_HIDDEN
+ }
- keyPathsRecyclerView.buildModelsWith { controller ->
- composition?.let {
- animationView.resolveKeyPath(KeyPath("**")).forEachIndexed { index, keyPath ->
- BottomSheetItemViewModel_()
- .id(index)
- .text(keyPath.keysToString())
- .addTo(controller)
-
- }
- }
- }
-
- updateUiFromState()
+ private fun View.animateVisible(visible: Boolean) {
+ beginDelayedTransition()
+ isVisible = visible
}
private fun invertColor(color: Int) {
@@ -371,15 +391,7 @@
android.R.id.home -> requireActivity().finish()
R.id.info -> Unit
R.id.visibility -> {
- uiState.controls = !item.isChecked
- uiState.controlBar = !item.isChecked
- uiState.renderGraph = false
- uiState.border = false
- uiState.backgroundColor = false
- uiState.scale = false
- uiState.speed = false
- uiState.trim = false
- updateUiFromState()
+ viewModel.setDistractionFree(item.isChecked)
val menuIcon = if (item.isChecked) R.drawable.ic_eye_teal else R.drawable.ic_eye_selector
item.icon = ContextCompat.getDrawable(requireContext(), menuIcon)
}
@@ -389,10 +401,8 @@
private fun onCompositionLoaded(composition: LottieComposition?) {
composition ?: return
- this.composition = composition
animationView.setComposition(composition)
- animationView.setMinAndMaxFrame(composition.startFrame.toInt(), composition.endFrame.toInt())
animationView.setPerformanceTrackingEnabled(true)
var renderTimeGraphRange = 4f
animationView.performanceTracker?.addFrameListener { ms ->
@@ -403,61 +413,23 @@
renderTimesGraph.invalidate()
}
- // Force warning to update
- warningsContainer.removeAllViews()
- updateWarnings()
-
// Scale up to fill the screen
scaleSeekBar.progress = 100
- keyPathsRecyclerView.requestModelBuild()
- }
+ keyPathsRecyclerView.buildModelsWith { controller ->
+ animationView.resolveKeyPath(KeyPath("**")).forEachIndexed { index, keyPath ->
+ BottomSheetItemViewModel_()
+ .id(index)
+ .text(keyPath.keysToString())
+ .addTo(controller)
- private fun updateUiFromState() {
- beginDelayedTransition()
-
- controlsContainer.isVisible = uiState.controls
- controlBar.isVisible = uiState.controlBar
-
- renderGraphToggle.isActivated = uiState.renderGraph
- renderTimesGraphContainer.isVisible = uiState.renderGraph
- renderTimesPerLayerButton.isVisible = uiState.renderGraph
- lottieVersionView.isVisible = !uiState.renderGraph
-
- borderToggle.isActivated = uiState.border
- borderToggle.setImageResource(
- if (uiState.border) R.drawable.ic_border_on
- else R.drawable.ic_border_off
- )
- animationView.setBackgroundResource(if (borderToggle.isActivated) R.drawable.outline else 0)
-
- backgroundColorToggle.isActivated = uiState.backgroundColor
- backgroundColorContainer.isVisible = uiState.backgroundColor
-
- scaleToggle.isActivated = uiState.scale
- scaleContainer.isVisible = uiState.scale
-
- trimToggle.isActivated = uiState.trim
- trimContainer.isVisible = uiState.trim
- // I think this is a lint bug. It complains about int being <ErrorType>
- //noinspection StringFormatMatches
- minFrame.setText(resources.getString(R.string.min_frame, animationView.minFrame.toInt()))
- //noinspection StringFormatMatches
- maxFrame.setText(resources.getString(R.string.max_frame, animationView.maxFrame.toInt()))
-
- hardwareAccelerationToggle.isActivated = animationView.useHardwareAcceleration
- mergePathsToggle.isActivated = animationView.isMergePathsEnabledForKitKatAndAbove
-
- speedToggle.isActivated = uiState.speed
- speedContainer.isVisible = uiState.speed
- speedButtonsContainer.children.forEach {
- if (it is ControlBarItemToggleView) {
- it.isActivated = it.getText().replace("x", "").toFloat() == animationView.speed
}
}
- loopButton.isActivated = animationView.repeatCount == ValueAnimator.INFINITE
- playButton.isActivated = animationView.isAnimating
+ updateWarnings()
+ }
+
+ override fun invalidate() {
}
private fun updateRenderTimesPerLayer() {
@@ -473,9 +445,12 @@
}
}
- private fun updateWarnings() {
- val warnings = composition?.warnings ?: emptySet<String>()
- if (!warnings.isEmpty() && warnings.size == warningsContainer.childCount) return
+ private fun updateWarnings() = withState(viewModel) { state ->
+ // Force warning to update
+ warningsContainer.removeAllViews()
+
+ val warnings = state.composition()?.warnings ?: emptySet<String>()
+ if (!warnings.isEmpty() && warnings.size == warningsContainer.childCount) return@withState
warningsContainer.removeAllViews()
warnings.forEach {
@@ -495,12 +470,13 @@
private fun minScale() = 0.15f
- private fun maxScale(): Float {
+ private fun maxScale(): Float = withState(viewModel) { state ->
val screenWidth = resources.displayMetrics.widthPixels.toFloat()
val screenHeight = resources.displayMetrics.heightPixels.toFloat()
- return min(
- screenWidth / (composition?.bounds?.width()?.toFloat() ?: screenWidth),
- screenHeight / (composition?.bounds?.height()?.toFloat() ?: screenHeight)
+ val bounds = state.composition()?.bounds
+ return@withState min(
+ screenWidth / (bounds?.width()?.toFloat() ?: screenWidth),
+ screenHeight / (bounds?.height()?.toFloat() ?: screenHeight)
)
}
@@ -524,7 +500,7 @@
val animationSpeed: Float = Math.abs(animation.speed)
- val totalTime = ((animation.duration / animationSpeed) / 1000.0)
+ val totalTime = ((animation.duration / animationSpeed) / 1000.0)
val totalTimeFormatted = ("%.1f").format(totalTime)
val progress = (totalTime / 100.0) * (Math.round(animation.progress * 100.0))
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
index ac90cc8..f70d592 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
@@ -1,54 +1,122 @@
package com.airbnb.lottie.samples
+import android.animation.ValueAnimator
import android.app.Application
-import android.arch.lifecycle.AndroidViewModel
-import android.arch.lifecycle.MutableLiveData
import android.net.Uri
-import android.os.Handler
-import android.os.Looper
+import android.support.v4.app.FragmentActivity
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieTask
import com.airbnb.lottie.samples.model.CompositionArgs
+import com.airbnb.mvrx.*
import java.io.FileInputStream
import java.io.FileNotFoundException
-class PlayerViewModel(application: Application) : AndroidViewModel(application) {
+data class PlayerState(
+ val composition: Async<LottieComposition> = Uninitialized,
+ val controlsVisible: Boolean = true,
+ val controlBarVisible: Boolean = true,
+ val renderGraphVisible: Boolean = false,
+ val borderVisible: Boolean = false,
+ val backgroundColorVisible: Boolean = false,
+ val scaleVisible: Boolean = false,
+ val speedVisible: Boolean = false,
+ val trimVisible: Boolean = false,
+ val useHardwareAcceleration: Boolean = false,
+ val useMergePaths: Boolean = false,
+ val minFrame: Int = 0,
+ val maxFrame: Int = 0,
+ val speed: Float = 1f,
+ val repeatCount: Int = ValueAnimator.INFINITE
+) : MvRxState
- private val handler = Handler(Looper.getMainLooper())
-
- val composition = MutableLiveData<LottieComposition>()
- val error = MutableLiveData<Throwable>()
+class PlayerViewModel(
+ initialState: PlayerState,
+ private val application: Application
+) : MvRxViewModel<PlayerState>(initialState) {
fun fetchAnimation(args: CompositionArgs) {
val url = args.url ?: args.animationData?.lottieLink
- val task = when {
- url != null -> LottieCompositionFactory.fromUrl(getApplication(), url)
+ when {
+ url != null -> LottieCompositionFactory.fromUrl(application, url)
args.fileUri != null -> taskForUri(args.fileUri)
- args.asset != null -> LottieCompositionFactory.fromAsset(getApplication(), args.asset)
+ args.asset != null -> LottieCompositionFactory.fromAsset(application, args.asset)
else -> throw IllegalArgumentException("Don't know how to fetch animation for $args")
}
- registerTask(task)
+ .addListener {
+ setState {
+ copy(composition = Success(it), minFrame = it.startFrame.toInt(), maxFrame = it.endFrame.toInt())
+ }
+ }
+ .addFailureListener { setState { copy(composition = Fail(it)) } }
}
private fun taskForUri(uri: Uri): LottieTask<LottieComposition> {
val fis = try {
when (uri.scheme) {
"file" -> FileInputStream(uri.path)
- "content" -> getApplication<LottieApplication>().contentResolver.openInputStream(uri)
+ "content" -> application.contentResolver.openInputStream(uri)
else -> return LottieTask() { throw IllegalArgumentException("Unknown scheme ${uri.scheme}") }
}
} catch (e: FileNotFoundException) {
- return LottieTask() { throw e }
+ return LottieTask { throw e }
}
return LottieCompositionFactory.fromJsonInputStream(fis, uri.toString())
}
- private fun registerTask(task: LottieTask<LottieComposition>) {
- task
- .addListener { composition.value = it }
- .addFailureListener { error.value = it }
+ fun toggleRenderGraphVisible() = setState { copy(renderGraphVisible = !renderGraphVisible) }
+
+ fun toggleBorderVisible() = setState { copy(borderVisible = !borderVisible) }
+
+ fun toggleBackgroundColorVisible() = setState { copy(backgroundColorVisible = !backgroundColorVisible) }
+
+ fun setBackgroundColorVisible(visible: Boolean) = setState { copy(backgroundColorVisible = visible) }
+
+ fun toggleScaleVisible() = setState { copy(scaleVisible = !scaleVisible) }
+
+ fun setScaleVisible(visible: Boolean) = setState { copy(scaleVisible = visible) }
+
+ fun toggleSpeedVisible() = setState { copy(speedVisible = !speedVisible) }
+
+ fun setSpeedVisible(visible: Boolean) = setState { copy(speedVisible = visible) }
+
+ fun toggleTrimVisible() = setState { copy(trimVisible = !trimVisible) }
+
+ fun setTrimVisible(visible: Boolean) = setState { copy(trimVisible = visible) }
+
+ fun toggleHardwareAcceleration() = setState { copy(useHardwareAcceleration = !useHardwareAcceleration) }
+
+ fun toggleMergePaths() = setState { copy(useMergePaths = !useMergePaths) }
+
+ fun setMinFrame(minFrame: Int) = setState {
+ copy(minFrame = Math.max(minFrame, composition()?.startFrame?.toInt() ?: 0))
+ }
+
+ fun setMaxFrame(maxFrame: Int) = setState {
+ copy(maxFrame = Math.min(maxFrame, composition()?.endFrame?.toInt() ?: 0))
+ }
+
+ fun setSpeed(speed: Float) = setState { copy(speed = speed) }
+
+ fun toggleLoop() = setState { copy(repeatCount = if (repeatCount == ValueAnimator.INFINITE) 0 else ValueAnimator.INFINITE) }
+
+ fun setDistractionFree(distractionFree: Boolean) = setState {
+ copy(
+ controlsVisible = !distractionFree,
+ controlBarVisible = !distractionFree,
+ renderGraphVisible = false,
+ borderVisible = false,
+ backgroundColorVisible = false,
+ scaleVisible = false,
+ speedVisible = false,
+ trimVisible = false
+ )
+ }
+
+ companion object : MvRxViewModelFactory<PlayerState> {
+ @JvmStatic
+ override fun create(activity: FragmentActivity, state: PlayerState) = PlayerViewModel(state, activity.application)
}
}
\ No newline at end of file
diff --git a/LottieSample/src/main/res/layout/control_bar.xml b/LottieSample/src/main/res/layout/control_bar.xml
index d4f61ed..c8a0d1d 100644
--- a/LottieSample/src/main/res/layout/control_bar.xml
+++ b/LottieSample/src/main/res/layout/control_bar.xml
@@ -22,9 +22,7 @@
app:text="@string/control_bar_render_graph" />
<com.airbnb.lottie.samples.views.ControlBarItemToggleView
android:id="@+id/warningsButton"
- style="@style/ControlBarItem"
- app:src="@drawable/ic_device"
- app:text="@string/control_bar_no_warnings" />
+ style="@style/ControlBarItem" />
<com.airbnb.lottie.samples.views.ControlBarItemToggleView
android:id="@+id/borderToggle"
style="@style/ControlBarItem"
diff --git a/LottieSample/src/main/res/layout/control_bar_player_controls.xml b/LottieSample/src/main/res/layout/control_bar_player_controls.xml
index f909bb2..d7dc7f4 100644
--- a/LottieSample/src/main/res/layout/control_bar_player_controls.xml
+++ b/LottieSample/src/main/res/layout/control_bar_player_controls.xml
@@ -90,7 +90,8 @@
android:padding="4dp"
android:text="@string/render_times_per_layer_button"
android:textColor="@color/item_selected_teal"
- android:textSize="10sp"/>
+ android:textSize="10sp"
+ android:visibility="gone"/>
<TextView
android:id="@+id/lottieVersionView"
diff --git a/LottieSample/src/main/res/layout/control_bar_speed.xml b/LottieSample/src/main/res/layout/control_bar_speed.xml
index 107a739..34e54f3 100644
--- a/LottieSample/src/main/res/layout/control_bar_speed.xml
+++ b/LottieSample/src/main/res/layout/control_bar_speed.xml
@@ -4,7 +4,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/speedContainer"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:visibility="gone">
<LinearLayout
android:id="@+id/speedButtonsContainer"
diff --git a/LottieSample/src/main/res/layout/control_bar_trim.xml b/LottieSample/src/main/res/layout/control_bar_trim.xml
index b850b44..15a547c 100644
--- a/LottieSample/src/main/res/layout/control_bar_trim.xml
+++ b/LottieSample/src/main/res/layout/control_bar_trim.xml
@@ -4,7 +4,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/trimContainer"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:visibility="gone">
<LinearLayout
android:layout_width="wrap_content"
@@ -22,7 +23,7 @@
android:layout_weight="0.5"/>
<com.airbnb.lottie.samples.views.ControlBarItemToggleView
- android:id="@+id/minFrame"
+ android:id="@+id/minFrameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
@@ -32,7 +33,7 @@
android:layout_weight="1"/>
<com.airbnb.lottie.samples.views.ControlBarItemToggleView
- android:id="@+id/maxFrame"
+ android:id="@+id/maxFrameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
diff --git a/LottieSample/src/main/res/values/plurals.xml b/LottieSample/src/main/res/values/plurals.xml
index eda632d..42309d3 100644
--- a/LottieSample/src/main/res/values/plurals.xml
+++ b/LottieSample/src/main/res/values/plurals.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="warnings">
- <item quantity="zero">%d warnings</item>
+ <item quantity="zero">No warnings</item>
<item quantity="one">%d warning</item>
<item quantity="other">%d warnings</item>
</plurals>
diff --git a/LottieSample/src/main/res/values/strings.xml b/LottieSample/src/main/res/values/strings.xml
index b213819..6dc70df 100644
--- a/LottieSample/src/main/res/values/strings.xml
+++ b/LottieSample/src/main/res/values/strings.xml
@@ -48,7 +48,6 @@
<string name="control_bar_scale">Scale</string>
<string name="control_bar_trim">Trim</string>
<string name="control_bar_speed">Speed</string>
- <string name="control_bar_no_warnings">No Warnings</string>
<string name="control_bar_key_paths">Show KeyPaths</string>
<string name="control_bar_merge_paths">Merge Paths (KitKat+)</string>
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
index 13a2093..eb1c7a2 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
@@ -326,6 +326,9 @@
* anything about that.
*/
public void useHardwareAcceleration(boolean use) {
+ if (useHardwareLayer == use) {
+ return;
+ }
useHardwareLayer = use;
enableOrDisableHardwareLayer();
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
index c97b94f..a2b3e4a 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
@@ -132,6 +132,10 @@
* instead of using merge paths.
*/
public void enableMergePathsForKitKatAndAbove(boolean enable) {
+ if (enableMergePaths == enable) {
+ return;
+ }
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Log.w(TAG, "Merge paths are not supported pre-Kit Kat.");
return;