Show OTP Token in entry list

This commit is contained in:
J-Jamet
2021-08-26 17:17:59 +02:00
parent 21c3ccd637
commit 9cce5f645f
9 changed files with 155 additions and 54 deletions

View File

@@ -26,6 +26,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
@@ -148,6 +149,7 @@ class NodeAdapter (private val context: Context,
if (oldItem is Entry && newItem is Entry) {
typeContentTheSame = oldItem.getVisualTitle() == newItem.getVisualTitle()
&& oldItem.username == newItem.username
&& oldItem.getOtpElement() == newItem.getOtpElement()
&& oldItem.containsAttachment() == newItem.containsAttachment()
} else if (oldItem is Group && newItem is Group) {
typeContentTheSame = oldItem.numberOfChildEntries == newItem.numberOfChildEntries
@@ -357,6 +359,25 @@ class NodeAdapter (private val context: Context,
}
}
val otpElement = entry.getOtpElement()
holder.otpContainer?.removeCallbacks(holder.otpRunnable)
holder.otpRunnable = null
if (otpElement != null && otpElement.token.isNotEmpty()) {
holder.otpProgress?.apply {
max = otpElement.period
progress = otpElement.secondsRemaining
}
holder.otpToken?.text = otpElement.token
holder.otpRunnable = Runnable {
holder.otpProgress?.progress = otpElement.secondsRemaining
holder.otpToken?.text = otpElement.token
holder.otpContainer?.postDelayed(holder.otpRunnable, 200)
}
holder.otpContainer?.post(holder.otpRunnable)
holder.otpContainer?.visibility = View.VISIBLE
} else {
holder.otpContainer?.visibility = View.GONE
}
holder.attachmentIcon?.visibility =
if (entry.containsAttachment()) View.VISIBLE else View.GONE
@@ -413,6 +434,10 @@ class NodeAdapter (private val context: Context,
var text: TextView = itemView.findViewById(R.id.node_text)
var subText: TextView = itemView.findViewById(R.id.node_subtext)
var meta: TextView = itemView.findViewById(R.id.node_meta)
var otpContainer: ViewGroup? = itemView.findViewById(R.id.node_otp_container)
var otpProgress: ProgressBar? = itemView.findViewById(R.id.node_otp_progress)
var otpToken: TextView? = itemView.findViewById(R.id.node_otp_token)
var otpRunnable: Runnable? = null
var numberChildren: TextView? = itemView.findViewById(R.id.node_child_numbers)
var attachmentIcon: ImageView? = itemView.findViewById(R.id.node_attachment_icon)
}

View File

@@ -56,7 +56,7 @@ class OtpModel() : Parcelable {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as OtpElement
other as OtpModel
if (type != other.type) return false
// Token type is important only if it's a TOTP

View File

@@ -185,6 +185,19 @@ data class OtpElement(var otpModel: OtpModel = OtpModel()) {
return secondsRemaining == otpModel.period
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is OtpElement) return false
if (otpModel != other.otpModel) return false
return true
}
override fun hashCode(): Int {
return otpModel.hashCode()
}
companion object {
const val MIN_HOTP_COUNTER = 0
const val MAX_HOTP_COUNTER = Long.MAX_VALUE

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_selected="true" android:color="@color/white"/>
<item android:color="?attr/colorAccent"/>
</selector>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="270"
android:toDegrees="270">
<shape
android:shape="ring"
android:innerRadiusRatio="2.5"
android:thickness="2dp"
android:useLevel="true">
<solid android:color="?attr/colorAccent" />
</shape>
</rotate>

View File

@@ -7,6 +7,6 @@
android:innerRadiusRatio="2.5"
android:thickness="2dp"
android:useLevel="true">
<solid android:color="@color/orange" />
<solid android:color="@color/entry_info_color" />
</shape>
</rotate>

View File

@@ -25,15 +25,20 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.Selectable.Item">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:minHeight="56dp"
android:maxHeight="72dp"
app:layout_constraintWidth_percent="@dimen/content_percent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="?android:attr/selectableItemBackground" >
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/node_icon"
android:layout_width="32dp"
@@ -44,67 +49,116 @@
android:layout_marginStart="@dimen/image_list_margin_vertical"
android:layout_marginRight="@dimen/image_list_margin_vertical"
android:layout_marginEnd="@dimen/image_list_margin_vertical"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="@drawable/ic_blank_32dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<LinearLayout
android:id="@+id/node_container_info"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="2dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:layout_marginLeft="@dimen/image_list_margin_vertical"
android:layout_marginStart="@dimen/image_list_margin_vertical"
android:layout_marginRight="@dimen/image_list_margin_vertical"
android:layout_marginEnd="@dimen/image_list_margin_vertical"
android:layout_marginLeft="@dimen/image_list_margin_vertical"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/node_icon"
app:layout_constraintLeft_toRightOf="@+id/node_icon"
app:layout_constraintEnd_toStartOf="@+id/node_attachment_icon"
app:layout_constraintRight_toLeftOf="@+id/node_attachment_icon">
app:layout_constraintEnd_toStartOf="@+id/node_options"
app:layout_constraintRight_toLeftOf="@+id/node_options">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/node_text"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Entry.Title"
android:layout_width="wrap_content"
tools:text="Node Title"
android:maxLines="2"
android:layout_height="wrap_content"
android:ellipsize="end"
style="@style/KeepassDXStyle.TextAppearance.Entry.Title" />
android:maxLines="2"
tools:text="Node Title" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/node_subtext"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Entry.SubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-4dp"
tools:text="Node SubTitle"
android:lines="1"
android:singleLine="true"
style="@style/KeepassDXStyle.TextAppearance.Entry.SubTitle" />
tools:text="Node SubTitle" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/node_meta"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Entry.Meta"
android:layout_width="wrap_content"
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C"
android:layout_height="wrap_content"
android:lines="1"
android:singleLine="true"
style="@style/KeepassDXStyle.TextAppearance.Entry.Meta" />
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/node_attachment_icon"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/node_options"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="12dp"
android:layout_marginLeft="12dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<LinearLayout
android:id="@+id/node_otp_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="18dp"
android:layout_marginRight="18dp"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/node_attachment_icon"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/node_otp_token"
style="@style/KeepassDXStyle.TextAppearance.Entry.Info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:text="5136" />
<ProgressBar
android:id="@+id/node_otp_progress"
style="@style/KeepassDXStyle.ProgressBar.Circle.NoBackground"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="center"
android:layout_marginStart="6dp"
android:layout_marginLeft="6dp"
android:max="100"
android:progress="60" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/node_attachment_icon"
style="@style/KeepassDXStyle.TextAppearance.Entry.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/image_list_margin_vertical"
android:layout_marginStart="@dimen/image_list_margin_vertical"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:src="@drawable/ic_attach_file_white_24dp"
style="@style/KeepassDXStyle.TextAppearance.Entry.Icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent" />
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/node_otp_container" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -25,12 +25,16 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.Selectable.Item">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:minHeight="56dp"
android:maxHeight="72dp"
app:layout_constraintWidth_percent="@dimen/content_percent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="?android:attr/selectableItemBackground" >
@@ -66,14 +70,14 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/node_icon"
android:layout_toRightOf="@+id/node_icon"
android:layout_toStartOf="@+id/node_image_identifier"
android:layout_toLeftOf="@+id/node_image_identifier"
android:orientation="vertical"
android:paddingTop="2dp"
android:paddingBottom="4dp">
android:layout_toEndOf="@+id/node_icon"
android:layout_toRightOf="@+id/node_icon"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/node_text"
@@ -84,25 +88,27 @@
android:gravity="center_vertical"
android:maxLines="2"
tools:text="Node Title" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/node_subtext"
style="@style/KeepassDXStyle.TextAppearance.Group.SubTitle"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-4dp"
android:gravity="center_vertical"
android:lines="1"
android:singleLine="true"
android:visibility="gone"
tools:text="Node SubTitle" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/node_meta"
android:layout_height="wrap_content"
style="@style/KeepassDXStyle.TextAppearance.Group.Meta"
android:layout_width="wrap_content"
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C"
android:layout_height="wrap_content"
android:lines="1"
android:singleLine="true"
style="@style/KeepassDXStyle.TextAppearance.Group.Meta" />
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView

View File

@@ -354,6 +354,7 @@
<style name="KeepassDXStyle.TextAppearance.Entry.Title" parent="KeepassDXStyle.TextAppearance">
<item name="android:textColor">@color/entry_title_color</item>
<item name="android:tint">@color/entry_title_color</item>
<item name="android:textStyle">bold</item>
<item name="android:textSize">16sp</item>
</style>
<style name="KeepassDXStyle.TextAppearance.Entry.SubTitle" parent="KeepassDXStyle.TextAppearance.Small">
@@ -365,6 +366,12 @@
<item name="android:tint">@color/entry_title_color</item>
<item name="android:textSize">11sp</item>
</style>
<style name="KeepassDXStyle.TextAppearance.Entry.Info" parent="KeepassDXStyle.TextAppearance.Small">
<item name="android:textColor">@color/entry_info_color</item>
<item name="android:tint">@color/entry_info_color</item>
<item name="android:textStyle">bold</item>
<item name="android:textSize">16sp</item>
</style>
<style name="KeepassDXStyle.TextAppearance.Entry.Icon" parent="KeepassDXStyle.TextAppearance.Small">
<item name="tint">@color/entry_subtitle_color</item>
</style>
@@ -475,6 +482,10 @@
</style>
<!-- Progress bar -->
<style name="KeepassDXStyle.ProgressBar.Circle.NoBackground" parent="Widget.AppCompat.ProgressBar.Horizontal">
<item name="android:progressDrawable">@drawable/foreground_progress_circle</item>
<item name="android:background">@null</item>
</style>
<style name="KeepassDXStyle.ProgressBar.Circle" parent="Widget.AppCompat.ProgressBar.Horizontal">
<item name="android:progressDrawable">@drawable/foreground_progress_circle</item>
<item name="android:background">@drawable/background_progress_circle</item>