mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'feature/Breadcrumb' into develop
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
66
app/src/main/res/layout/item_breadcrumb.xml
Normal file
66
app/src/main/res/layout/item_breadcrumb.xml
Normal 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>
|
||||
76
app/src/main/res/layout/item_group.xml
Normal file
76
app/src/main/res/layout/item_group.xml
Normal 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>
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
2
fastlane/metadata/android/en-US/changelogs/92.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/92.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
* Add breadcrumb
|
||||
* Add path in search results #1148
|
||||
2
fastlane/metadata/android/fr-FR/changelogs/92.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/92.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
* Ajout d'un fil d'ariane
|
||||
* Ajout du chemin pour les résultats de recherche #1148
|
||||
Reference in New Issue
Block a user