blob: 4d8effb076f99f7dbab47b7780cb2886f761da72 [file] [log] [blame]
/*
* Copyright 2018 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.navigation.ui
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.Drawable
import android.os.Bundle
import androidx.annotation.StringRes
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
import androidx.navigation.FloatingWindow
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import java.lang.ref.WeakReference
/**
* The abstract OnDestinationChangedListener for keeping any type of app bar updated. This handles
* both updating the title and updating the Up Indicator, transitioning between the drawer icon and
* up arrow as needed.
*/
internal abstract class AbstractAppBarOnDestinationChangedListener(
private val context: Context,
private val configuration: AppBarConfiguration
) : NavController.OnDestinationChangedListener {
private val openableLayoutWeakReference =
configuration.openableLayout?.run { WeakReference(this) }
private var arrowDrawable: DrawerArrowDrawable? = null
private var animator: ValueAnimator? = null
protected abstract fun setTitle(title: CharSequence?)
protected abstract fun setNavigationIcon(icon: Drawable?, @StringRes contentDescription: Int)
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
arguments: Bundle?
) {
if (destination is FloatingWindow) {
return
}
val openableLayout = openableLayoutWeakReference?.get()
if (openableLayoutWeakReference != null && openableLayout == null) {
controller.removeOnDestinationChangedListener(this)
return
}
val label = destination.fillInLabel(context, arguments)
if (label != null) {
setTitle(label)
}
val isTopLevelDestination = configuration.isTopLevelDestination(destination)
if (openableLayout == null && isTopLevelDestination) {
setNavigationIcon(null, 0)
} else {
setActionBarUpIndicator(openableLayout != null && isTopLevelDestination)
}
}
@SuppressLint("ObjectAnimatorBinding")
private fun setActionBarUpIndicator(showAsDrawerIndicator: Boolean) {
val (arrow, animate) =
arrowDrawable?.run { this to true }
?: DrawerArrowDrawable(context).also { arrowDrawable = it } to false
setNavigationIcon(
arrow,
if (showAsDrawerIndicator) R.string.nav_app_bar_open_drawer_description
else R.string.nav_app_bar_navigate_up_description
)
val endValue = if (showAsDrawerIndicator) 0f else 1f
if (animate) {
val startValue = arrow.progress
animator?.cancel()
animator = ObjectAnimator.ofFloat(arrow, "progress", startValue, endValue)
(animator as ObjectAnimator).start()
} else {
arrow.progress = endValue
}
}
}