Merge branch 'feature/Breadcrumb' into develop

This commit is contained in:
J-Jamet
2021-12-16 17:44:20 +01:00
14 changed files with 462 additions and 147 deletions

View File

@@ -1,3 +1,7 @@
KeePassDX(3.1.0)
* Add breadcrumb
* Add path in search results #1148
KeePassDX(3.0.4)
* Fix autofill inline bugs #1173 #1165
* Small UI change

View File

@@ -11,8 +11,8 @@ android {
applicationId "com.kunzisoft.keepass"
minSdkVersion 15
targetSdkVersion 30
versionCode = 91
versionName = "3.0.4"
versionCode = 92
versionName = "3.1.0"
multiDexEnabled true
testApplicationId = "com.kunzisoft.keepass.tests"

View File

@@ -25,7 +25,6 @@ import android.app.TimePickerDialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.*
import android.util.Log
import android.view.Menu
@@ -41,12 +40,15 @@ import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.*
import com.kunzisoft.keepass.activities.fragments.GroupFragment
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.activities.helpers.SpecialMode
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
import com.kunzisoft.keepass.adapters.BreadcrumbAdapter
import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter
import com.kunzisoft.keepass.autofill.AutofillComponent
import com.kunzisoft.keepass.autofill.AutofillHelper
@@ -84,18 +86,23 @@ class GroupActivity : DatabaseLockActivity(),
private var coordinatorLayout: CoordinatorLayout? = null
private var lockView: View? = null
private var toolbar: Toolbar? = null
private var databaseNameView: TextView? = null
private var searchContainer: ViewGroup? = null
private var searchNumbers: TextView? = null
private var searchString: TextView? = null
private var toolbarBreadcrumb: Toolbar? = null
private var searchTitleView: View? = null
private var toolbarAction: ToolbarAction? = null
private var iconView: ImageView? = null
private var numberChildrenView: TextView? = null
private var addNodeButtonView: AddNodeButtonView? = null
private var groupNameView: TextView? = null
private var groupMetaView: TextView? = null
private var breadcrumbListView: RecyclerView? = null
private var loadingView: ProgressBar? = null
private val mGroupViewModel: GroupViewModel by viewModels()
private val mGroupEditViewModel: GroupEditViewModel by viewModels()
private var mBreadcrumbAdapter: BreadcrumbAdapter? = null
private var mGroupFragment: GroupFragment? = null
private var mRecyclingBinEnabled = false
private var mRecyclingBinIsCurrentGroup = false
@@ -123,8 +130,6 @@ class GroupActivity : DatabaseLockActivity(),
AutofillHelper.buildActivityResultLauncher(this)
else null
private var mIconColor: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -134,13 +139,16 @@ class GroupActivity : DatabaseLockActivity(),
// Initialize views
rootContainerView = findViewById(R.id.activity_group_container_view)
coordinatorLayout = findViewById(R.id.group_coordinator)
iconView = findViewById(R.id.group_icon)
numberChildrenView = findViewById(R.id.group_numbers)
addNodeButtonView = findViewById(R.id.add_node_button)
toolbar = findViewById(R.id.toolbar)
databaseNameView = findViewById(R.id.database_name)
searchContainer = findViewById(R.id.search_container)
searchNumbers = findViewById(R.id.search_numbers)
searchString = findViewById(R.id.search_string)
toolbarBreadcrumb = findViewById(R.id.toolbar_breadcrumb)
searchTitleView = findViewById(R.id.search_title)
groupNameView = findViewById(R.id.group_name)
groupMetaView = findViewById(R.id.group_meta)
breadcrumbListView = findViewById(R.id.breadcrumb_list)
toolbarAction = findViewById(R.id.toolbar_action)
lockView = findViewById(R.id.lock_button)
loadingView = findViewById(R.id.loading)
@@ -152,10 +160,18 @@ class GroupActivity : DatabaseLockActivity(),
toolbar?.title = ""
setSupportActionBar(toolbar)
// Retrieve the textColor to tint the icon
val taTextColor = theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse))
mIconColor = taTextColor.getColor(0, Color.WHITE)
taTextColor.recycle()
mBreadcrumbAdapter = BreadcrumbAdapter(this).apply {
// Open group on breadcrumb click
onItemClickListener = { node, _ ->
finishNodeAction()
mDatabase?.let { database ->
onNodeClick(database, node)
}
}
}
breadcrumbListView?.apply {
adapter = mBreadcrumbAdapter
}
// Retrieve group if defined at launch
manageIntent(intent)
@@ -211,6 +227,12 @@ class GroupActivity : DatabaseLockActivity(),
// Update last access time.
currentGroup.touch(modified = false, touchParents = false)
// Add breadcrumb
mBreadcrumbAdapter?.apply {
setNode(currentGroup)
breadcrumbListView?.scrollToPosition(itemCount -1)
}
// Add listeners to the add buttons
addNodeButtonView?.setAddGroupClickListener {
GroupEditDialogFragment.create(GroupInfo().apply {
@@ -371,7 +393,9 @@ class GroupActivity : DatabaseLockActivity(),
// Search suggestion
database?.let {
databaseNameView?.text = if (it.name.isNotEmpty()) it.name else getString(R.string.database)
mSearchSuggestionAdapter = SearchEntryCursorAdapter(this, it)
mBreadcrumbAdapter?.iconDrawableFactory = it.iconDrawableFactory
mOnSuggestionListener = object : SearchView.OnSuggestionListener {
override fun onSuggestionClick(position: Int): Boolean {
mSearchSuggestionAdapter?.let { searchAdapter ->
@@ -455,7 +479,8 @@ class GroupActivity : DatabaseLockActivity(),
finishNodeAction()
refreshNumberOfChildren(mCurrentGroup)
// Refresh breadcrumb
mBreadcrumbAdapter?.setNode(mCurrentGroup)
}
/**
@@ -502,58 +527,28 @@ class GroupActivity : DatabaseLockActivity(),
private fun assignGroupViewElements(group: Group?) {
// Assign title
if (group != null) {
if (groupNameView != null) {
val title = group.title
groupNameView?.text = if (title.isNotEmpty()) title else getText(R.string.root)
groupNameView?.invalidate()
}
if (groupMetaView != null) {
val meta = group.nodeId.toString()
groupMetaView?.text = meta
if (meta.isNotEmpty()
&& !group.isVirtual
&& PreferencesUtil.showUUID(this)) {
groupMetaView?.visibility = View.VISIBLE
} else {
groupMetaView?.visibility = View.GONE
}
groupMetaView?.invalidate()
}
}
if (group?.isVirtual == true) {
searchTitleView?.visibility = View.VISIBLE
if (toolbar != null) {
toolbar?.navigationIcon = null
}
iconView?.visibility = View.GONE
searchContainer?.visibility = View.VISIBLE
val title = group.title
searchString?.text = if (title.isNotEmpty()) title else ""
searchNumbers?.text = group.numberOfChildEntries.toString()
databaseNameView?.visibility = View.GONE
toolbarBreadcrumb?.navigationIcon = null
toolbarBreadcrumb?.collapse()
} else {
searchTitleView?.visibility = View.GONE
// Assign the group icon depending of IconPack or custom icon
iconView?.visibility = View.VISIBLE
group?.let { currentGroup ->
iconView?.let { imageView ->
mIconDrawableFactory?.assignDatabaseIcon(
imageView,
currentGroup.icon,
mIconColor
)
}
if (toolbar != null) {
if (group.containsParent())
toolbar?.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp)
else {
toolbar?.navigationIcon = null
}
searchContainer?.visibility = View.GONE
databaseNameView?.visibility = View.VISIBLE
// Refresh breadcrumb
if (toolbarBreadcrumb?.isVisible != true) {
mBreadcrumbAdapter?.setNode(null)
toolbarBreadcrumb?.expand {
mBreadcrumbAdapter?.setNode(group)
}
} else {
mBreadcrumbAdapter?.setNode(group)
}
}
// Assign number of children
refreshNumberOfChildren(group)
// Hide button
initAddButton(group)
}
@@ -577,18 +572,6 @@ class GroupActivity : DatabaseLockActivity(),
}
}
private fun refreshNumberOfChildren(group: Group?) {
numberChildrenView?.apply {
if (PreferencesUtil.showNumberEntries(context)) {
group?.refreshNumberOfChildEntries(Group.ChildFilter.getDefaults(context))
text = group?.numberOfChildEntries?.toString() ?: ""
visibility = View.VISIBLE
} else {
visibility = View.GONE
}
}
}
override fun onScrolled(dy: Int) {
if (actionNodeMode == null)
addNodeButtonView?.hideOrShowButtonOnScrollListener(dy)
@@ -1034,7 +1017,7 @@ class GroupActivity : DatabaseLockActivity(),
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
onBackPressed()
// TODO change database
return true
}
R.id.menu_search ->

View File

@@ -0,0 +1,143 @@
package com.kunzisoft.keepass.adapters
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.icons.IconDrawableFactory
import com.kunzisoft.keepass.settings.PreferencesUtil
class BreadcrumbAdapter(val context: Context)
: RecyclerView.Adapter<BreadcrumbAdapter.BreadcrumbGroupViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
var iconDrawableFactory: IconDrawableFactory? = null
@SuppressLint("NotifyDataSetChanged")
set(value) {
field = value
notifyDataSetChanged()
}
private var mNodeBreadcrumb: MutableList<Node?> = mutableListOf()
var onItemClickListener: ((item: Node, position: Int)->Unit)? = null
private var mShowNumberEntries = false
private var mShowUUID = false
private var mIconColor: Int = 0
init {
mShowNumberEntries = PreferencesUtil.showNumberEntries(context)
mShowUUID = PreferencesUtil.showUUID(context)
// Retrieve the textColor to tint the icon
val taTextColor = context.theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse))
mIconColor = taTextColor.getColor(0, Color.WHITE)
taTextColor.recycle()
}
@SuppressLint("NotifyDataSetChanged")
fun setNode(node: Node?) {
mNodeBreadcrumb.clear()
node?.let {
var currentNode = it
mNodeBreadcrumb.add(0, currentNode)
while (currentNode.containsParent()) {
currentNode.parent?.let { parent ->
currentNode = parent
mNodeBreadcrumb.add(0, currentNode)
}
}
}
notifyDataSetChanged()
}
override fun getItemViewType(position: Int): Int {
return when (position) {
mNodeBreadcrumb.size - 1 -> 0
else -> 1
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BreadcrumbGroupViewHolder {
return BreadcrumbGroupViewHolder(inflater.inflate(
when (viewType) {
0 -> R.layout.item_group
else -> R.layout.item_breadcrumb
}, parent, false)
)
}
override fun onBindViewHolder(holder: BreadcrumbGroupViewHolder, position: Int) {
val node = mNodeBreadcrumb[position]
holder.groupNameView.apply {
text = when {
node == null -> ""
node.title.isEmpty() -> context.getString(R.string.root)
else -> node.title
}
}
holder.itemView.setOnClickListener {
node?.let {
onItemClickListener?.invoke(it, position)
}
}
if (node?.type == Type.GROUP) {
(node as Group).let { group ->
holder.groupIconView?.let { imageView ->
iconDrawableFactory?.assignDatabaseIcon(
imageView,
group.icon,
mIconColor
)
}
holder.groupNumbersView?.apply {
if (mShowNumberEntries) {
group.refreshNumberOfChildEntries(Group.ChildFilter.getDefaults(context))
text = group.numberOfChildEntries.toString()
visibility = View.VISIBLE
} else {
visibility = View.GONE
}
}
holder.groupMetaView?.apply {
val meta = group.nodeId.toString()
text = meta
visibility = if (meta.isNotEmpty()
&& !group.isVirtual
&& mShowUUID
) {
View.VISIBLE
} else {
View.GONE
}
}
}
}
}
override fun getItemCount(): Int {
return mNodeBreadcrumb.size
}
inner class BreadcrumbGroupViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var groupIconView: ImageView? = itemView.findViewById(R.id.group_icon)
var groupNumbersView: TextView? = itemView.findViewById(R.id.group_numbers)
var groupNameView: TextView = itemView.findViewById(R.id.group_name)
var groupMetaView: TextView? = itemView.findViewById(R.id.group_meta)
}
}

View File

@@ -79,6 +79,8 @@ class NodeAdapter (private val context: Context,
private var mShowOTP: Boolean = false
private var mShowUUID: Boolean = false
private var mEntryFilters = arrayOf<Group.ChildFilter>()
private var mOldVirtualGroup = false
private var mVirtualGroup = false
private var mActionNodesList = LinkedList<Node>()
private var mNodeClickCallback: NodeClickCallback? = null
@@ -145,6 +147,8 @@ class NodeAdapter (private val context: Context,
* Rebuild the list by clear and build children from the group
*/
fun rebuildList(group: Group) {
mOldVirtualGroup = mVirtualGroup
mVirtualGroup = group.isVirtual
assignPreferences()
mNodeSortedList.replaceAll(group.getFilteredChildren(mEntryFilters))
}
@@ -155,6 +159,8 @@ class NodeAdapter (private val context: Context,
}
override fun areContentsTheSame(oldItem: Node, newItem: Node): Boolean {
if (mOldVirtualGroup != mVirtualGroup)
return false
var typeContentTheSame = true
if (oldItem is Entry && newItem is Entry) {
typeContentTheSame = oldItem.getVisualTitle() == newItem.getVisualTitle()
@@ -356,6 +362,15 @@ class NodeAdapter (private val context: Context,
visibility = View.GONE
}
}
// Add path to virtual group
if (mVirtualGroup) {
holder.path?.apply {
text = subNode.getPathString()
visibility = View.VISIBLE
}
} else {
holder.path?.visibility = View.GONE
}
// Specific elements for entry
if (subNode.type == Type.ENTRY) {
@@ -497,6 +512,7 @@ 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 path: TextView? = itemView.findViewById(R.id.node_path)
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)

View File

@@ -32,6 +32,19 @@ interface Node: NodeVersionedInterface<Group> {
fun removeParent() {
parent = null
}
fun getPathString(): String {
val pathNodes = mutableListOf<Node>()
var currentNode = this
pathNodes.add(0, currentNode)
while (currentNode.containsParent()) {
currentNode.parent?.let { parent ->
currentNode = parent
pathNodes.add(0, currentNode)
}
}
return pathNodes.joinToString("/") { it.title }
}
}
/**

View File

@@ -31,11 +31,66 @@
android:layout_height="wrap_content"
android:theme="?attr/toolbarSpecialAppearance" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:title="@string/app_name"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_below="@+id/special_mode_view"
android:background="?attr/colorPrimary"
android:theme="?attr/toolbarAppearance" >
<TextView
android:id="@+id/database_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Database"
style="@style/KeepassDXStyle.TextAppearance.Title.TextOnPrimary" />
<RelativeLayout
android:id="@+id/search_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:orientation="vertical">
<TextView
android:id="@+id/search_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/search_results"
style="@style/KeepassDXStyle.TextAppearance.Default.TextOnPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/search_numbers"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="3"
android:layout_margin="4dp"
android:layout_below="@+id/search_title"
style="@style/KeepassDXStyle.TextAppearance.Info" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/search_string"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/search"
android:maxLines="1"
android:ellipsize="end"
android:layout_marginTop="2dp"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_below="@+id/search_title"
android:layout_toRightOf="@+id/search_numbers"
android:layout_toEndOf="@+id/search_numbers"
style="@style/KeepassDXStyle.TextAppearance.Title.TextOnPrimary" />
</RelativeLayout>
</androidx.appcompat.widget.Toolbar>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/group_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/special_mode_view"
android:layout_below="@+id/toolbar"
android:layout_above="@+id/toolbar_action">
<com.google.android.material.appbar.AppBarLayout
@@ -43,84 +98,22 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:targetApi="lollipop"
android:elevation="4dp"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:title="@string/app_name"
android:id="@+id/toolbar_breadcrumb"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="?attr/toolbarAppearance"
android:elevation="4dp"
app:layout_scrollFlags="scroll|snap|enterAlways"
tools:targetApi="lollipop">
<LinearLayout
android:id="@+id/group_header"
android:layout_width="wrap_content"
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/breadcrumb_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/toolbar"
android:orientation="vertical">
<TextView android:id="@+id/search_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/search_results"
android:visibility="gone"
style="@style/KeepassDXStyle.TextAppearance.Default.TextOnPrimary" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:baselineAligned="false">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/group_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end|center_vertical"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:scaleType="fitXY" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/group_numbers"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="3"
style="@style/KeepassDXStyle.TextAppearance.Info" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="start|center_vertical"
android:layout_marginLeft="14dp"
android:layout_marginStart="14dp"
android:layout_weight="1">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/group_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/root"
android:maxLines="2"
android:ellipsize="end"
style="@style/KeepassDXStyle.TextAppearance.Title.TextOnPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/group_meta"
android:layout_height="match_parent"
android:layout_width="match_parent"
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C"
android:lines="1"
android:singleLine="true"
style="@style/KeepassDXStyle.TextAppearance.Meta.TextOnPrimary" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:orientation="horizontal" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
@@ -159,6 +152,7 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"

View File

@@ -57,7 +57,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="144dp"
android:layout_marginTop="?attr/actionBarSize">
android:layout_marginTop="?attr/actionBarSize"
android:background="?attr/colorPrimary">
<ImageView
android:layout_width="96dp"
android:layout_height="96dp"

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2021 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/>.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/breadcrumb_group"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical"
android:baselineAligned="false"
android:background="?android:attr/selectableItemBackground">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/group_icon"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_marginStart="6dp"
android:layout_marginLeft="6dp"
android:layout_marginEnd="6dp"
android:layout_marginRight="6dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/hint_icon_name"
android:scaleType="fitXY" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/group_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:text="@string/root"
android:maxLines="2"
android:ellipsize="end"
style="@style/KeepassDXStyle.TextAppearance.SubTitle.TextOnPrimary"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/group_separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow_right_white_24dp"
android:tint="?attr/textColorInverse"
android:importantForAccessibility="no"
android:layout_marginEnd="6dp"
android:layout_marginRight="6dp"
style="@style/KeepassDXStyle.TextAppearance.SubTitle.TextOnPrimary"
android:textStyle="bold"
tools:targetApi="jelly_bean" />
</LinearLayout>

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2021 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/>.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/breadcrumb_group"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical"
android:baselineAligned="false"
android:background="?android:attr/selectableItemBackground">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:layout_marginRight="6dp"
android:gravity="center_vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/group_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end|center_vertical"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:scaleType="fitXY" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/group_numbers"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="3"
style="@style/KeepassDXStyle.TextAppearance.Info" />
</RelativeLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="start|center_vertical"
android:layout_marginEnd="6dp"
android:layout_marginRight="6dp"
android:layout_weight="1">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/group_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/root"
android:maxLines="2"
android:ellipsize="end"
style="@style/KeepassDXStyle.TextAppearance.Title.TextOnPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/group_meta"
android:layout_height="match_parent"
android:layout_width="match_parent"
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C"
android:lines="1"
android:singleLine="true"
style="@style/KeepassDXStyle.TextAppearance.Meta.TextOnPrimary" />
</LinearLayout>
</LinearLayout>

View File

@@ -101,6 +101,15 @@
android:lines="1"
android:singleLine="true"
tools:text="7543A7EAB2EA7CFD1394F1615EBEB08C" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/node_path"
style="@style/KeepassDXStyle.TextAppearance.Entry.Meta"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:visibility="gone"
tools:text="Database / Group A / Group B" />
</LinearLayout>
<LinearLayout

View File

@@ -298,6 +298,8 @@
</style>
<style name="KeepassDXStyle.TextAppearance.Default.TextOnPrimary" parent="android:style/TextAppearance">
<item name="android:textColor">?attr/textColorInverse</item>
<item name="drawableTint">?attr/textColorInverse</item>
<item name="android:drawablePadding">8dp</item>
</style>
<style name="KeepassDXStyle.TextAppearance.Secondary.TextOnPrimary" parent="KeepassDXStyle.TextAppearance.Default.TextOnPrimary">
<item name="android:textColor">?android:attr/textColorHintInverse</item>
@@ -310,9 +312,13 @@
<item name="android:textStyle">italic</item>
</style>
<style name="KeepassDXStyle.TextAppearance.Title.TextOnPrimary" parent="KeepassDXStyle.TextAppearance.Default.TextOnPrimary">
<item name="android:textSize">18sp</item>
<item name="android:textSize">16sp</item>
<item name="android:textStyle">bold</item>
</style>
<style name="KeepassDXStyle.TextAppearance.SubTitle.TextOnPrimary" parent="KeepassDXStyle.TextAppearance.Default.TextOnPrimary">
<item name="android:textSize">14sp</item>
<item name="android:textStyle">normal</item>
</style>
<style name="KeepassDXStyle.TextAppearance.Meta.TextOnPrimary" parent="KeepassDXStyle.TextAppearance.Default.TextOnPrimary">
<item name="android:textSize">11sp</item>
</style>

View File

@@ -0,0 +1,2 @@
* Add breadcrumb
* Add path in search results #1148

View File

@@ -0,0 +1,2 @@
* Ajout d'un fil d'ariane
* Ajout du chemin pour les résultats de recherche #1148