Remove unused legacy views
This commit is contained in:
parent
956af82f79
commit
537cde60a5
@ -1,29 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.legacy.data
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import de.mm20.launcher2.graphics.TextDrawable
|
|
||||||
import de.mm20.launcher2.ui.R
|
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
|
||||||
import de.mm20.launcher2.ktx.sp
|
|
||||||
import de.mm20.launcher2.search.data.Searchable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper searchable that is used to display text inside [de.mm20.launcher2.ui.search2.SearchGridViews]
|
|
||||||
*/
|
|
||||||
class InformationText(
|
|
||||||
override val label: String,
|
|
||||||
val clickAction: (() -> Unit)? = null
|
|
||||||
) : Searchable() {
|
|
||||||
|
|
||||||
override val key: String
|
|
||||||
get() = "text://${label}"
|
|
||||||
|
|
||||||
override fun getPlaceholderIcon(context: Context): LauncherIcon {
|
|
||||||
return LauncherIcon(
|
|
||||||
foreground = TextDrawable("i", fontSize = 40 * context.sp),
|
|
||||||
background = ColorDrawable(ContextCompat.getColor(context, R.color.grey))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.legacy.transition
|
|
||||||
|
|
||||||
import android.animation.Animator
|
|
||||||
import android.animation.AnimatorSet
|
|
||||||
import android.animation.ObjectAnimator
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.view.animation.AccelerateInterpolator
|
|
||||||
import android.view.animation.DecelerateInterpolator
|
|
||||||
import androidx.transition.Transition
|
|
||||||
import androidx.transition.TransitionValues
|
|
||||||
import de.mm20.launcher2.ui.legacy.view.LauncherCardView
|
|
||||||
|
|
||||||
class LauncherCards : Transition() {
|
|
||||||
override fun captureStartValues(transitionValues: TransitionValues) {
|
|
||||||
val view = transitionValues.view
|
|
||||||
if (view is LauncherCardView) {
|
|
||||||
transitionValues.values[PROP_ELEVATION] = view.cardElevation
|
|
||||||
transitionValues.values[PROP_BG_OPACITY] = view.backgroundOpacity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun captureEndValues(transitionValues: TransitionValues) {
|
|
||||||
val view = transitionValues.view
|
|
||||||
if (view is LauncherCardView) {
|
|
||||||
transitionValues.values[PROP_ELEVATION] = view.cardElevation
|
|
||||||
transitionValues.values[PROP_BG_OPACITY] = view.backgroundOpacity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
|
|
||||||
if (startValues == null || endValues == null) return null
|
|
||||||
|
|
||||||
if (startValues.view !is LauncherCardView) return null
|
|
||||||
val endView = endValues.view as? LauncherCardView
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
val startElevation = startValues.values[PROP_ELEVATION] as Float
|
|
||||||
val endElevation = endValues.values[PROP_ELEVATION] as Float
|
|
||||||
|
|
||||||
val startBgOpacity = startValues.values[PROP_BG_OPACITY] as Int
|
|
||||||
val endBgOpacity = endValues.values[PROP_BG_OPACITY] as Int
|
|
||||||
|
|
||||||
if(startBgOpacity < endBgOpacity) {
|
|
||||||
return AnimatorSet().apply {
|
|
||||||
playTogether(
|
|
||||||
ObjectAnimator.ofFloat(endView, "cardElevation", startElevation, startElevation, endElevation).apply {
|
|
||||||
interpolator = AccelerateInterpolator()
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofInt(endView, "backgroundOpacity", startBgOpacity, endBgOpacity).apply {
|
|
||||||
interpolator = DecelerateInterpolator()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AnimatorSet().apply {
|
|
||||||
playTogether(
|
|
||||||
ObjectAnimator.ofFloat(endView, "cardElevation", startElevation, endElevation, endElevation).apply {
|
|
||||||
interpolator = DecelerateInterpolator()
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofInt(endView, "backgroundOpacity", startBgOpacity, endBgOpacity).apply {
|
|
||||||
interpolator = AccelerateInterpolator()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val PROP_ELEVATION = "mm20:app:elevation"
|
|
||||||
private const val PROP_BG_OPACITY = "mm20:app:bg_opacity"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.legacy.view
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import androidx.appcompat.widget.AppCompatImageView
|
|
||||||
import de.mm20.launcher2.ui.R
|
|
||||||
|
|
||||||
class AspectRationImageView : AppCompatImageView {
|
|
||||||
|
|
||||||
var aspectRatio = 1f
|
|
||||||
var fixedHeight = false
|
|
||||||
|
|
||||||
constructor(context: Context) : super(context)
|
|
||||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
|
||||||
constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(context, attrs, defStyleRes) {
|
|
||||||
attrs?.let {
|
|
||||||
val ta = context.theme.obtainStyledAttributes(it, R.styleable.AspectRatioImageView, 0, defStyleRes)
|
|
||||||
aspectRatio = ta.getFloat(R.styleable.AspectRatioImageView_aspectRatio, 1f)
|
|
||||||
fixedHeight = ta.getBoolean(R.styleable.AspectRatioImageView_fixedHeight, false)
|
|
||||||
ta.recycle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
||||||
if (fixedHeight) {
|
|
||||||
setMeasuredDimension((measuredHeight / aspectRatio).toInt(), measuredHeight)
|
|
||||||
} else {
|
|
||||||
setMeasuredDimension(measuredWidth, (measuredWidth * aspectRatio).toInt())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.legacy.view
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import com.google.android.material.card.MaterialCardView
|
|
||||||
import de.mm20.launcher2.ktx.dp
|
|
||||||
import de.mm20.launcher2.ktx.lifecycleOwner
|
|
||||||
import de.mm20.launcher2.ktx.lifecycleScope
|
|
||||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
|
||||||
import de.mm20.launcher2.ui.R
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.core.component.KoinComponent
|
|
||||||
import org.koin.core.component.inject
|
|
||||||
|
|
||||||
open class InnerCardView @JvmOverloads constructor(
|
|
||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.materialCardViewStyle
|
|
||||||
) : MaterialCardView(context, attrs, defStyleAttr), KoinComponent {
|
|
||||||
init {
|
|
||||||
|
|
||||||
radius = LauncherCardView.currentCardStyle.radius * dp
|
|
||||||
strokeColor = ContextCompat.getColor(context, R.color.color_divider)
|
|
||||||
strokeWidth = (1 * dp).toInt()
|
|
||||||
cardElevation = 2 * dp
|
|
||||||
outlineProvider = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private val dataStore: LauncherDataStore by inject()
|
|
||||||
private var job: Job? = null
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
job?.cancel()
|
|
||||||
job = lifecycleScope.launch {
|
|
||||||
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
|
|
||||||
dataStore.data.map { it.cards.radius }.distinctUntilChanged().collectLatest {
|
|
||||||
radius = it * dp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onDetachedFromWindow() {
|
|
||||||
super.onDetachedFromWindow()
|
|
||||||
job?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.legacy.view
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import com.google.android.material.card.MaterialCardView
|
|
||||||
import de.mm20.launcher2.ktx.dp
|
|
||||||
import de.mm20.launcher2.ktx.lifecycleOwner
|
|
||||||
import de.mm20.launcher2.ktx.lifecycleScope
|
|
||||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
|
||||||
import de.mm20.launcher2.preferences.Settings
|
|
||||||
import de.mm20.launcher2.ui.R
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.core.component.KoinComponent
|
|
||||||
import org.koin.core.component.inject
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A card view implementation that solves the following issues of MaterialCardView:
|
|
||||||
* (1) Content clipping in transitions
|
|
||||||
* (2) Elevation overlay color
|
|
||||||
*/
|
|
||||||
open class LauncherCardView @JvmOverloads constructor(
|
|
||||||
context: Context,
|
|
||||||
attrs: AttributeSet? = null,
|
|
||||||
defStyleAttr: Int = R.attr.materialCardViewStyle
|
|
||||||
) : MaterialCardView(context, attrs, defStyleAttr), KoinComponent {
|
|
||||||
|
|
||||||
var backgroundOpacity: Int = (currentCardStyle.opacity * 255).roundToInt()
|
|
||||||
set(value) {
|
|
||||||
setCardBackgroundColor(cardBackgroundColor.defaultColor.let {
|
|
||||||
ColorStateList.valueOf((it and 0xFFFFFF) or (value shl 24))
|
|
||||||
})
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
private var strokeOpacity: Int = if (currentCardStyle.borderWidth > 0) 0xFF else 0
|
|
||||||
set(value) {
|
|
||||||
setStrokeColor(strokeColorStateList?.defaultColor?.let {
|
|
||||||
ColorStateList.valueOf((it and 0xFFFFFF) or (value shl 24))
|
|
||||||
})
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
private var overrideBackgroundOpacity = false
|
|
||||||
|
|
||||||
private var actualElevation: Float
|
|
||||||
|
|
||||||
init {
|
|
||||||
strokeColor = cardBackgroundColor.defaultColor
|
|
||||||
strokeWidth = (currentCardStyle.borderWidth * dp).roundToInt()
|
|
||||||
radius = currentCardStyle.radius * dp
|
|
||||||
|
|
||||||
context.theme.obtainStyledAttributes(
|
|
||||||
attrs,
|
|
||||||
R.styleable.LauncherCardView,
|
|
||||||
0, 0
|
|
||||||
).apply {
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (hasValue(R.styleable.LauncherCardView_backgroundOpacity)) {
|
|
||||||
overrideBackgroundOpacity = true
|
|
||||||
backgroundOpacity = getInt(R.styleable.LauncherCardView_backgroundOpacity, 255)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
recycle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
strokeOpacity = if (backgroundOpacity == 0) 0 else 0xFF
|
|
||||||
actualElevation = context.resources.getDimension(R.dimen.card_elevation)
|
|
||||||
cardElevation = if (backgroundOpacity == 255) actualElevation else 0f
|
|
||||||
}
|
|
||||||
|
|
||||||
private val dataStore: LauncherDataStore by inject()
|
|
||||||
|
|
||||||
private var job: Job? = null
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
job?.cancel()
|
|
||||||
job = lifecycleScope.launch {
|
|
||||||
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
|
|
||||||
dataStore.data.map { it.cards }.distinctUntilChanged().collectLatest {
|
|
||||||
currentCardStyle = it
|
|
||||||
if (!overrideBackgroundOpacity) {
|
|
||||||
backgroundOpacity = (it.opacity * 255).roundToInt()
|
|
||||||
strokeOpacity = if (backgroundOpacity == 0) 0 else 0xFF
|
|
||||||
cardElevation = if (backgroundOpacity == 255) actualElevation else 0f
|
|
||||||
}
|
|
||||||
strokeWidth = (it.borderWidth * dp).roundToInt()
|
|
||||||
radius = it.radius * dp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetachedFromWindow() {
|
|
||||||
super.onDetachedFromWindow()
|
|
||||||
job?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
internal var currentCardStyle = Settings.CardSettings.getDefaultInstance()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,475 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.legacy.view
|
|
||||||
|
|
||||||
import android.animation.AnimatorSet
|
|
||||||
import android.animation.ObjectAnimator
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.*
|
|
||||||
import android.view.animation.AccelerateInterpolator
|
|
||||||
import android.widget.FrameLayout
|
|
||||||
import android.widget.ImageView
|
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.animation.doOnEnd
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.lifecycle.*
|
|
||||||
import com.google.android.material.card.MaterialCardView
|
|
||||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
|
||||||
import de.mm20.launcher2.ktx.dp
|
|
||||||
import de.mm20.launcher2.ktx.lifecycleOwner
|
|
||||||
import de.mm20.launcher2.ktx.lifecycleScope
|
|
||||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
|
||||||
import de.mm20.launcher2.search.data.Searchable
|
|
||||||
import de.mm20.launcher2.transition.ChangingLayoutTransition
|
|
||||||
import de.mm20.launcher2.ui.R
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.core.component.KoinComponent
|
|
||||||
import org.koin.core.component.inject
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
class SwipeCardView @JvmOverloads constructor(
|
|
||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
|
||||||
) : MaterialCardView(context, attrs, defStyleAttr), KoinComponent {
|
|
||||||
|
|
||||||
private val backdrop = FrameLayout(context)
|
|
||||||
private val icon = ImageView(context)
|
|
||||||
private val content = MaterialCardView(context)
|
|
||||||
private val iconColor = ContextCompat.getColor(context, R.color.swipe_card_icon_color)
|
|
||||||
private val iconColorActive =
|
|
||||||
ContextCompat.getColor(context, R.color.swipe_card_icon_color_active)
|
|
||||||
|
|
||||||
init {
|
|
||||||
super.addView(backdrop)
|
|
||||||
super.addView(icon, LayoutParams((40 * dp).toInt(), (24 * dp).toInt()))
|
|
||||||
icon.setColorFilter(iconColor)
|
|
||||||
super.addView(content)
|
|
||||||
content.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
||||||
content.transitionName = "SwipeCardView/content"
|
|
||||||
radius = LauncherCardView.currentCardStyle.radius * dp
|
|
||||||
content.radius = radius
|
|
||||||
super.setCardBackgroundColor(
|
|
||||||
ContextCompat.getColor(
|
|
||||||
context,
|
|
||||||
R.color.swipe_cardview_background
|
|
||||||
)
|
|
||||||
)
|
|
||||||
content.layoutTransition = ChangingLayoutTransition()
|
|
||||||
val ta = context.obtainStyledAttributes(intArrayOf(android.R.attr.selectableItemBackground))
|
|
||||||
content.foreground = ta.getDrawable(0)
|
|
||||||
ta.recycle()
|
|
||||||
}
|
|
||||||
|
|
||||||
var leftAction: SwipeAction? = null
|
|
||||||
var rightAction: SwipeAction? = null
|
|
||||||
|
|
||||||
|
|
||||||
private var leftThreshold = false
|
|
||||||
set(value) {
|
|
||||||
if (value == field) return
|
|
||||||
if (value) {
|
|
||||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
||||||
leftAction?.color?.let { backdrop.setBackgroundColor(it) }
|
|
||||||
AnimatorSet().also {
|
|
||||||
it.playTogether(
|
|
||||||
ViewAnimationUtils.createCircularReveal(
|
|
||||||
backdrop,
|
|
||||||
(28 * dp).toInt(),
|
|
||||||
(height * 0.5).toInt(),
|
|
||||||
0f,
|
|
||||||
width.toFloat()
|
|
||||||
)
|
|
||||||
.setDuration(300),
|
|
||||||
ObjectAnimator.ofArgb(icon, "colorFilter", iconColor, iconColorActive)
|
|
||||||
.apply {
|
|
||||||
duration = 150
|
|
||||||
startDelay = 100
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofFloat(icon, "scaleX", 1.2f).apply {
|
|
||||||
duration = 200
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofFloat(icon, "scaleY", 1.2f).apply {
|
|
||||||
duration = 200
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}.start()
|
|
||||||
} else {
|
|
||||||
AnimatorSet().also {
|
|
||||||
it.playTogether(
|
|
||||||
ViewAnimationUtils.createCircularReveal(
|
|
||||||
backdrop,
|
|
||||||
(28 * dp).toInt(),
|
|
||||||
(height * 0.5).toInt(),
|
|
||||||
width.toFloat(),
|
|
||||||
0f
|
|
||||||
).apply {
|
|
||||||
doOnEnd {
|
|
||||||
if (!rightThreshold && !leftThreshold) backdrop.setBackgroundColor(0)
|
|
||||||
}
|
|
||||||
duration = 300
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofArgb(icon, "colorFilter", iconColorActive, iconColor)
|
|
||||||
.apply {
|
|
||||||
duration = 150
|
|
||||||
startDelay = 100
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofFloat(icon, "scaleX", 1f).apply {
|
|
||||||
duration = 200
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofFloat(icon, "scaleY", 1f).apply {
|
|
||||||
duration = 200
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}.start()
|
|
||||||
|
|
||||||
}
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
private var rightThreshold = false
|
|
||||||
set(value) {
|
|
||||||
if (value == field) return
|
|
||||||
if (value) {
|
|
||||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
||||||
rightAction?.color?.let { backdrop.setBackgroundColor(it) }
|
|
||||||
AnimatorSet().also {
|
|
||||||
it.playTogether(
|
|
||||||
ViewAnimationUtils.createCircularReveal(
|
|
||||||
backdrop,
|
|
||||||
(width - 28 * dp).toInt(),
|
|
||||||
(height * 0.5).toInt(),
|
|
||||||
0f,
|
|
||||||
width.toFloat()
|
|
||||||
)
|
|
||||||
.setDuration(300),
|
|
||||||
ObjectAnimator.ofArgb(icon, "colorFilter", iconColor, iconColorActive)
|
|
||||||
.apply {
|
|
||||||
duration = 150
|
|
||||||
startDelay = 100
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofFloat(icon, "scaleX", 1.2f).apply {
|
|
||||||
duration = 200
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofFloat(icon, "scaleY", 1.2f).apply {
|
|
||||||
duration = 200
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}.start()
|
|
||||||
} else {
|
|
||||||
AnimatorSet().also {
|
|
||||||
it.playTogether(
|
|
||||||
ViewAnimationUtils.createCircularReveal(
|
|
||||||
backdrop,
|
|
||||||
(width - 28 * dp).toInt(),
|
|
||||||
(height * 0.5).toInt(),
|
|
||||||
width.toFloat(),
|
|
||||||
0f
|
|
||||||
).apply {
|
|
||||||
doOnEnd {
|
|
||||||
if (!rightThreshold && !leftThreshold) backdrop.setBackgroundColor(0)
|
|
||||||
}
|
|
||||||
duration = 300
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofArgb(icon, "colorFilter", iconColorActive, iconColor)
|
|
||||||
.apply {
|
|
||||||
duration = 150
|
|
||||||
startDelay = 100
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofFloat(icon, "scaleX", 1f).apply {
|
|
||||||
duration = 200
|
|
||||||
},
|
|
||||||
ObjectAnimator.ofFloat(icon, "scaleY", 1f).apply {
|
|
||||||
duration = 200
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}.start()
|
|
||||||
|
|
||||||
}
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
private var swipeDirectionLeft: Boolean? = null
|
|
||||||
set(value) {
|
|
||||||
if (field == value) return
|
|
||||||
backdrop.setBackgroundColor(0)
|
|
||||||
if (value == true) {
|
|
||||||
leftAction?.icon?.let { icon.setImageResource(it) }
|
|
||||||
icon.setPadding((16 * dp).toInt(), 0, 0, 0)
|
|
||||||
icon.layoutParams = (icon.layoutParams as LayoutParams).also {
|
|
||||||
it.gravity = Gravity.CENTER_VERTICAL or Gravity.START
|
|
||||||
}
|
|
||||||
icon.pivotX = 28 * dp
|
|
||||||
} else if (value == false) {
|
|
||||||
rightAction?.icon?.let { icon.setImageResource(it) }
|
|
||||||
icon.setPadding(0, 0, (16 * dp).toInt(), 0)
|
|
||||||
icon.layoutParams = (icon.layoutParams as LayoutParams).also {
|
|
||||||
it.gravity = Gravity.CENTER_VERTICAL or Gravity.END
|
|
||||||
}
|
|
||||||
icon.pivotX = 12 * dp
|
|
||||||
}
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setCardBackgroundColor(color: Int) {
|
|
||||||
content.setCardBackgroundColor(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setCardBackgroundColor(color: ColorStateList?) {
|
|
||||||
content.setCardBackgroundColor(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addView(child: View?) {
|
|
||||||
content.addView(child)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addView(child: View?, params: ViewGroup.LayoutParams?) {
|
|
||||||
content.addView(child, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addView(child: View?, width: Int, height: Int) {
|
|
||||||
content.addView(child, width, height)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setRadius(radius: Float) {
|
|
||||||
super.setRadius(radius)
|
|
||||||
content?.radius = radius
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeAllViews() {
|
|
||||||
content.removeAllViews()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeAllViewsInLayout() {
|
|
||||||
content.removeAllViewsInLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeView(view: View?) {
|
|
||||||
content.removeView(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeViewAt(index: Int) {
|
|
||||||
content.removeViewAt(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeViewInLayout(view: View?) {
|
|
||||||
content.removeViewInLayout(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeViews(start: Int, count: Int) {
|
|
||||||
content.removeViews(start, count)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeViewsInLayout(start: Int, count: Int) {
|
|
||||||
content.removeViewsInLayout(start, count)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private var downX = 0f
|
|
||||||
private var downY = 0f
|
|
||||||
private var isClick = false
|
|
||||||
private var isLongClick = false
|
|
||||||
private val longClickRunnable = Runnable {
|
|
||||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
||||||
performLongClick()
|
|
||||||
isClick = false
|
|
||||||
isLongClick = true
|
|
||||||
content.foreground?.state = intArrayOf(android.R.attr.state_enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
|
||||||
return when (event?.action) {
|
|
||||||
MotionEvent.ACTION_DOWN -> {
|
|
||||||
downX = event.x
|
|
||||||
downY = event.y
|
|
||||||
isClick = true
|
|
||||||
isLongClick = false
|
|
||||||
handler?.postDelayed(
|
|
||||||
longClickRunnable,
|
|
||||||
ViewConfiguration.getLongPressTimeout().toLong()
|
|
||||||
)
|
|
||||||
content.foreground?.setHotspot(event.x, event.y)
|
|
||||||
content.foreground?.state =
|
|
||||||
intArrayOf(android.R.attr.state_pressed, android.R.attr.state_enabled)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
MotionEvent.ACTION_MOVE -> {
|
|
||||||
if (abs(event.x - downX) > abs(event.y - downY)) parent.requestDisallowInterceptTouchEvent(
|
|
||||||
true
|
|
||||||
)
|
|
||||||
if (isLongClick) return false
|
|
||||||
swipeDirectionLeft = event.x - downX > 0
|
|
||||||
if (isClick && abs(event.x - downX) < 4 * dp) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
isClick = false
|
|
||||||
handler?.removeCallbacks(longClickRunnable)
|
|
||||||
content.translationX = event.x - downX
|
|
||||||
leftThreshold = content.translationX > 0.5f * width
|
|
||||||
rightThreshold = content.translationX < -0.5f * width
|
|
||||||
content.foreground?.state = intArrayOf(android.R.attr.state_enabled)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
MotionEvent.ACTION_UP -> {
|
|
||||||
when {
|
|
||||||
isClick -> {
|
|
||||||
performClick()
|
|
||||||
content.foreground?.state = intArrayOf(android.R.attr.state_enabled)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
leftThreshold -> {
|
|
||||||
if (leftAction?.action?.invoke() == true) {
|
|
||||||
content.animate().translationX(width.toFloat())
|
|
||||||
.setDuration(200)
|
|
||||||
.setInterpolator(AccelerateInterpolator())
|
|
||||||
.start()
|
|
||||||
} else {
|
|
||||||
content.animate().translationX(0f)
|
|
||||||
.setDuration(300)
|
|
||||||
.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rightThreshold -> {
|
|
||||||
if (rightAction?.action?.invoke() == true) {
|
|
||||||
content.animate().translationX(-width.toFloat())
|
|
||||||
.setDuration(200)
|
|
||||||
.setInterpolator(AccelerateInterpolator())
|
|
||||||
.start()
|
|
||||||
} else {
|
|
||||||
content.animate().translationX(0f)
|
|
||||||
.setDuration(300)
|
|
||||||
.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
content.animate().translationX(0f).setDuration(300).start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
|
|
||||||
}
|
|
||||||
MotionEvent.ACTION_CANCEL -> {
|
|
||||||
content.animate().translationX(0f).setDuration(300).start()
|
|
||||||
handler?.removeCallbacks(longClickRunnable)
|
|
||||||
content.foreground?.state = intArrayOf(android.R.attr.state_enabled)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
MotionEvent.ACTION_OUTSIDE -> {
|
|
||||||
handler?.removeCallbacks(longClickRunnable)
|
|
||||||
content.foreground?.state = intArrayOf(android.R.attr.state_enabled)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val dataStore: LauncherDataStore by inject()
|
|
||||||
private var job: Job? = null
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
job?.cancel()
|
|
||||||
job = lifecycleScope.launch {
|
|
||||||
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
|
|
||||||
dataStore.data.map { it.cards.radius }.distinctUntilChanged().collectLatest {
|
|
||||||
radius = it * dp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onDetachedFromWindow() {
|
|
||||||
super.onDetachedFromWindow()
|
|
||||||
job?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
open class SwipeAction(
|
|
||||||
@DrawableRes var icon: Int,
|
|
||||||
var color: Int,
|
|
||||||
/**
|
|
||||||
* Action that is performed after a swipe.
|
|
||||||
* returns true if the card should be animated out or false if it should be animated back.
|
|
||||||
*/
|
|
||||||
var action: () -> Boolean
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class FavoriteSwipeAction(val context: Context, val searchable: Searchable) :
|
|
||||||
SwipeCardView.SwipeAction(
|
|
||||||
R.drawable.ic_star_solid,
|
|
||||||
ContextCompat.getColor(context, R.color.amber),
|
|
||||||
{ false }
|
|
||||||
), KoinComponent {
|
|
||||||
|
|
||||||
private val repository: FavoritesRepository by inject()
|
|
||||||
|
|
||||||
private val pinned = repository.isPinned(searchable)
|
|
||||||
.asLiveData((context as AppCompatActivity).lifecycleScope.coroutineContext)
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
|
||||||
pinned.observe(context as LifecycleOwner) {
|
|
||||||
setPinned(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setPinned(pinned: Boolean) {
|
|
||||||
if (pinned) {
|
|
||||||
icon = R.drawable.ic_star_outline
|
|
||||||
action = {
|
|
||||||
repository.unpinItem(
|
|
||||||
searchable
|
|
||||||
)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
icon = R.drawable.ic_star_solid
|
|
||||||
action = {
|
|
||||||
repository.pinItem(
|
|
||||||
searchable
|
|
||||||
)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HideSwipeAction(val context: Context, val searchable: Searchable) : SwipeCardView.SwipeAction(
|
|
||||||
R.drawable.ic_visibility_off,
|
|
||||||
ContextCompat.getColor(context, R.color.blue),
|
|
||||||
{ false }
|
|
||||||
), KoinComponent {
|
|
||||||
|
|
||||||
private val repository: FavoritesRepository by inject()
|
|
||||||
|
|
||||||
private val hidden = repository.isPinned(searchable)
|
|
||||||
.asLiveData((context as AppCompatActivity).lifecycleScope.coroutineContext)
|
|
||||||
|
|
||||||
init {
|
|
||||||
hidden.observe(context as LifecycleOwner) {
|
|
||||||
setHidden(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setHidden(hidden: Boolean) {
|
|
||||||
if (hidden) {
|
|
||||||
icon = R.drawable.ic_visibility
|
|
||||||
action = {
|
|
||||||
repository.unhideItem(
|
|
||||||
searchable
|
|
||||||
)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
icon = R.drawable.ic_visibility_off
|
|
||||||
action = {
|
|
||||||
repository.hideItem(
|
|
||||||
searchable
|
|
||||||
)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,303 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.legacy.view
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.appcompat.widget.PopupMenu
|
|
||||||
import androidx.appcompat.widget.TooltipCompat
|
|
||||||
import androidx.core.view.setPadding
|
|
||||||
import androidx.lifecycle.*
|
|
||||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
|
||||||
import de.mm20.launcher2.ktx.dp
|
|
||||||
import de.mm20.launcher2.search.data.Searchable
|
|
||||||
import de.mm20.launcher2.ui.R
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import org.koin.core.component.KoinComponent
|
|
||||||
import org.koin.core.component.inject
|
|
||||||
|
|
||||||
class ToolbarView : LinearLayout {
|
|
||||||
constructor(context: Context) : super(context)
|
|
||||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
|
||||||
constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(
|
|
||||||
context,
|
|
||||||
attrs,
|
|
||||||
defStyleRes
|
|
||||||
)
|
|
||||||
|
|
||||||
private val slots = context.resources.getInteger(R.integer.config_toolbarSlots)
|
|
||||||
|
|
||||||
private var leftOverflowIcon: ImageView? = null
|
|
||||||
private var rightOverflowIcon: ImageView? = null
|
|
||||||
|
|
||||||
private val leftActions = mutableListOf<ToolbarAction>()
|
|
||||||
private val rightActions = mutableListOf<ToolbarAction>()
|
|
||||||
|
|
||||||
var iconStyle = R.style.LauncherTheme_IconStyle
|
|
||||||
|
|
||||||
init {
|
|
||||||
orientation = HORIZONTAL
|
|
||||||
clipChildren = false
|
|
||||||
clipToPadding = false
|
|
||||||
|
|
||||||
val spacer = View(context)
|
|
||||||
spacer.layoutParams = LayoutParams(0, LayoutParams.MATCH_PARENT, 1f)
|
|
||||||
addView(spacer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addAction(action: ToolbarAction, placement: Int) {
|
|
||||||
val useOverflowMenu = (leftActions.size >= slots && placement == PLACEMENT_START) ||
|
|
||||||
(rightActions.size >= slots && placement == PLACEMENT_END)
|
|
||||||
|
|
||||||
if (useOverflowMenu) {
|
|
||||||
if (placement == PLACEMENT_START) {
|
|
||||||
if (leftOverflowIcon == null) {
|
|
||||||
val overflowMenuIcon = ImageView(context, null, R.attr.iconStyle)
|
|
||||||
overflowMenuIcon.isClickable = true
|
|
||||||
overflowMenuIcon.isFocusable = true
|
|
||||||
overflowMenuIcon.setPadding((12 * dp).toInt())
|
|
||||||
overflowMenuIcon.layoutParams =
|
|
||||||
LayoutParams((48 * dp).toInt(), (48 * dp).toInt())
|
|
||||||
overflowMenuIcon.setImageResource(R.drawable.ic_more_vert)
|
|
||||||
removeViewAt(leftActions.size - 1)
|
|
||||||
addView(overflowMenuIcon, leftActions.size - 1)
|
|
||||||
leftOverflowIcon = overflowMenuIcon
|
|
||||||
}
|
|
||||||
leftActions.add(action)
|
|
||||||
val popup = PopupMenu(context, leftOverflowIcon!!)
|
|
||||||
for (i in slots - 1 until leftActions.size) {
|
|
||||||
if (leftActions[i].subActions.isNotEmpty()) {
|
|
||||||
val submenu = popup.menu.addSubMenu(leftActions[i].title)
|
|
||||||
for ((j, sa) in leftActions[i].subActions.withIndex()) {
|
|
||||||
submenu.add(i, j, 0, sa.title)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
popup.menu.add(i, 0, 0, leftActions[i].title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
popup.setOnMenuItemClickListener {
|
|
||||||
if (leftActions[it.groupId].subActions.isEmpty()) {
|
|
||||||
leftActions[it.groupId].clickAction?.invoke()
|
|
||||||
} else {
|
|
||||||
leftActions[it.groupId].subActions[it.itemId].clickAction.invoke()
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
leftOverflowIcon?.setOnClickListener {
|
|
||||||
popup.show()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (rightOverflowIcon == null) {
|
|
||||||
val overflowMenuIcon = ImageView(context, null, R.attr.iconStyle)
|
|
||||||
overflowMenuIcon.isClickable = true
|
|
||||||
overflowMenuIcon.isFocusable = true
|
|
||||||
overflowMenuIcon.setPadding((12 * dp).toInt())
|
|
||||||
overflowMenuIcon.layoutParams =
|
|
||||||
LayoutParams((48 * dp).toInt(), (48 * dp).toInt())
|
|
||||||
overflowMenuIcon.setImageResource(R.drawable.ic_more_vert)
|
|
||||||
removeViewAt(childCount - 1)
|
|
||||||
addView(overflowMenuIcon)
|
|
||||||
rightOverflowIcon = overflowMenuIcon
|
|
||||||
}
|
|
||||||
rightActions.add(action)
|
|
||||||
val popup = PopupMenu(context, rightOverflowIcon!!)
|
|
||||||
for (i in slots - 1 until rightActions.size) {
|
|
||||||
if (rightActions[i].subActions.isNotEmpty()) {
|
|
||||||
val submenu = popup.menu.addSubMenu(i, -1, 0, rightActions[i].title)
|
|
||||||
for ((j, sa) in rightActions[i].subActions.withIndex()) {
|
|
||||||
submenu.add(i, j, 0, sa.title)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val item = popup.menu.add(i, -1, 0, rightActions[i].title)
|
|
||||||
rightActions[i].titleChanged = {
|
|
||||||
item.title = rightActions[i].title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
popup.setOnMenuItemClickListener {
|
|
||||||
if (rightActions[it.groupId].subActions.isEmpty()) {
|
|
||||||
rightActions[it.groupId].clickAction?.invoke()
|
|
||||||
} else if (it.itemId != -1) {
|
|
||||||
rightActions[it.groupId].subActions[it.itemId].clickAction.invoke()
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
rightOverflowIcon?.setOnClickListener {
|
|
||||||
popup.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val imageView = getIconView(action)
|
|
||||||
if (placement == PLACEMENT_START) {
|
|
||||||
addView(imageView, leftActions.size)
|
|
||||||
leftActions.add(action)
|
|
||||||
} else {
|
|
||||||
addView(imageView)
|
|
||||||
rightActions.add(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getIconView(action: ToolbarAction): ImageView {
|
|
||||||
val imageView = ImageView(context, null, 0, iconStyle)
|
|
||||||
imageView.setImageResource(action.icon)
|
|
||||||
imageView.isClickable = true
|
|
||||||
imageView.isFocusable = true
|
|
||||||
action.iconChanged = {
|
|
||||||
imageView.setImageResource(action.icon)
|
|
||||||
}
|
|
||||||
action.titleChanged = {
|
|
||||||
TooltipCompat.setTooltipText(imageView, action.title)
|
|
||||||
}
|
|
||||||
TooltipCompat.setTooltipText(imageView, action.title)
|
|
||||||
|
|
||||||
val submenu =
|
|
||||||
if (action.subActions.isEmpty()) null else PopupMenu(context, imageView).apply {
|
|
||||||
for ((i, subAction) in action.subActions.withIndex()) {
|
|
||||||
menu.add(0, i, 0, subAction.title)
|
|
||||||
}
|
|
||||||
setOnMenuItemClickListener {
|
|
||||||
action.subActions[it.itemId].clickAction.invoke()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
imageView.setOnClickListener { _ ->
|
|
||||||
if (submenu != null) {
|
|
||||||
submenu.show()
|
|
||||||
} else {
|
|
||||||
action.clickAction?.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
imageView.setPadding((12 * dp).toInt())
|
|
||||||
imageView.layoutParams = LayoutParams((48 * dp).toInt(), (48 * dp).toInt())
|
|
||||||
|
|
||||||
return imageView
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
removeAllViews()
|
|
||||||
leftActions.clear()
|
|
||||||
rightActions.clear()
|
|
||||||
leftOverflowIcon = null
|
|
||||||
rightOverflowIcon = null
|
|
||||||
val spacer = View(context)
|
|
||||||
spacer.layoutParams = LayoutParams(0, LayoutParams.MATCH_PARENT, 1f)
|
|
||||||
addView(spacer)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val PLACEMENT_START = 0
|
|
||||||
val PLACEMENT_END = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class ToolbarAction(icon: Int, title: String) {
|
|
||||||
|
|
||||||
@DrawableRes
|
|
||||||
var icon: Int = icon
|
|
||||||
set(@DrawableRes value) {
|
|
||||||
field = value
|
|
||||||
iconChanged?.invoke()
|
|
||||||
}
|
|
||||||
var title = title
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
titleChanged?.invoke()
|
|
||||||
}
|
|
||||||
|
|
||||||
var subActions: MutableList<ToolbarSubaction> = mutableListOf()
|
|
||||||
|
|
||||||
var clickAction: (() -> Unit)? = null
|
|
||||||
|
|
||||||
internal var iconChanged: (() -> Unit)? = null
|
|
||||||
internal var titleChanged: (() -> Unit)? = null
|
|
||||||
}
|
|
||||||
|
|
||||||
open class ToolbarSubaction(val title: String, var clickAction: (() -> Unit)) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class FavoriteToolbarAction(val context: Context, val item: Searchable) : ToolbarAction(
|
|
||||||
R.drawable.ic_star_outline,
|
|
||||||
context.getString(R.string.menu_favorites_pin)
|
|
||||||
), KoinComponent {
|
|
||||||
|
|
||||||
private val repository: FavoritesRepository by inject()
|
|
||||||
private var isPinned = false
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
if (value) {
|
|
||||||
title = context.getString(R.string.menu_favorites_unpin)
|
|
||||||
icon = R.drawable.ic_star_solid
|
|
||||||
} else {
|
|
||||||
title = context.getString(R.string.menu_favorites_pin)
|
|
||||||
icon = R.drawable.ic_star_outline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
clickAction = {
|
|
||||||
if (isPinned) {
|
|
||||||
repository.unpinItem(item)
|
|
||||||
} else {
|
|
||||||
repository.pinItem(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(context as LifecycleOwner).apply {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
|
||||||
repository.isPinned(item).collectLatest {
|
|
||||||
isPinned = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VisibilityToolbarAction(val context: Context, val item: Searchable) : ToolbarAction(
|
|
||||||
R.drawable.ic_visibility,
|
|
||||||
context.getString(R.string.menu_hide)
|
|
||||||
), KoinComponent {
|
|
||||||
|
|
||||||
private val repository: FavoritesRepository by inject()
|
|
||||||
private var isHidden = false
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
if (value) {
|
|
||||||
title = context.getString(R.string.menu_unhide)
|
|
||||||
icon = R.drawable.ic_visibility
|
|
||||||
} else {
|
|
||||||
title = context.getString(R.string.menu_hide)
|
|
||||||
icon = R.drawable.ic_visibility_off
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
clickAction = {
|
|
||||||
if (isHidden) {
|
|
||||||
repository.unhideItem(item)
|
|
||||||
} else {
|
|
||||||
repository.hideItem(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(context as LifecycleOwner).apply {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
|
||||||
repository.isHidden(item).collectLatest {
|
|
||||||
isHidden = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user