1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-3264 Editor | Fix | Apply correct icon sizing for BASIC and IMAGE layouts (#2295)

Co-authored-by: Evgenii Kozlov <enklave.mare.balticum@protonmail.com>
This commit is contained in:
Nadezhda-Gurova 2025-04-12 15:14:22 +02:00 committed by GitHub
parent 58898d8ac0
commit 867d28a7ec
Signed by: github
GPG key ID: B5690EEEBB952194
8 changed files with 202 additions and 57 deletions

View file

@ -58,6 +58,7 @@ import com.anytypeio.anytype.core_ui.databinding.ItemBlockTableBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTextBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleFileBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleImageBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleProfileBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleTodoBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleVideoBinding
@ -172,6 +173,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_LOADING
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_BUTTON_OPEN_FILE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_BUTTON_OPEN_IMAGE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_IMAGE_TITLE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PARAGRAPH
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE_ERROR
@ -376,6 +378,13 @@ class BlockAdapter(
ItemBlockTitleFileBinding.inflate(inflater, parent, false)
)
}
HOLDER_IMAGE_TITLE -> {
Title.Image(
ItemBlockTitleImageBinding.inflate(inflater, parent, false)
)
}
HOLDER_VIDEO_TITLE -> Title.Video(
ItemBlockTitleVideoBinding.inflate(inflater, parent, false),
lifecycle
@ -1046,6 +1055,13 @@ class BlockAdapter(
)
}
is Title.Image -> {
holder.processPayloads(
payloads = payloads.typeOf(),
item = blocks[position] as BlockView.Title.Image
)
}
is Numbered -> {
holder.processChangePayload(
payloads = payloads.typeOf(),
@ -1419,6 +1435,12 @@ class BlockAdapter(
bind(item = blocks[position] as BlockView.Title.Video)
}
}
is Title.Image -> {
holder.apply {
bind(item = blocks[position] as BlockView.Title.Image)
}
}
is Code -> {
holder.bind(
item = blocks[position] as BlockView.Code,

View file

@ -21,6 +21,7 @@ import com.anytypeio.anytype.core_ui.common.SearchHighlightSpan
import com.anytypeio.anytype.core_ui.common.SearchTargetHighlightSpan
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleFileBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleImageBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleProfileBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleTodoBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleVideoBinding
@ -175,6 +176,7 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder {
}
}
}
open fun setImage(item: BlockView.Title) {
Timber.d("Setting image for ${item.id}, image=${item.image}")
item.image?.let { url ->
@ -187,48 +189,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder {
} ?: apply { image.setImageDrawable(null) }
}
// TODO use for objects with image layout; move to the dedicated view holder.
private fun loadImageWithCustomResize(imageView: ImageView, url: String) {
val context = imageView.context
val displayMetrics = context.resources.displayMetrics
val screenWidth = displayMetrics.widthPixels
Glide.with(context)
.asBitmap()
.load(url)
.override(Target.SIZE_ORIGINAL)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
val aspectRatio = resource.width.toFloat() / resource.height.toFloat()
val calculatedHeight = (screenWidth / aspectRatio).toInt()
val imageHeight = when {
calculatedHeight < dpToPx(context, 188) -> dpToPx(context, 188)
calculatedHeight > dpToPx(context, 443) -> dpToPx(context, 443)
else -> calculatedHeight
}
imageView.updateLayoutParams<ConstraintLayout.LayoutParams> {
width = screenWidth
height = imageHeight
}
imageView.setImageBitmap(resource)
}
override fun onLoadCleared(placeholder: Drawable?) {
imageView.setImageDrawable(null)
}
})
}
private fun dpToPx(context: Context, dp: Int): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp.toFloat(),
context.resources.displayMetrics
).toInt()
}
open fun processPayloads(
payloads: List<BlockViewDiffUtil.Payload>,
item: BlockView.Title
@ -659,6 +619,79 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder {
}
}
class Image(private val imageBinding: ItemBlockTitleImageBinding) : Title(imageBinding.root) {
private val context: Context = imageBinding.root.context
override val icon: ObjectIconWidget = imageBinding.objectIconWidget
override val image: ImageView = imageBinding.imageIcon
override val content: TextInputWidget = imageBinding.title
override val selectionView: View = itemView
override fun applyTextColor(item: BlockView.Title) {
// Do nothing
}
override fun applyBackground(item: BlockView.Title) {
// Do nothing
}
fun bind(item: BlockView.Title.Image) {
super.bind(
item = item,
onCoverClicked = {},
click = {}
)
content.setText(item.text)
}
override fun setImage(item: BlockView.Title) {
item.image?.let { url ->
image.visible()
loadImageWithCustomResize(image, url)
} ?: run { image.setImageDrawable(null) }
}
private fun loadImageWithCustomResize(imageView: ImageView, url: String) {
val displayMetrics = context.resources.displayMetrics
val screenWidth = displayMetrics.widthPixels
Glide.with(context)
.asBitmap()
.load(url)
.override(Target.SIZE_ORIGINAL)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
val aspectRatio = resource.width.toFloat() / resource.height.toFloat()
val calculatedHeight = (screenWidth / aspectRatio).toInt()
val imageHeight = when {
calculatedHeight < dpToPx(context, 188) -> dpToPx(context, 188)
calculatedHeight > dpToPx(context, 443) -> dpToPx(context, 443)
else -> calculatedHeight
}
imageView.updateLayoutParams<ConstraintLayout.LayoutParams> {
width = screenWidth
height = imageHeight
}
imageView.setImageBitmap(resource)
}
override fun onLoadCleared(placeholder: Drawable?) {
imageView.setImageDrawable(null)
}
})
}
private fun dpToPx(context: Context, dp: Int): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp.toFloat(),
context.resources.displayMetrics
).toInt()
}
}
class Video(
private val videoBinding: ItemBlockTitleVideoBinding,

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/cover"
android:layout_width="0dp"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/imageIcon"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxHeight="443dp"
android:layout_marginTop="50dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:background="@color/shape_tertiary"
android:transitionName="@string/logo_transition"
app:layout_constraintTop_toBottomOf="@id/cover"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:shapeAppearance="@style/TitleImageAppearanceOverlay"
app:strokeColor="@color/shape_tertiary"
app:strokeWidth="1dp"
tools:src="@drawable/ic_mime_pdf"/>
<com.anytypeio.anytype.core_ui.widgets.ObjectIconWidget
android:id="@+id/objectIconWidget"
android:layout_width="64dp"
android:layout_height="80dp"
android:layout_marginStart="28dp"
android:visibility="gone"
android:layout_marginTop="16dp"
android:background="@drawable/bg_title_file_icon"
android:transitionName="@string/logo_transition"
app:imageSize="80dp"
app:layout_constraintTop_toBottomOf="@id/imageIcon"
app:layout_constraintStart_toStartOf="parent"
tools:src="@drawable/ic_mime_pdf"/>
<com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
android:id="@+id/title"
style="@style/BlockTitleContentStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/untitled"
android:paddingStart="20dp"
android:paddingEnd="20dp"
app:ignoreDragAndDrop="true"
app:layout_constraintTop_toBottomOf="@id/objectIconWidget"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:onlyPasteAsPlaneText="true"
tools:text="Image title here"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -47,6 +47,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_TYPE_DELETED
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_BUTTON_OPEN_FILE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_BUTTON_OPEN_IMAGE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_IMAGE_TITLE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PARAGRAPH
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE_ERROR
@ -689,7 +690,6 @@ sealed class BlockView : ViewType {
override var cursor: Int? = null,
override val searchFields: List<Searchable.Field> = emptyList(),
override val hint: String? = null,
val url: String?,
val icon: ObjectIcon
) : Title(), Searchable {
override fun getViewType() = HOLDER_FILE_TITLE
@ -722,6 +722,29 @@ sealed class BlockView : ViewType {
override fun getViewType() = HOLDER_VIDEO_TITLE
}
/**
* UI-model for a image -layout title block.
*/
data class Image(
override val id: String,
override var isFocused: Boolean = false,
override var text: String,
override var coverColor: CoverColor? = null,
override var coverImage: Url? = null,
override var coverGradient: String? = null,
override val background: ThemeColor = ThemeColor.DEFAULT,
override val color: ThemeColor = ThemeColor.DEFAULT,
val emoji: String? = null,
override val image: String? = null,
override val mode: Mode = Mode.READ,
override var cursor: Int? = null,
override val searchFields: List<Searchable.Field> = emptyList(),
override val hint: String? = null,
val icon: ObjectIcon
) : Title(), Searchable {
override fun getViewType() = HOLDER_IMAGE_TITLE
}
/**
* UI-model for a profile-layout title block.
* @property id block's id

View file

@ -7,6 +7,7 @@ object Types {
const val HOLDER_ARCHIVE_TITLE = 36
const val HOLDER_FILE_TITLE = 37
const val HOLDER_VIDEO_TITLE = 60
const val HOLDER_IMAGE_TITLE = 11
const val HOLDER_TODO_TITLE = 48
const val HOLDER_HEADER_ONE = 2
const val HOLDER_HEADER_TWO = 3

View file

@ -1533,20 +1533,16 @@ class DefaultBlockViewRenderer @Inject constructor(
background = block.parseThemeBackgroundColor(),
color = block.textColor(),
icon = currentObject.objectIcon(builder = urlBuilder, objType = objType),
url = currentObject.getFileUrl(urlBuilder)
)
}
ObjectType.Layout.IMAGE -> {
BlockView.Title.Basic(
val icon = currentObject.iconImage
BlockView.Title.Image(
mode = Mode.READ,
id = block.id,
text = fieldParser.getObjectName(currentObject),
image = currentObject.iconImage?.let { image ->
if (image.isNotBlank())
urlBuilder.large(image)
else
null
},
text = content.text,
image = if (!icon.isNullOrEmpty()) urlBuilder.large(icon) else null,
icon = currentObject.objectIcon(builder = urlBuilder),
isFocused = resolveIsFocused(focus, block),
cursor = cursor,
coverColor = coverContainer.coverColor,

View file

@ -356,6 +356,7 @@ fun List<BlockView>.toggleTableMode(
is BlockView.ButtonOpenFile.ImageButton -> view
is BlockView.ButtonOpenFile.FileButton -> view
is BlockView.Title.Video -> view
is BlockView.Title.Image -> view
}
}
}

View file

@ -125,8 +125,7 @@ class FileLayoutTest : EditorPresentationTestSetup() {
mime = fileObject.fileMimeType,
fileName = fileObject.name,
extensions = fileExt
),
url = null
)
),
BlockView.ButtonOpenFile.FileButton(
id = fileBlock.id,
@ -185,11 +184,12 @@ class FileLayoutTest : EditorPresentationTestSetup() {
val firstTimeExpected = ViewState.Success(
listOf(
BlockView.Title.Basic(
BlockView.Title.Image(
isFocused = false,
id = title.id,
text = "${fileObject.name}.$fileExt",
mode = BlockView.Mode.READ //in this case for a Image Object title is always locked!
text = title.content.asText().text,
mode = BlockView.Mode.READ,
icon = ObjectIcon.None
),
BlockView.ButtonOpenFile.ImageButton(
id = fileBlock.id,