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:
parent
58898d8ac0
commit
867d28a7ec
8 changed files with 202 additions and 57 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
69
core-ui/src/main/res/layout/item_block_title_image.xml
Normal file
69
core-ui/src/main/res/layout/item_block_title_image.xml
Normal 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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue