Improvements & new search

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2020-01-06 21:09:44 +01:00
parent 93fb37a340
commit 689b8e93af
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
9 changed files with 297 additions and 308 deletions

View File

@ -1,249 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.adapters.items
import android.content.Context
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.text.format.DateUtils
import android.util.Log
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import coil.api.load
import coil.transform.CircleCropTransformation
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType.ONE_TO_ONE_CONVERSATION
import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.newarch.local.models.getCredentials
import com.nextcloud.talk.newarch.utils.Images
import com.nextcloud.talk.utils.ApiUtils
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFilterable
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.flexibleadapter.utils.FlexibleUtils
import eu.davidea.viewholders.FlexibleViewHolder
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.rv_item_conversation_with_last_message.*
import kotlinx.android.synthetic.main.rv_item_conversation_with_last_message.view.*
import java.util.*
import java.util.regex.Pattern
class ConversationItem(
val model: Conversation,
val user: UserNgEntity,
private val context: Context
) : AbstractFlexibleItem<ConversationItem.ConversationItemViewHolder>(), IFilterable<String> {
override fun equals(other: Any?): Boolean {
if (other is ConversationItem) {
val inItem = other as ConversationItem?
val comparedConversation = inItem!!.model
return (model.conversationId == comparedConversation.conversationId
&& model.token == comparedConversation.token
&& model.name == comparedConversation.name
&& model.displayName == comparedConversation.displayName
&& model.type == comparedConversation.type
&& model.lastMessage == comparedConversation.lastMessage
&& model.favorite == comparedConversation.favorite
&& model.hasPassword == comparedConversation.hasPassword
&& model.unreadMessages == comparedConversation.unreadMessages
&& model.unreadMention == comparedConversation.unreadMention
&& model.objectType == comparedConversation.objectType
&& model.changing == comparedConversation.changing
&& inItem.user.id == user.id)
}
return false
}
override fun hashCode(): Int {
return Objects.hash(
model.token,
user.id
)
}
override fun getLayoutRes(): Int {
return R.layout.rv_item_conversation_with_last_message
}
override fun createViewHolder(
view: View,
adapter: FlexibleAdapter<IFlexible<*>>
): ConversationItemViewHolder {
return ConversationItemViewHolder(view, adapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>,
holder: ConversationItemViewHolder,
position: Int,
payloads: List<Any>
) {
val appContext = NextcloudTalkApplication.sharedApplication!!.applicationContext
if (model.changing) {
holder.actionProgressBar!!.visibility = View.VISIBLE
} else {
holder.actionProgressBar!!.visibility = View.GONE
}
if (adapter.hasFilter()) {
FlexibleUtils.highlightText(
holder.dialogName!!, model.displayName,
adapter.getFilter(String::class.java).toString(),
NextcloudTalkApplication.sharedApplication!!
.resources.getColor(R.color.colorPrimary)
)
} else {
holder.dialogName!!.text = model.displayName
}
if (model.unreadMessages > 0) {
holder.dialogUnreadBubble!!.visibility = View.VISIBLE
if (model.unreadMessages < 100) {
holder.dialogUnreadBubble!!.text = model.unreadMessages.toLong()
.toString()
} else {
holder.dialogUnreadBubble!!.text = context.getString(R.string.nc_99_plus)
}
if (model.unreadMention) {
holder.dialogUnreadBubble!!.background =
context.getDrawable(R.drawable.bubble_circle_unread_mention)
} else {
holder.dialogUnreadBubble!!.background =
context.getDrawable(R.drawable.bubble_circle_unread)
}
} else {
holder.dialogUnreadBubble!!.visibility = View.GONE
}
if (model.hasPassword) {
holder.passwordProtectedRoomImageView!!.visibility = View.VISIBLE
} else {
holder.passwordProtectedRoomImageView!!.visibility = View.GONE
}
if (model.favorite) {
holder.favoriteConversationImageView!!.visibility = View.VISIBLE
} else {
holder.favoriteConversationImageView!!.visibility = View.GONE
}
if (model.lastMessage != null) {
holder.dialogDate!!.visibility = View.VISIBLE
holder.dialogDate!!.text = DateUtils.getRelativeTimeSpanString(
model.lastActivity * 1000L,
System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE
)
if (!TextUtils.isEmpty(
model.lastMessage!!.systemMessage
) || Conversation.ConversationType.SYSTEM_CONVERSATION == model.type
) {
holder.dialogLastMessage!!.text = model.lastMessage!!.text
} else {
var authorDisplayName = ""
model.lastMessage!!.activeUser = user
val text: String
if (model.lastMessage!!
.messageType == ChatMessage.MessageType.REGULAR_TEXT_MESSAGE && (!(ONE_TO_ONE_CONVERSATION).equals(
model.type) || model.lastMessage!!.actorId == user.userId)
) {
if (model.lastMessage!!.actorId == user.userId) {
text = String.format(
appContext.getString(R.string.nc_formatted_message_you),
model.lastMessage!!.lastMessageDisplayText
)
} else {
authorDisplayName = if (!TextUtils.isEmpty(model.lastMessage!!.actorDisplayName))
model.lastMessage!!.actorDisplayName
else if ("guests" == model.lastMessage!!.actorType)
appContext.getString(R.string.nc_guest)
else
""
text = String.format(
appContext.getString(R.string.nc_formatted_message),
authorDisplayName,
model.lastMessage!!.lastMessageDisplayText
)
}
} else {
text = model.lastMessage!!.lastMessageDisplayText
}
holder.dialogLastMessage.text = text
}
} else {
holder.dialogDate.visibility = View.GONE
holder.dialogLastMessage.setText(R.string.nc_no_messages_yet)
}
val conversationDrawable: Drawable? = Images().getImageForConversation(context, model)
conversationDrawable?.let {
holder.itemView.dialogAvatar.load(conversationDrawable)
}?: run {
holder.itemView.dialogAvatar.load(ApiUtils.getUrlForAvatarWithName(
user.baseUrl,
model.name, R.dimen.avatar_size))
{
addHeader("Authorization", user.getCredentials())
transformations(CircleCropTransformation())
}
}
}
override fun onViewAttached(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?, holder: ConversationItemViewHolder?, position: Int) {
super.onViewAttached(adapter, holder, position)
Log.d("MAriO", model.displayName!!)
}
override fun onViewDetached(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?, holder: ConversationItemViewHolder?, position: Int) {
super.onViewDetached(adapter, holder, position)
Log.d("MAriO DETACH", model.displayName!!)
}
override fun filter(constraint: String): Boolean {
return model.displayName != null && Pattern.compile(
constraint, Pattern.CASE_INSENSITIVE or Pattern.LITERAL
)
.matcher(model.displayName!!.trim { it <= ' ' })
.find()
}
class ConversationItemViewHolder(
view: View,
adapter: FlexibleAdapter<*>
) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View?
get() = itemView
}
}

View File

@ -20,11 +20,14 @@
package com.nextcloud.talk.controllers.base
import android.app.Activity
import android.content.ComponentCallbacks
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
@ -33,7 +36,14 @@ import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.annotation.RequiresApi
import androidx.appcompat.app.ActionBar
import androidx.core.view.isVisible
import androidx.core.view.marginBottom
import androidx.core.view.updatePadding
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MainActivity
import com.nextcloud.talk.controllers.AccountVerificationController
import com.nextcloud.talk.controllers.ServerSelectionController
import com.nextcloud.talk.controllers.SwitchAccountController
@ -41,6 +51,8 @@ import com.nextcloud.talk.controllers.WebViewLoginController
import com.nextcloud.talk.controllers.base.providers.ActionBarProvider
import com.nextcloud.talk.utils.preferences.AppPreferences
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.search_layout.*
import org.greenrobot.eventbus.EventBus
import org.koin.android.ext.android.inject
import java.util.*
@ -68,15 +80,39 @@ abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
return when (item.itemId) {
android.R.id.home -> {
router.popCurrentController()
return true
true
}
else -> return super.onOptionsItemSelected(item)
else -> super.onOptionsItemSelected(item)
}
}
private fun showSearchOrToolbar() {
val value = getIsUsingSearchLayout()
activity?.let {
if (it is MainActivity) {
it.searchCardView.isVisible = value
it.inputEditText.hint = getSearchHint()
val layoutParams = it.toolbar.layoutParams as AppBarLayout.LayoutParams
if (value) {
it.appBar.setBackgroundResource(R.color.transparent)
//it.toolbar.setContentInsetsAbsolute(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16f, resources?.displayMetrics).toInt(), 0)
//layoutParams.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS or AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
it.toolbar.layoutParams = layoutParams
} else {
it.appBar.setBackgroundResource(R.color.colorPrimary)
//it.toolbar.setContentInsetsAbsolute(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24f, resources?.displayMetrics).toInt(), 0)
layoutParams.scrollFlags = 0
it.toolbar.layoutParams = layoutParams
}
}
}
}
private fun cleanTempCertPreference() {
val temporaryClassNames = ArrayList<String>()
temporaryClassNames.add(ServerSelectionController::class.java.name)
@ -95,16 +131,22 @@ abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
cleanTempCertPreference()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.isKeyboardIncognito) {
disableKeyboardPersonalisedLearning(view as ViewGroup)
activity?.let {
if (it is MainActivity && getIsUsingSearchLayout()) {
disableKeyboardPersonalisedLearning(it.appBar)
}
}
}
}
override fun onAttach(view: View) {
super.onAttach(view)
setTitle()
if (actionBar != null) {
actionBar!!.setDisplayHomeAsUpEnabled(parentController != null || router.backstackSize > 1)
}
actionBar?.setDisplayHomeAsUpEnabled(parentController != null || router.backstackSize > 1)
showSearchOrToolbar()
}
override fun onDetach(view: View) {
@ -159,4 +201,7 @@ abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
open fun getTitle(): String? {
return null
}
open fun getIsUsingSearchLayout(): Boolean = false
open fun getSearchHint(): String? = null
}

View File

@ -100,7 +100,10 @@ class Conversation {
@JsonIgnore
var changing: Boolean = false
@JsonIgnore
val isPublic: Boolean = ConversationType.PUBLIC_CONVERSATION == type
@JsonIgnore
val isGuest: Boolean =
Participant.ParticipantType.GUEST == participantType ||
Participant.ParticipantType.USER_FOLLOWING_LINK == participantType
@ -155,6 +158,48 @@ class Conversation {
) || type != ConversationType.ONE_TO_ONE_CONVERSATION && participants!!.size > 1
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Conversation
if (databaseId != other.databaseId) return false
if (databaseUserId != other.databaseUserId) return false
if (conversationId != other.conversationId) return false
if (token != other.token) return false
if (name != other.name) return false
if (displayName != other.displayName) return false
if (type != other.type) return false
if (count != other.count) return false
if (numberOfGuests != other.numberOfGuests) return false
if (participants != other.participants) return false
if (participantType != other.participantType) return false
if (hasPassword != other.hasPassword) return false
if (password != other.password) return false
if (favorite != other.favorite) return false
if (lastActivity != other.lastActivity) return false
if (unreadMessages != other.unreadMessages) return false
if (unreadMention != other.unreadMention) return false
if (lastMessage != other.lastMessage) return false
if (objectType != other.objectType) return false
if (notificationLevel != other.notificationLevel) return false
if (conversationReadOnlyState != other.conversationReadOnlyState) return false
if (lobbyState != other.lobbyState) return false
if (lobbyTimer != other.lobbyTimer) return false
if (lastReadMessageId != other.lastReadMessageId) return false
if (canStartCall != other.canStartCall) return false
if (changing != other.changing) return false
return true
}
override fun hashCode(): Int {
var result = databaseUserId?.hashCode() ?: 0
result = 31 * result + (token?.hashCode() ?: 0)
return result
}
enum class NotificationLevel {
DEFAULT,
ALWAYS,

View File

@ -55,8 +55,12 @@ import com.nextcloud.talk.utils.bundle.BundleKeys
import com.otaliastudios.elements.*
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main.view.*
import kotlinx.android.synthetic.main.controller_conversations_rv.view.*
import kotlinx.android.synthetic.main.message_state.view.*
import kotlinx.android.synthetic.main.search_layout.*
import kotlinx.android.synthetic.main.search_layout.view.*
import org.koin.android.ext.android.inject
import org.parceler.Parcels
import java.util.*
@ -68,50 +72,6 @@ class ConversationsListView : BaseView() {
private lateinit var viewModel: ConversationsListViewModel
val factory: ConversationListViewModelFactory by inject()
private var searchItem: MenuItem? = null
private var settingsItem: MenuItem? = null
private var searchView: SearchView? = null
override fun onCreateOptionsMenu(
menu: Menu,
inflater: MenuInflater
) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_conversation_plus_filter, menu)
searchItem = menu.findItem(R.id.action_search)
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
settingsItem = menu.findItem(R.id.action_settings)
settingsItem?.actionView?.transitionName = "userAvatar.transitionTag"
viewModel.loadAvatar()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_settings -> {
val names = ArrayList<String>()
names.add("userAvatar.transitionTag")
router.pushController(
RouterTransaction.with(SettingsController())
.pushChangeHandler(
TransitionChangeHandlerCompat(
SharedElementTransition(names), VerticalChangeHandler()
)
)
.popChangeHandler(
TransitionChangeHandlerCompat(
SharedElementTransition(names), VerticalChangeHandler()
)
)
)
return true
}
else -> return super.onOptionsItemSelected(item)
}
}
/*private fun initSearchView() {
val searchManager = activity!!.getSystemService(Context.SEARCH_SERVICE) as SearchManager
searchView = MenuItemCompat.getActionView(searchItem) as SearchView
@ -144,7 +104,6 @@ class ConversationsListView : BaseView() {
inflater: LayoutInflater,
container: ViewGroup
): View {
setHasOptionsMenu(true)
actionBar?.show()
viewModel = viewModelProvider(factory).get(ConversationsListViewModel::class.java)
@ -172,8 +131,26 @@ class ConversationsListView : BaseView() {
swipeRefreshLayoutView.setColorSchemeResources(R.color.colorPrimary)
}
activity?.rightButton?.setOnClickListener {
val settingsTransitionName = "userAvatar.transitionTag"
router.pushController(
RouterTransaction.with(SettingsController())
.pushChangeHandler(
TransitionChangeHandlerCompat(
SharedElementTransition(arrayListOf(settingsTransitionName)), VerticalChangeHandler()
)
)
.popChangeHandler(
TransitionChangeHandlerCompat(
SharedElementTransition(arrayListOf(settingsTransitionName)), VerticalChangeHandler()
)
)
)
}
viewModel.avatar.observe(this@ConversationsListView) { avatar ->
settingsItem?.icon = avatar
activity?.rightButton?.setImageDrawable(avatar)
}
return view
@ -321,6 +298,14 @@ class ConversationsListView : BaseView() {
return items
}
override fun getIsUsingSearchLayout(): Boolean {
return true
}
override fun getSearchHint(): String? {
return resources?.getString(R.string.nc_search_conversations)
}
override fun getTitle(): String? {
return resources?.getString(R.string.nc_app_name)
}

View File

@ -0,0 +1,28 @@
<!--
~ /*
~ * Nextcloud Talk application
~ *
~ * @author Mario Danic
~ * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
~ *
~ * This program 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.
~ *
~ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
~ */
-->
<vector android:autoMirrored="true" android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -33,16 +33,27 @@
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:id="@+id/appBar"
app:elevation="0dp"
>
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
app:contentInsetStart="24dp"
app:contentInsetStartWithNavigation="0dp"
app:popupTheme="@style/appActionBarPopupMenu" />
android:layout_height="?android:attr/actionBarSize"
android:background="@color/transparent"
app:elevation="0dp"
app:contentInsetEnd="12dp"
app:contentInsetStart="12dp"
app:popupTheme="@style/appActionBarPopupMenu">
<include layout="@layout/search_layout"/>
</com.google.android.material.appbar.MaterialToolbar>>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -38,7 +38,8 @@
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/rv_item_conversation_with_last_message" />
tools:listitem="@layout/rv_item_conversation_with_last_message"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ /*
~ * Nextcloud Talk application
~ *
~ * @author Mario Danic
~ * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
~ *
~ * This program 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.
~ *
~ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
~ */
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
>
<androidx.cardview.widget.CardView
android:id="@+id/searchCardView"
android:layout_width="match_parent"
android:layout_height="48dp"
android:clickable="true"
android:focusable="true"
android:layout_marginVertical="8dp"
android:background="@color/transparent"
app:cardUseCompatPadding="false"
app:cardBackgroundColor="@color/transparent"
app:cardElevation="0dp"
android:elevation="0dp"
app:cardCornerRadius="4dp"
tools:cardCornerRadius="4dp"
tools:cardElevation="0dp"
android:clipToPadding="true"
android:clipChildren="true"
android:animateLayoutChanges="true"
android:layout_centerHorizontal="true">
<RelativeLayout
android:layout_width="match_parent"
android:clipToPadding="true"
android:layout_height="wrap_content"
android:background="@color/colorPrimary">
<FrameLayout
android:id="@+id/leftContainer"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginStart="16dp"
android:layout_centerVertical="true">
<ImageButton
android:id="@+id/leftButton"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:layout_gravity="center"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="fitCenter"
android:tint="@color/fg_inverse"
android:src="@drawable/ic_arrow_back_black_24dp"
android:visibility="visible"
tools:src="@drawable/ic_arrow_back_black_24dp" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:indeterminateTint="@color/colorPrimary"
android:visibility="gone" />
</FrameLayout>
<EditText
android:id="@+id/inputEditText"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center"
android:layout_toEndOf="@id/leftContainer"
android:layout_alignWithParentIfMissing="true"
android:inputType="textNoSuggestions"
android:layout_marginStart="16dp"
android:imeOptions="actionSearch|flagNoExtractUi"
android:lines="1"
android:layout_marginEnd="16dp"
android:maxLines="1"
android:background="@null"
android:textSize="16sp"
tools:hint="Search"
android:layout_toStartOf="@id/rightButton"/>
<ImageView
android:id="@+id/rightButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="16dp"
android:layout_alignParentEnd="true"
android:transitionName="userAvatar.transitionTag"
android:layout_centerVertical="true"
android:scaleType="fitCenter"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_settings_white_24dp"
tools:src="@tools:sample/avatars[0]"
android:visibility="visible" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</merge>

View File

@ -46,6 +46,7 @@
<string name="nc_never">Never joined</string>
<string name="nc_search">Search</string>
<string name="nc_search_conversations">Search conversations</string>
<string name="nc_certificate_dialog_title">Check out the certificate</string>
<string name="nc_certificate_dialog_text">Do you trust the until now unknown SSL certificate, issued by %1$s for %2$s, valid from %3$s to %4$s?</string>