WIP: add new screen for conversation info editing

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2023-04-24 17:20:16 +02:00
parent 84008a40dc
commit 3dc3bf0cf8
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
5 changed files with 489 additions and 228 deletions

View File

@ -214,6 +214,10 @@
android:name=".conversation.info.ConversationInfoActivity"
android:theme="@style/AppTheme" />
<activity
android:name=".conversation.info.ConversationInfoEditActivity"
android:theme="@style/AppTheme" />
<activity
android:name=".contacts.ContactsActivity"
android:theme="@style/AppTheme" />

View File

@ -27,7 +27,6 @@
package com.nextcloud.talk.conversation.info
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
@ -42,8 +41,6 @@ import android.view.View.VISIBLE
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SwitchCompat
import androidx.core.net.toFile
import androidx.core.view.ViewCompat
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
@ -52,7 +49,6 @@ import com.afollestad.materialdialogs.LayoutMode.WRAP_CONTENT
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.datetime.dateTimePicker
import com.github.dhaval2404.imagepicker.ImagePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.BaseActivity
@ -85,9 +81,6 @@ import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateConstants
import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.Mimetype
import com.nextcloud.talk.utils.PickImage
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule
@ -97,12 +90,9 @@ import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.io.File
import org.parceler.Parcels
import java.util.Calendar
import java.util.Collections
import java.util.Locale
@ -137,11 +127,6 @@ class ConversationInfoActivity :
private var adapter: FlexibleAdapter<ParticipantItem>? = null
private var userItems: MutableList<ParticipantItem> = ArrayList()
private lateinit var optionsMenu: Menu
private var edit = false
private lateinit var pickImage: PickImage
private val workerData: Data?
get() {
if (!TextUtils.isEmpty(conversationToken) && conversationUser != null) {
@ -176,8 +161,6 @@ class ConversationInfoActivity :
databaseStorageModule = DatabaseStorageModule(conversationUser, conversationToken)
}
setupAvatarOptions()
binding.notificationSettingsView.notificationSettings.setStorageModule(databaseStorageModule)
binding.webinarInfoView.webinarSettings.setStorageModule(databaseStorageModule)
binding.guestAccessView.guestAccessSettings.setStorageModule(databaseStorageModule)
@ -203,22 +186,6 @@ class ConversationInfoActivity :
binding.progressBar.let { viewThemeUtils.platform.colorCircularProgressBar(it) }
}
private fun setupAvatarOptions() {
pickImage = PickImage(this, conversationUser)
binding.avatarUpload.setOnClickListener { pickImage.selectLocal() }
binding.avatarChoose.setOnClickListener { pickImage.selectRemote() }
binding.avatarCamera.setOnClickListener { pickImage.takePicture() }
binding.avatarDelete.setOnClickListener { deleteAvatar() }
binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") }
binding.let {
viewThemeUtils.material.themeFAB(it.avatarUpload)
viewThemeUtils.material.themeFAB(it.avatarChoose)
viewThemeUtils.material.themeFAB(it.avatarCamera)
viewThemeUtils.material.themeFAB(it.avatarDelete)
}
}
private fun setupActionBar() {
setSupportActionBar(binding.conversationInfoToolbar)
binding.conversationInfoToolbar.setNavigationOnClickListener {
@ -239,7 +206,6 @@ class ConversationInfoActivity :
super.onCreateOptionsMenu(menu)
if (CapabilitiesUtilNew.isConversationAvatarEndpointAvailable(conversationUser)) {
menuInflater.inflate(R.menu.menu_conversation_info, menu)
optionsMenu = menu
return true
}
return false
@ -248,13 +214,23 @@ class ConversationInfoActivity :
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
super.onPrepareOptionsMenu(menu)
// menu.findItem(R.id.edit).isVisible = editableFields.size > 0
setEditMode(false)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.edit) {
toggleEditMode()
val bundle = Bundle()
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
bundle.putParcelable(
BundleKeys.KEY_ACTIVE_CONVERSATION,
Parcels.wrap(conversation)
)
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken)
val intent = Intent(this, ConversationInfoEditActivity::class.java)
intent.putExtras(bundle)
startActivity(intent)
}
return true
}
@ -288,135 +264,6 @@ class ConversationInfoActivity :
}
}
private fun toggleEditMode() {
edit = !edit
if (edit) {
setEditMode(true)
} else {
setEditMode(false)
}
}
private fun setEditMode(editing: Boolean) {
if (editing) {
optionsMenu.findItem(R.id.edit).setTitle(R.string.save)
binding.avatarUpload.visibility = VISIBLE
binding.avatarChoose.visibility = VISIBLE
binding.avatarCamera.visibility = VISIBLE
binding.avatarDelete.visibility = VISIBLE
edit = true
} else {
optionsMenu.findItem(R.id.edit).setTitle(R.string.edit)
binding.avatarUpload.visibility = GONE
binding.avatarChoose.visibility = GONE
binding.avatarCamera.visibility = GONE
binding.avatarDelete.visibility = GONE
edit = false
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (resultCode) {
Activity.RESULT_OK -> {
pickImage.handleActivityResult(
requestCode,
resultCode,
data
) { uploadAvatar(it.toFile()) }
}
ImagePicker.RESULT_ERROR -> {
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
}
else -> {
Log.i(TAG, "Task Cancelled")
}
}
}
private fun uploadAvatar(file: File?) {
val builder = MultipartBody.Builder()
builder.setType(MultipartBody.FORM)
builder.addFormDataPart(
"file",
file!!.name,
file.asRequestBody(Mimetype.IMAGE_PREFIX_GENERIC.toMediaTypeOrNull())
)
val filePart: MultipartBody.Part = MultipartBody.Part.createFormData(
"file",
file.name,
file.asRequestBody(Mimetype.IMAGE_JPG.toMediaTypeOrNull())
)
// upload file
ncApi.uploadAvatar(
credentials,
ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversation!!.token),
filePart
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> {
override fun onSubscribe(d: Disposable) {
// unused atm
}
override fun onNext(genericOverall: GenericOverall) {
DisplayUtils.loadAvatarImage(conversationUser, binding.avatarImage, true)
}
override fun onError(e: Throwable) {
Toast.makeText(
applicationContext,
context.getString(R.string.default_error_msg),
Toast.LENGTH_LONG
).show()
Log.e(TAG, "Error uploading avatar", e)
}
override fun onComplete() {
setEditMode(false)
}
})
}
private fun deleteAvatar() {
ncApi.deleteAvatar(
credentials,
ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversationToken)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> {
override fun onSubscribe(d: Disposable) {
// unused atm
}
override fun onNext(genericOverall: GenericOverall) {
DisplayUtils.loadAvatarImage(
conversationUser,
binding.avatarImage,
true
)
}
override fun onError(e: Throwable) {
Toast.makeText(
applicationContext,
context.getString(R.string.default_error_msg),
Toast.LENGTH_LONG
).show()
Log.e(TAG, "Failed to delete avatar", e)
}
override fun onComplete() {
setEditMode(false)
}
})
}
private fun showSharedItems() {
val intent = Intent(this, SharedItemsActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@ -816,11 +663,9 @@ class ConversationInfoActivity :
} else {
binding?.clearConversationHistory?.visibility = GONE
}
binding.avatarButtons.visibility = VISIBLE
} else {
binding?.addParticipantsAction?.visibility = GONE
binding?.clearConversationHistory?.visibility = GONE
binding.avatarButtons.visibility = GONE
}
if (!isDestroyed) {

View File

@ -0,0 +1,327 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* @author Andy Scherzinger
* @author Tim Krüger
* @author Marcel Hibbe
* Copyright (C) 2022-2023 Marcel Hibbe (dev@mhibbe.de)
* Copyright (C) 2021-2022 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de)
* 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.conversation.info
import android.app.Activity
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.view.Menu
import android.widget.Toast
import androidx.core.net.toFile
import androidx.core.view.ViewCompat
import autodagger.AutoInjector
import com.github.dhaval2404.imagepicker.ImagePicker
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.BaseActivity
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.ActivityConversationInfoEditBinding
import com.nextcloud.talk.extensions.loadAvatar
import com.nextcloud.talk.extensions.loadConversationAvatar
import com.nextcloud.talk.extensions.loadSystemAvatar
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.repositories.conversations.ConversationsRepository
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.Mimetype
import com.nextcloud.talk.utils.PickImage
import com.nextcloud.talk.utils.bundle.BundleKeys
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import org.parceler.Parcels
import java.io.File
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class ConversationInfoEditActivity :
BaseActivity() {
private lateinit var binding: ActivityConversationInfoEditBinding
@Inject
lateinit var ncApi: NcApi
@Inject
lateinit var conversationsRepository: ConversationsRepository
@Inject
lateinit var dateUtils: DateUtils
private lateinit var conversationToken: String
private lateinit var conversationUser: User
private lateinit var credentials: String
private var conversation: Conversation? = null
private lateinit var optionsMenu: Menu
private var edit = false
private lateinit var pickImage: PickImage
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
binding = ActivityConversationInfoEditBinding.inflate(layoutInflater)
setupActionBar()
setContentView(binding.root)
setupSystemColors()
val extras: Bundle? = intent.extras
conversationUser = extras?.getParcelable(BundleKeys.KEY_USER_ENTITY)!!
conversationToken = extras.getString(BundleKeys.KEY_ROOM_TOKEN)!!
if (intent.hasExtra(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
conversation = Parcels.unwrap<Conversation>(extras.getParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION))
}
credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token)
}
override fun onResume() {
super.onResume()
loadConversationAvatar()
binding.displayNameText.text = conversation!!.displayName
if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) {
binding.descriptionText.text = conversation!!.description
}
setupAvatarOptions()
}
private fun setupAvatarOptions() {
pickImage = PickImage(this, conversationUser)
binding.avatarUpload.setOnClickListener { pickImage.selectLocal() }
binding.avatarChoose.setOnClickListener { pickImage.selectRemote() }
binding.avatarCamera.setOnClickListener { pickImage.takePicture() }
binding.avatarDelete.setOnClickListener { deleteAvatar() }
binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") }
binding.let {
viewThemeUtils.material.themeFAB(it.avatarUpload)
viewThemeUtils.material.themeFAB(it.avatarChoose)
viewThemeUtils.material.themeFAB(it.avatarCamera)
viewThemeUtils.material.themeFAB(it.avatarDelete)
}
}
private fun setupActionBar() {
setSupportActionBar(binding.conversationInfoEditToolbar)
binding.conversationInfoEditToolbar.setNavigationOnClickListener {
onBackPressed()
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(android.R.color.transparent)))
supportActionBar?.title = resources!!.getString(R.string.nc_conversation_menu_conversation_info)
viewThemeUtils.material.themeToolbar(binding.conversationInfoEditToolbar)
}
// override fun onCreateOptionsMenu(menu: Menu): Boolean {
// super.onCreateOptionsMenu(menu)
//
// menuInflater.inflate(R.menu.menu_conversation_info, menu)
// optionsMenu = menu
// return true
// }
//
// override fun onPrepareOptionsMenu(menu: Menu): Boolean {
// super.onPrepareOptionsMenu(menu)
// // menu.findItem(R.id.edit).isVisible = editableFields.size > 0
// setEditMode(true)
// return true
// }
//
// override fun onOptionsItemSelected(item: MenuItem): Boolean {
// if (item.itemId == R.id.edit) {
// toggleEditMode()
// }
// return true
// }
//
//
// private fun toggleEditMode() {
// edit = !edit
// if (edit) {
// setEditMode(true)
// } else {
// setEditMode(false)
// }
// }
//
// private fun setEditMode(editing: Boolean) {
// if (editing) {
// optionsMenu.findItem(R.id.edit).setTitle(R.string.save)
// edit = true
// } else {
// optionsMenu.findItem(R.id.edit).setTitle(R.string.edit)
// edit = false
// }
// }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (resultCode) {
Activity.RESULT_OK -> {
pickImage.handleActivityResult(
requestCode,
resultCode,
data
) { uploadAvatar(it.toFile()) }
}
ImagePicker.RESULT_ERROR -> {
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
}
else -> {
Log.i(TAG, "Task Cancelled")
}
}
}
private fun uploadAvatar(file: File?) {
val builder = MultipartBody.Builder()
builder.setType(MultipartBody.FORM)
builder.addFormDataPart(
"file",
file!!.name,
file.asRequestBody(Mimetype.IMAGE_PREFIX_GENERIC.toMediaTypeOrNull())
)
val filePart: MultipartBody.Part = MultipartBody.Part.createFormData(
"file",
file.name,
file.asRequestBody(Mimetype.IMAGE_JPG.toMediaTypeOrNull())
)
// upload file
ncApi.uploadAvatar(
credentials,
ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversation!!.token),
filePart
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> {
override fun onSubscribe(d: Disposable) {
// unused atm
}
override fun onNext(genericOverall: GenericOverall) {
DisplayUtils.loadAvatarImage(conversationUser, binding.avatarImage, true)
}
override fun onError(e: Throwable) {
Toast.makeText(
applicationContext,
context.getString(R.string.default_error_msg),
Toast.LENGTH_LONG
).show()
Log.e(TAG, "Error uploading avatar", e)
}
override fun onComplete() {
// setEditMode(false)
}
})
}
private fun deleteAvatar() {
ncApi.deleteAvatar(
credentials,
ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversationToken)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> {
override fun onSubscribe(d: Disposable) {
// unused atm
}
override fun onNext(genericOverall: GenericOverall) {
DisplayUtils.loadAvatarImage(
conversationUser,
binding.avatarImage,
true
)
}
override fun onError(e: Throwable) {
Toast.makeText(
applicationContext,
context.getString(R.string.default_error_msg),
Toast.LENGTH_LONG
).show()
Log.e(TAG, "Failed to delete avatar", e)
}
override fun onComplete() {
// setEditMode(false)
}
})
}
private fun loadConversationAvatar() {
when (conversation!!.type) {
Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) {
conversation!!.name?.let { binding.avatarImage.loadAvatar(conversationUser, it) }
}
Conversation.ConversationType.ROOM_GROUP_CALL, Conversation.ConversationType.ROOM_PUBLIC_CALL -> {
binding.avatarImage.loadConversationAvatar(conversationUser, conversation!!)
}
Conversation.ConversationType.ROOM_SYSTEM -> {
binding.avatarImage.loadSystemAvatar()
}
else -> {
// unused atm
}
}
}
companion object {
private const val TAG = "ConversationEditInfo"
}
}

View File

@ -106,66 +106,6 @@
</RelativeLayout>
<LinearLayout
android:id="@+id/avatar_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginTop="@dimen/standard_margin"
android:visibility="invisible"
tools:visibility="visible">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/avatar_upload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_quarter_margin"
android:contentDescription="@string/upload_new_avatar_from_device"
android:tint="@android:color/white"
app:elevation="0dp"
app:fabSize="mini"
app:srcCompat="@drawable/upload" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/avatar_choose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_quarter_margin"
android:contentDescription="@string/choose_avatar_from_cloud"
android:tint="@android:color/white"
app:elevation="0dp"
app:fabSize="mini"
app:srcCompat="@drawable/ic_mimetype_folder" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/avatar_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_quarter_margin"
android:contentDescription="@string/set_avatar_from_camera"
android:tint="@android:color/white"
app:elevation="0dp"
app:fabSize="mini"
app:srcCompat="@drawable/ic_baseline_photo_camera_24" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/avatar_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_quarter_margin"
android:contentDescription="@string/delete_avatar"
android:tint="@android:color/white"
app:elevation="0dp"
app:fabSize="mini"
app:srcCompat="@drawable/trashbin" />
</LinearLayout>
</com.yarolegovich.mp.MaterialPreferenceCategory>
<com.yarolegovich.mp.MaterialPreferenceCategory

View File

@ -0,0 +1,145 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ @author Andy Scherzinger
~ @author Marcel Hibbe
~ @author Tim Krüger
~ Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
~ Copyright (C) 2022-2023 Marcel Hibbe <dev@mhibbe.de>
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~ 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/>.
-->
<LinearLayout 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"
android:id="@+id/parent_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/conversation_info_edit_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/conversation_info_edit_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/appbar"
android:theme="?attr/actionBarPopupTheme"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIconTint="@color/fontAppbar"
app:popupTheme="@style/appActionBarPopupMenu"
app:titleTextColor="@color/fontAppbar"
tools:title="@string/nc_app_product_name" />
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/avatar_image"
android:layout_width="@dimen/avatar_size_big"
android:layout_height="@dimen/avatar_size_big"
android:layout_marginTop="@dimen/standard_margin"
android:layout_gravity="center"
android:contentDescription="@string/avatar"
tools:src="@drawable/account_circle_48dp" />
<LinearLayout
android:id="@+id/avatar_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/standard_margin"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/avatar_upload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_quarter_margin"
android:contentDescription="@string/upload_new_avatar_from_device"
android:tint="@android:color/white"
app:elevation="0dp"
app:fabSize="mini"
app:srcCompat="@drawable/upload" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/avatar_choose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_quarter_margin"
android:contentDescription="@string/choose_avatar_from_cloud"
android:tint="@android:color/white"
app:elevation="0dp"
app:fabSize="mini"
app:srcCompat="@drawable/ic_mimetype_folder" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/avatar_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_quarter_margin"
android:contentDescription="@string/set_avatar_from_camera"
android:tint="@android:color/white"
app:elevation="0dp"
app:fabSize="mini"
app:srcCompat="@drawable/ic_baseline_photo_camera_24" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/avatar_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_quarter_margin"
android:contentDescription="@string/delete_avatar"
android:tint="@android:color/white"
app:elevation="0dp"
app:fabSize="mini"
app:srcCompat="@drawable/trashbin" />
</LinearLayout>
<androidx.emoji2.widget.EmojiTextView
android:id="@+id/display_name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/margin_between_elements"
tools:text="Jane Doe" />
<androidx.emoji2.widget.EmojiTextView
android:id="@+id/description_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/standard_margin"
android:layout_marginTop="@dimen/margin_between_elements"
android:autoLink="web"
tools:text="Hello world!" />
</LinearLayout>
</LinearLayout>