mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
265 lines
9.0 KiB
Kotlin
265 lines
9.0 KiB
Kotlin
/*
|
|
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
|
*
|
|
* This file is part of KeePassDX.
|
|
*
|
|
* KeePassDX is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* KeePassDX is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
package com.kunzisoft.keepass.view
|
|
|
|
import android.animation.Animator
|
|
import android.animation.AnimatorSet
|
|
import android.animation.ValueAnimator
|
|
import android.content.Context
|
|
import android.graphics.*
|
|
import android.text.Selection
|
|
import android.text.Spannable
|
|
import android.text.SpannableString
|
|
import android.text.Spanned
|
|
import android.text.method.LinkMovementMethod
|
|
import android.text.method.PasswordTransformationMethod
|
|
import android.text.style.ClickableSpan
|
|
import android.view.View
|
|
import android.view.animation.AccelerateDecelerateInterpolator
|
|
import android.widget.TextView
|
|
import android.widget.Toast
|
|
import androidx.appcompat.widget.Toolbar
|
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
import androidx.core.view.isVisible
|
|
import androidx.core.view.updatePadding
|
|
import com.google.android.material.snackbar.Snackbar
|
|
import com.kunzisoft.keepass.R
|
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
|
import androidx.appcompat.view.menu.ActionMenuItemView
|
|
|
|
import android.widget.ImageView
|
|
import androidx.appcompat.widget.ActionMenuView
|
|
|
|
import androidx.core.graphics.drawable.DrawableCompat
|
|
|
|
import android.graphics.drawable.Drawable
|
|
import com.google.android.material.appbar.CollapsingToolbarLayout
|
|
|
|
|
|
/**
|
|
* Replace font by monospace, must be called after setText()
|
|
*/
|
|
fun TextView.applyFontVisibility() {
|
|
val typeFace = Typeface.createFromAsset(context.assets, "fonts/FiraMono-Regular.ttf")
|
|
typeface = typeFace
|
|
}
|
|
|
|
fun TextView.applyHiddenStyle(hide: Boolean, changeMaxLines: Boolean = true) {
|
|
if (hide) {
|
|
transformationMethod = PasswordTransformationMethod.getInstance()
|
|
if (changeMaxLines)
|
|
maxLines = 1
|
|
} else {
|
|
transformationMethod = null
|
|
if (changeMaxLines)
|
|
maxLines = 800
|
|
}
|
|
}
|
|
|
|
fun TextView.setTextSize(unit: Int, defaultSize: Float, multiplier: Float) {
|
|
if (multiplier > 0.0F)
|
|
setTextSize(unit, defaultSize * multiplier)
|
|
}
|
|
|
|
fun TextView.strikeOut(strikeOut: Boolean) {
|
|
paintFlags = if (strikeOut)
|
|
paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
|
else
|
|
paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
|
}
|
|
|
|
fun TextView.customLink(listener: (View) -> Unit) {
|
|
val spannableString = SpannableString(this.text)
|
|
val clickableSpan = object : ClickableSpan() {
|
|
override fun onClick(view: View) {
|
|
Selection.setSelection((view as TextView).text as Spannable, 0)
|
|
view.invalidate()
|
|
listener.invoke(view)
|
|
}
|
|
}
|
|
spannableString.setSpan(clickableSpan, 0, text.length,
|
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
this.movementMethod = LinkMovementMethod.getInstance() // without LinkMovementMethod, link can not click
|
|
this.setText(spannableString, TextView.BufferType.SPANNABLE)
|
|
}
|
|
|
|
fun Snackbar.asError(): Snackbar {
|
|
this.view.apply {
|
|
setBackgroundColor(Color.RED)
|
|
findViewById<TextView>(R.id.snackbar_text).setTextColor(Color.WHITE)
|
|
}
|
|
return this
|
|
}
|
|
|
|
fun View.collapse(animate: Boolean = true,
|
|
onCollapseFinished: (() -> Unit)? = null) {
|
|
val recordViewHeight = layoutParams.height
|
|
val slideAnimator = ValueAnimator.ofInt(height, 0)
|
|
if (animate)
|
|
slideAnimator.duration = 300L
|
|
slideAnimator.addUpdateListener { animation ->
|
|
layoutParams.height = animation.animatedValue as Int
|
|
requestLayout()
|
|
}
|
|
AnimatorSet().apply {
|
|
play(slideAnimator)
|
|
interpolator = AccelerateDecelerateInterpolator()
|
|
addListener(object: Animator.AnimatorListener {
|
|
override fun onAnimationStart(animation: Animator?) {
|
|
}
|
|
override fun onAnimationRepeat(animation: Animator?) {}
|
|
override fun onAnimationEnd(animation: Animator?) {
|
|
visibility = View.GONE
|
|
layoutParams.height = recordViewHeight
|
|
onCollapseFinished?.invoke()
|
|
}
|
|
override fun onAnimationCancel(animation: Animator?) {}
|
|
})
|
|
}.start()
|
|
}
|
|
|
|
fun View.expand(animate: Boolean = true,
|
|
defaultHeight: Int? = null,
|
|
onExpandFinished: (() -> Unit)? = null) {
|
|
val viewHeight = defaultHeight ?: layoutParams.height
|
|
layoutParams.height = 0
|
|
val slideAnimator = ValueAnimator
|
|
.ofInt(0, viewHeight)
|
|
if (animate)
|
|
slideAnimator.duration = 300L
|
|
var alreadyVisible = false
|
|
slideAnimator.addUpdateListener { animation ->
|
|
layoutParams.height = animation.animatedValue as Int
|
|
if (!alreadyVisible && layoutParams.height > 0) {
|
|
visibility = View.VISIBLE
|
|
alreadyVisible = true
|
|
}
|
|
requestLayout()
|
|
}
|
|
AnimatorSet().apply {
|
|
play(slideAnimator)
|
|
interpolator = AccelerateDecelerateInterpolator()
|
|
addListener(object: Animator.AnimatorListener {
|
|
override fun onAnimationStart(animation: Animator?) {}
|
|
override fun onAnimationRepeat(animation: Animator?) {}
|
|
override fun onAnimationEnd(animation: Animator?) {
|
|
onExpandFinished?.invoke()
|
|
}
|
|
override fun onAnimationCancel(animation: Animator?) {}
|
|
})
|
|
}.start()
|
|
}
|
|
|
|
fun View.hideByFading() {
|
|
alpha = 1f
|
|
animate()
|
|
.alpha(0f)
|
|
.setDuration(140)
|
|
.setListener(null)
|
|
}
|
|
|
|
fun View.showByFading() {
|
|
if (!isVisible) {
|
|
isVisible = true
|
|
// Trick to keep the focus
|
|
alpha = 0.0001f
|
|
animate()
|
|
.alpha(1f)
|
|
.setDuration(140)
|
|
.setListener(null)
|
|
}
|
|
}
|
|
|
|
fun View.updateLockPaddingLeft() {
|
|
updatePadding(resources.getDimensionPixelSize(
|
|
if (PreferencesUtil.showLockDatabaseButton(context)) {
|
|
R.dimen.lock_button_size
|
|
} else {
|
|
R.dimen.hidden_lock_button_size
|
|
}
|
|
))
|
|
}
|
|
|
|
fun Context.showActionErrorIfNeeded(result: ActionRunnable.Result) {
|
|
if (!result.isSuccess) {
|
|
result.exception?.errorId?.let { errorId ->
|
|
Toast.makeText(this, errorId, Toast.LENGTH_LONG).show()
|
|
} ?: result.message?.let { message ->
|
|
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
|
}
|
|
}
|
|
}
|
|
|
|
fun CoordinatorLayout.showActionErrorIfNeeded(result: ActionRunnable.Result) {
|
|
if (!result.isSuccess) {
|
|
result.exception?.errorId?.let { errorId ->
|
|
Snackbar.make(this, errorId, Snackbar.LENGTH_LONG).asError().show()
|
|
} ?: result.message?.let { message ->
|
|
Snackbar.make(this, message, Snackbar.LENGTH_LONG).asError().show()
|
|
}
|
|
}
|
|
}
|
|
|
|
fun Toolbar.changeControlColor(color: Int) {
|
|
val colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
|
for (i in 0 until childCount) {
|
|
val view: View = getChildAt(i)
|
|
// Change the color of back button (or open drawer button).
|
|
if (view is ImageView) {
|
|
//Action Bar back button
|
|
view.drawable.colorFilter = colorFilter
|
|
}
|
|
if (view is ActionMenuView) {
|
|
view.post {
|
|
for (j in 0 until view.childCount) {
|
|
// Change the color of any ActionMenuViews - icons that
|
|
// are not back button, nor text, nor overflow menu icon.
|
|
val innerView: View = view.getChildAt(j)
|
|
if (innerView is ActionMenuItemView) {
|
|
innerView.compoundDrawables.forEach { drawable ->
|
|
//Important to set the color filter in separate thread,
|
|
//by adding it to the message queue
|
|
//Won't work otherwise.
|
|
drawable?.colorFilter = colorFilter
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Change the color of title and subtitle.
|
|
setTitleTextColor(color)
|
|
setSubtitleTextColor(color)
|
|
// Change the color of the Overflow Menu icon.
|
|
var drawable: Drawable? = overflowIcon
|
|
if (drawable != null) {
|
|
drawable = DrawableCompat.wrap(drawable)
|
|
DrawableCompat.setTint(drawable.mutate(), color)
|
|
overflowIcon = drawable
|
|
}
|
|
invalidate()
|
|
}
|
|
|
|
fun CollapsingToolbarLayout.changeTitleColor(color: Int) {
|
|
setCollapsedTitleTextColor(color)
|
|
setExpandedTitleColor(color)
|
|
invalidate()
|
|
} |