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 package com.nextcloud.talk.controllers.base
import android.app.Activity
import android.content.ComponentCallbacks import android.content.ComponentCallbacks
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -33,7 +36,14 @@ import android.view.inputmethod.InputMethodManager
import android.widget.EditText import android.widget.EditText
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.ActionBar 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.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.AccountVerificationController
import com.nextcloud.talk.controllers.ServerSelectionController import com.nextcloud.talk.controllers.ServerSelectionController
import com.nextcloud.talk.controllers.SwitchAccountController 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.controllers.base.providers.ActionBarProvider
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.uber.autodispose.lifecycle.LifecycleScopeProvider 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.greenrobot.eventbus.EventBus
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.util.* import java.util.*
@ -68,15 +80,39 @@ abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { return when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {
router.popCurrentController() 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() { private fun cleanTempCertPreference() {
val temporaryClassNames = ArrayList<String>() val temporaryClassNames = ArrayList<String>()
temporaryClassNames.add(ServerSelectionController::class.java.name) temporaryClassNames.add(ServerSelectionController::class.java.name)
@ -95,16 +131,22 @@ abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
cleanTempCertPreference() cleanTempCertPreference()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.isKeyboardIncognito) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.isKeyboardIncognito) {
disableKeyboardPersonalisedLearning(view as ViewGroup) disableKeyboardPersonalisedLearning(view as ViewGroup)
activity?.let {
if (it is MainActivity && getIsUsingSearchLayout()) {
disableKeyboardPersonalisedLearning(it.appBar)
}
}
} }
} }
override fun onAttach(view: View) { override fun onAttach(view: View) {
super.onAttach(view) super.onAttach(view)
setTitle() setTitle()
if (actionBar != null) { actionBar?.setDisplayHomeAsUpEnabled(parentController != null || router.backstackSize > 1)
actionBar!!.setDisplayHomeAsUpEnabled(parentController != null || router.backstackSize > 1) showSearchOrToolbar()
}
} }
override fun onDetach(view: View) { override fun onDetach(view: View) {
@ -159,4 +201,7 @@ abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
open fun getTitle(): String? { open fun getTitle(): String? {
return null return null
} }
open fun getIsUsingSearchLayout(): Boolean = false
open fun getSearchHint(): String? = null
} }

View File

@ -100,7 +100,10 @@ class Conversation {
@JsonIgnore @JsonIgnore
var changing: Boolean = false var changing: Boolean = false
@JsonIgnore
val isPublic: Boolean = ConversationType.PUBLIC_CONVERSATION == type val isPublic: Boolean = ConversationType.PUBLIC_CONVERSATION == type
@JsonIgnore
val isGuest: Boolean = val isGuest: Boolean =
Participant.ParticipantType.GUEST == participantType || Participant.ParticipantType.GUEST == participantType ||
Participant.ParticipantType.USER_FOLLOWING_LINK == participantType Participant.ParticipantType.USER_FOLLOWING_LINK == participantType
@ -155,6 +158,48 @@ class Conversation {
) || type != ConversationType.ONE_TO_ONE_CONVERSATION && participants!!.size > 1 ) || 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 { enum class NotificationLevel {
DEFAULT, DEFAULT,
ALWAYS, ALWAYS,

View File

@ -55,8 +55,12 @@ import com.nextcloud.talk.utils.bundle.BundleKeys
import com.otaliastudios.elements.* import com.otaliastudios.elements.*
import com.uber.autodispose.lifecycle.LifecycleScopeProvider import com.uber.autodispose.lifecycle.LifecycleScopeProvider
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager 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.controller_conversations_rv.view.*
import kotlinx.android.synthetic.main.message_state.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.koin.android.ext.android.inject
import org.parceler.Parcels import org.parceler.Parcels
import java.util.* import java.util.*
@ -68,50 +72,6 @@ class ConversationsListView : BaseView() {
private lateinit var viewModel: ConversationsListViewModel private lateinit var viewModel: ConversationsListViewModel
val factory: ConversationListViewModelFactory by inject() 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() { /*private fun initSearchView() {
val searchManager = activity!!.getSystemService(Context.SEARCH_SERVICE) as SearchManager val searchManager = activity!!.getSystemService(Context.SEARCH_SERVICE) as SearchManager
searchView = MenuItemCompat.getActionView(searchItem) as SearchView searchView = MenuItemCompat.getActionView(searchItem) as SearchView
@ -144,7 +104,6 @@ class ConversationsListView : BaseView() {
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup container: ViewGroup
): View { ): View {
setHasOptionsMenu(true)
actionBar?.show() actionBar?.show()
viewModel = viewModelProvider(factory).get(ConversationsListViewModel::class.java) viewModel = viewModelProvider(factory).get(ConversationsListViewModel::class.java)
@ -172,8 +131,26 @@ class ConversationsListView : BaseView() {
swipeRefreshLayoutView.setColorSchemeResources(R.color.colorPrimary) 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 -> viewModel.avatar.observe(this@ConversationsListView) { avatar ->
settingsItem?.icon = avatar activity?.rightButton?.setImageDrawable(avatar)
} }
return view return view
@ -321,6 +298,14 @@ class ConversationsListView : BaseView() {
return items return items
} }
override fun getIsUsingSearchLayout(): Boolean {
return true
}
override fun getSearchHint(): String? {
return resources?.getString(R.string.nc_search_conversations)
}
override fun getTitle(): String? { override fun getTitle(): String? {
return resources?.getString(R.string.nc_app_name) 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 <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" 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 <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
app:contentInsetStart="24dp" android:layout_height="?android:attr/actionBarSize"
app:contentInsetStartWithNavigation="0dp" android:background="@color/transparent"
app:popupTheme="@style/appActionBarPopupMenu" /> 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> </com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -38,7 +38,8 @@
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="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> </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_never">Never joined</string>
<string name="nc_search">Search</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_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> <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>