From d2499d5eef470b940b82d05aa0e6479e41541baf Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Thu, 3 Jul 2025 14:53:48 +0200 Subject: [PATCH 01/24] DROID-3805 refactoring part 1 --- .../anytype/ui/editor/EditorFragment.kt | 9 +- .../anytype/ui/sets/ObjectSetFragment.kt | 24 +-- .../features/editor/BlockViewDiffUtil.kt | 2 +- .../features/editor/holders/other/Title.kt | 152 ++++++------------ .../tools/EditorHeaderOverlayDetector.kt | 18 +-- .../core_ui/widgets/ObjectIconWidget.kt | 5 +- .../src/main/res/layout/item_block_title.xml | 49 +----- .../res/layout/item_block_title_profile.xml | 37 +---- .../main/res/layout/item_block_title_todo.xml | 10 +- .../editor/editor/model/BlockView.kt | 4 +- .../editor/render/DefaultBlockViewRenderer.kt | 46 +++--- .../relations/ObjectSetRenderMapper.kt | 6 +- 12 files changed, 121 insertions(+), 241 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index a0a32a7da2..8b846d8dd3 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -1502,11 +1502,10 @@ open class EditorFragment : NavigationFragment(R.layout.f if (title != null) { when (title) { is BlockView.Title.Basic -> { - resetTopToolbarTitle( - text = title.text, - emoji = title.emoji, - image = title.image, - ) +// resetTopToolbarTitle( +// text = title.text, +// icon = title.icon, +// ) if (title.hasCover) { val mng = binding.recycler.layoutManager as LinearLayoutManager val pos = mng.findFirstVisibleItemPosition() diff --git a/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt index 2a6bf290df..a404dace33 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/sets/ObjectSetFragment.kt @@ -889,7 +889,7 @@ open class ObjectSetFragment : binding.topToolbar.root.findViewById(R.id.tvTopToolbarTitle).text = header.title.text binding.objectHeader.root.findViewById(R.id.docEmojiIconContainer).apply { - if (header.title.emoji != null) visible() else gone() + //if (header.title.emoji != null) visible() else gone() jobs += this.clicks() .throttleFirst() .onEach { vm.onObjectIconClicked() } @@ -911,8 +911,8 @@ open class ObjectSetFragment : } } - binding.objectHeader.root.findViewById(R.id.emojiIcon) - .setEmojiOrNull(header.title.emoji) +// binding.objectHeader.root.findViewById(R.id.emojiIcon) +// .setEmojiOrNull(header.title.emoji) setCover( coverColor = header.title.coverColor, @@ -938,15 +938,15 @@ open class ObjectSetFragment : private fun setupHeaderMargins(header: SetOrCollectionHeaderState.Default) { when { - header.title.emoji != null -> { - title.updateLayoutParams { - topMargin = dimen(R.dimen.dp_12) - } - binding.objectHeader.docEmojiIconContainer.updateLayoutParams { - topMargin = - if (!header.title.hasCover) dimen(R.dimen.dp_12) else dimen(R.dimen.dp_72) - } - } +// header.title.emoji != null -> { +// title.updateLayoutParams { +// topMargin = dimen(R.dimen.dp_12) +// } +// binding.objectHeader.docEmojiIconContainer.updateLayoutParams { +// topMargin = +// if (!header.title.hasCover) dimen(R.dimen.dp_12) else dimen(R.dimen.dp_72) +// } +// } header.title.image != null -> { title.updateLayoutParams { topMargin = dimen(R.dimen.dp_10) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockViewDiffUtil.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockViewDiffUtil.kt index f3251dbe4a..0e49230e49 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockViewDiffUtil.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockViewDiffUtil.kt @@ -37,7 +37,7 @@ class BlockViewDiffUtil( val changes = mutableListOf() if (newBlock is BlockView.Title.Basic && oldBlock is BlockView.Title.Basic) { - if (newBlock.emoji != oldBlock.emoji || newBlock.image != oldBlock.image) + if (newBlock.icon != oldBlock.icon) changes.add(TITLE_ICON_CHANGED) if (newBlock.coverColor != oldBlock.coverColor || newBlock.coverGradient != oldBlock.coverGradient diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 79fcdcfe6d..7ef7a0f52d 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -7,7 +7,7 @@ import android.view.View import android.widget.FrameLayout.LayoutParams import android.widget.ImageView import android.widget.TextView -import androidx.compose.ui.platform.ComposeView + import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle @@ -17,7 +17,7 @@ import androidx.recyclerview.widget.RecyclerView import coil3.imageLoader import coil3.load import coil3.request.ImageRequest -import coil3.request.transformations + import coil3.target.Target import com.anytypeio.anytype.core_ui.R import com.anytypeio.anytype.core_ui.common.SearchHighlightSpan @@ -39,12 +39,13 @@ import com.anytypeio.anytype.core_utils.ext.dimen import com.anytypeio.anytype.core_utils.ext.gone import com.anytypeio.anytype.core_utils.ext.removeSpans import com.anytypeio.anytype.core_utils.ext.visible -import com.anytypeio.anytype.emojifier.Emojifier + import com.anytypeio.anytype.presentation.editor.cover.CoverColor import com.anytypeio.anytype.presentation.editor.cover.CoverGradient import com.anytypeio.anytype.presentation.editor.editor.KeyPressedEvent import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType import com.anytypeio.anytype.presentation.editor.editor.model.BlockView +import com.anytypeio.anytype.presentation.objects.ObjectIcon import com.google.android.exoplayer2.DefaultLoadControl import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem @@ -172,11 +173,8 @@ 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 -> - image.visible() - image.load(url) - } ?: apply { image.setImageDrawable(null) } + // Default no-op implementation + // Each subclass handles its own icon logic using ObjectIconWidget } open fun processPayloads( @@ -258,10 +256,8 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { class Document(val binding: ItemBlockTitleBinding) : Title(binding.root) { - override val icon: View = binding.docEmojiIconContainer - override val image: ImageView = binding.imageIcon - private val emoji: ImageView = binding.emojiIcon - private val emojiFallback: TextView = binding.emojiIconFallback + override val icon: ObjectIconWidget = binding.objectIconWidget + override val image: ImageView = binding.objectIconWidget.binding.ivImage override val selectionView: View = itemView override val root: View = itemView @@ -282,53 +278,33 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { onCoverClicked = onCoverClicked, click = click ) - setEmoji(item) + setIcon(item) applySearchHighlights(item) - image.setOnClickListener { - click( - ListenerType.Picture.TitleView( - item = item - ) - ) - } - if (item.mode == BlockView.Mode.EDIT) { icon.setOnClickListener { onPageIconClicked() } - image.setOnClickListener { onPageIconClicked() } } setupIconVisibility(item) } + private fun setIcon(item: BlockView.Title.Basic) { + icon.setIcon(item.icon) + } + private fun setupIconVisibility(item: BlockView.Title.Basic) { when { - item.image != null -> { - binding.imageIcon.visible() - binding.docEmojiIconContainer.gone() + item.icon !is ObjectIcon.None -> { + icon.visible() binding.title.updateLayoutParams { topMargin = dimen(R.dimen.dp_10) } - binding.imageIcon.updateLayoutParams { + icon.updateLayoutParams { topMargin = if (!item.hasCover) dimen(R.dimen.dp_51) else dimen(R.dimen.dp_102) } } - - item.emoji != null -> { - binding.imageIcon.gone() - binding.docEmojiIconContainer.visible() - binding.title.updateLayoutParams { - topMargin = dimen(R.dimen.dp_12) - } - binding.docEmojiIconContainer.updateLayoutParams { - topMargin = - if (!item.hasCover) dimen(R.dimen.dp_60) else dimen(R.dimen.dp_120) - } - } - else -> { - binding.imageIcon.gone() - binding.docEmojiIconContainer.gone() + icon.gone() if (!item.hasCover) { content.updateLayoutParams { topMargin = dimen(R.dimen.dp_80) @@ -350,8 +326,7 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { if (item is BlockView.Title.Basic) { payloads.forEach { payload -> if (payload.isTitleIconChanged) { - setEmoji(item) - setImage(item) + setIcon(item) setupIconVisibility(item) } if (payload.isCoverChanged) { @@ -364,36 +339,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { } } - private fun setEmoji(item: BlockView.Title.Basic) { - try { - if (!item.emoji.isNullOrEmpty()) { - try { - val adapted = Emojifier.safeUri(item.emoji!!) - if (adapted != Emojifier.Config.EMPTY_URI) { - emojiFallback.text = "" - emojiFallback.gone() - emoji.load(adapted) { - memoryCachePolicy(coil3.request.CachePolicy.ENABLED) - diskCachePolicy(coil3.request.CachePolicy.ENABLED) - } - emoji.visible() - } else { - emoji.setImageDrawable(null) - emoji.gone() - emojiFallback.text = item.emoji - emojiFallback.visible() - } - } catch (e: Throwable) { - Timber.w(e, "Error while setting emoji icon for: ${item.emoji}") - } - } else { - emoji.setImageDrawable(null) - } - } catch (e: Throwable) { - Timber.w(e, "Could not set emoji icon") - } - } - override fun applyTextColor(item: BlockView.Title) { setTextColor(item.color) } @@ -405,17 +350,11 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { class Profile(val binding: ItemBlockTitleProfileBinding) : Title(binding.root) { - override val icon: View = binding.docProfileIconContainer - override val image: ImageView = binding.imageIcon + override val icon: ObjectIconWidget = binding.objectIconWidget + override val image: ImageView = binding.objectIconWidget.binding.ivImage override val content: TextInputWidget = binding.title override val selectionView: View = itemView - private val gradientView: ComposeView - get() = binding - .docProfileIconContainer - .findViewById(R.id.gradient) - - private val iconText = binding.imageText private var hasImage = false init { @@ -433,6 +372,7 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { onCoverClicked = onCoverClicked, click = click ) + setIcon(item) setupMargins(item) applySearchHighlights(item) if (item.mode == BlockView.Mode.EDIT) { @@ -440,34 +380,28 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { } } - override fun setImage(item: BlockView.Title) { - item.image?.let { url -> - iconText.text = "" - gradientView.gone() - hasImage = true - image.visible() - image.load(url) { - transformations(coil3.transform.CircleCropTransformation()) + private fun setIcon(item: BlockView.Title.Profile) { + when { + !item.image.isNullOrBlank() -> { + hasImage = true + icon.setIcon(ObjectIcon.Profile.Image(item.image!!, item.text.orEmpty())) + } + else -> { + hasImage = false + icon.setIcon(ObjectIcon.Profile.Avatar(item.text.orEmpty())) } - } ?: apply { - hasImage = false - gradientView.gone() - setIconText(item.text) - image.setImageDrawable(null) } } - private fun setIconText(name: String?) { - if (name.isNullOrEmpty()) { - iconText.text = "U" - } else { - iconText.text = name.first().uppercaseChar().toString() + override fun setImage(item: BlockView.Title) { + if (item is BlockView.Title.Profile) { + setIcon(item) } } fun onTitleTextChanged(text: String) { if (!hasImage) { - setIconText(text) + icon.setIcon(ObjectIcon.Profile.Avatar(text)) } } @@ -479,7 +413,7 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { if (item is BlockView.Title.Profile) { payloads.forEach { payload -> if (payload.isTitleIconChanged) { - setImage(item) + setIcon(item) } if (payload.isSearchHighlightChanged) { applySearchHighlights(item) @@ -500,7 +434,7 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { } private fun setupMargins(item: BlockView.Title.Profile) { - binding.docProfileIconContainer.updateLayoutParams { + icon.updateLayoutParams { topMargin = if (!item.hasCover) dimen(R.dimen.dp_46) else dimen(R.dimen.dp_92) } } @@ -508,11 +442,11 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { class Todo(val binding: ItemBlockTitleTodoBinding) : Title(binding.root) { - override val icon: View = binding.todoTitleCheckbox - override val image: ImageView = binding.todoTitleCheckbox + override val icon: ObjectIconWidget = binding.objectIconWidget + override val image: ImageView = binding.objectIconWidget.binding.ivImage override val selectionView: View = itemView - val checkbox = binding.todoTitleCheckbox + val checkbox = binding.objectIconWidget.checkbox var isLocked: Boolean = false override val root: View = itemView @@ -534,10 +468,14 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { click = click ) setLocked(item.mode) - checkbox.isSelected = item.isChecked + setTaskIcon(item.isChecked) applySearchHighlights(item) } + private fun setTaskIcon(isChecked: Boolean) { + icon.setIcon(ObjectIcon.Task(isChecked)) + } + private fun setLocked(mode: BlockView.Mode) { isLocked = mode == BlockView.Mode.READ } @@ -556,7 +494,7 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { applySearchHighlights(item) } if (payload.isTitleCheckboxChanged) { - checkbox.isSelected = item.isChecked + setTaskIcon(item.isChecked) } } } diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/tools/EditorHeaderOverlayDetector.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/tools/EditorHeaderOverlayDetector.kt index c4ee074bdf..5a54d21543 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/tools/EditorHeaderOverlayDetector.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/tools/EditorHeaderOverlayDetector.kt @@ -32,21 +32,13 @@ class EditorHeaderOverlayDetector( val root = holder.binding.root val title = holder.binding.title val cover = holder.binding.cover - val emojiIconContainer = holder.binding.docEmojiIconContainer - val imageIconContainer = holder.binding.imageIcon + val iconWidget = holder.binding.objectIconWidget onHeaderOverlaid = when { - emojiIconContainer.isVisible -> { + iconWidget.isVisible -> { if (cover.isVisible) { - (emojiIconContainer.top + root.top >= threshold + thresholdPadding) + (iconWidget.top + root.top >= threshold + thresholdPadding) } else { - (emojiIconContainer.top + root.top >= (threshold / 2) + thresholdPadding) - } - } - imageIconContainer.isVisible -> { - if (cover.isVisible) { - (imageIconContainer.top + root.top >= threshold + thresholdPadding) - } else { - (imageIconContainer.top + root.top >= (threshold / 2) + thresholdPadding) + (iconWidget.top + root.top >= (threshold / 2) + thresholdPadding) } } else -> { @@ -66,7 +58,7 @@ class EditorHeaderOverlayDetector( is Title.Profile -> { val container = holder.itemView val cover = holder.binding.cover - val icon = holder.binding.docProfileIconContainer + val icon = holder.binding.objectIconWidget val title = holder.binding.title onHeaderOverlaid = if (cover.isVisible) { (container.top + icon.bottom >= threshold + thresholdPadding) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt index 352bef53ad..61e7363581 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt @@ -205,10 +205,7 @@ class ObjectIconWidget @JvmOverloads constructor( if (adapted != Emojifier.Config.EMPTY_URI) { binding.tvEmojiFallback.gone() binding.ivEmoji.visible() - binding.ivEmoji.load(adapted) { - diskCachePolicy(CachePolicy.ENABLED) - memoryCachePolicy(CachePolicy.ENABLED) - } + binding.ivEmoji.load(adapted) } else { setTypeIcon(fallback) } diff --git a/core-ui/src/main/res/layout/item_block_title.xml b/core-ui/src/main/res/layout/item_block_title.xml index 8e7ba5802e..0ad434762f 100644 --- a/core-ui/src/main/res/layout/item_block_title.xml +++ b/core-ui/src/main/res/layout/item_block_title.xml @@ -20,53 +20,18 @@ tools:background="@color/orange" tools:visibility="gone" /> - - - - - - - - - + app:emojiSize="48dp" + app:imageSize="96dp" + app:imageCornerRadius="12dp" /> + app:constraint_referenced_ids="objectIconWidget,cover,root" /> \ No newline at end of file diff --git a/core-ui/src/main/res/layout/item_block_title_profile.xml b/core-ui/src/main/res/layout/item_block_title_profile.xml index 63639024a8..e4de82a13d 100644 --- a/core-ui/src/main/res/layout/item_block_title_profile.xml +++ b/core-ui/src/main/res/layout/item_block_title_profile.xml @@ -17,40 +17,19 @@ app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> - - - - - - - - + app:layout_constraintTop_toTopOf="parent" + app:emojiSize="64dp" + app:imageSize="112dp" + app:initialTextSize="64sp" + tools:visibility="visible" /> diff --git a/core-ui/src/main/res/layout/item_block_title_todo.xml b/core-ui/src/main/res/layout/item_block_title_todo.xml index 55f483a600..63fdf6f00f 100644 --- a/core-ui/src/main/res/layout/item_block_title_todo.xml +++ b/core-ui/src/main/res/layout/item_block_title_todo.xml @@ -20,18 +20,18 @@ tools:background="@color/orange" tools:visibility="gone" /> - + app:layout_goneMarginTop="@dimen/dp_80" + app:checkboxSize="24dp" /> = emptyList(), - override val hint: String? = null + override val hint: String? = null, + val icon: ObjectIcon ) : Title(), Searchable { override fun getViewType() = HOLDER_TITLE } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt index 78b572fd4f..887b4d1b48 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt @@ -73,6 +73,20 @@ class DefaultBlockViewRenderer @Inject constructor( onRenderFlag: (BlockViewRenderer.RenderFlag) -> Unit, ): List { + val obj = details.getObject(id = context) + + if (obj?.isValid != true) { + Timber.w("Object with id $context is not valid, skipping rendering") + return emptyList() + } + + val objType = storeOfObjectTypes.getTypeOfObject(obj) + + if (objType?.isValid != true) { + Timber.w("Object type for object with id $context is not valid, skipping rendering") + return emptyList() + } + val children = getValue(anchor) val result = mutableListOf() @@ -86,8 +100,7 @@ class DefaultBlockViewRenderer @Inject constructor( isPreviousBlockMedia = false when (content.style) { Content.Text.Style.TITLE -> { - val obj = details.getObject(id = context) - if (obj?.layout == ObjectType.Layout.NOTE) { + if (obj.layout == ObjectType.Layout.NOTE) { // Workaround for skipping title block in objects with note layout Timber.d("Skipping title rendering for object with note layout or nullable object") } else { @@ -101,7 +114,7 @@ class DefaultBlockViewRenderer @Inject constructor( root = root, restrictions = restrictions, currentObject = obj, - storeOfObjectTypes = storeOfObjectTypes, + objType = objType ) ) } @@ -1417,8 +1430,8 @@ class DefaultBlockViewRenderer @Inject constructor( root: Block, focus: Focus, restrictions: List, - currentObject: ObjectWrapper.Basic?, - storeOfObjectTypes: StoreOfObjectTypes + currentObject: ObjectWrapper.Basic, + objType: ObjectWrapper.Type ): BlockView.Title { val focusTarget = focus.target @@ -1448,22 +1461,20 @@ class DefaultBlockViewRenderer @Inject constructor( if (mode == EditorMode.Edit) Mode.EDIT else Mode.READ } - return when (currentObject?.layout) { + return when (currentObject.layout) { ObjectType.Layout.BASIC -> { BlockView.Title.Basic( mode = blockMode, id = block.id, text = content.text, - emoji = currentObject.iconEmoji?.takeIf { it.isNotBlank() }, - image = currentObject.iconImage?.takeIf { it.isNotBlank() } - ?.let { urlBuilder.medium(it) }, isFocused = resolveIsFocused(focus, block), cursor = cursor, coverColor = coverContainer.coverColor, coverImage = coverContainer.coverImage, coverGradient = coverContainer.coverGradient, background = block.parseThemeBackgroundColor(), - color = block.textColor() + color = block.textColor(), + icon = currentObject.objectIcon(builder = urlBuilder, objType = objType) ) } ObjectType.Layout.TODO -> { @@ -1504,7 +1515,7 @@ class DefaultBlockViewRenderer @Inject constructor( id = block.id, text = fieldParser.getObjectName(currentObject), videoUrl = currentObject.getFileUrl(urlBuilder), - icon = currentObject.objectIcon(builder = urlBuilder), + icon = currentObject.objectIcon(builder = urlBuilder, objType = objType), isFocused = resolveIsFocused(focus, block), cursor = cursor, coverColor = coverContainer.coverColor, @@ -1530,7 +1541,7 @@ class DefaultBlockViewRenderer @Inject constructor( coverGradient = coverContainer.coverGradient, background = block.parseThemeBackgroundColor(), color = block.textColor(), - icon = currentObject.objectIcon(builder = urlBuilder, objType = objType), + icon = currentObject.objectIcon(builder = urlBuilder, objType = objType) ) } ObjectType.Layout.IMAGE -> { @@ -1540,7 +1551,7 @@ class DefaultBlockViewRenderer @Inject constructor( id = block.id, text = content.text, image = if (!icon.isNullOrEmpty()) urlBuilder.large(icon) else null, - icon = currentObject.objectIcon(builder = urlBuilder), + icon = currentObject.objectIcon(builder = urlBuilder, objType = objType), isFocused = resolveIsFocused(focus, block), cursor = cursor, coverColor = coverContainer.coverColor, @@ -1556,19 +1567,16 @@ class DefaultBlockViewRenderer @Inject constructor( mode = blockMode, id = block.id, text = content.text, - emoji = currentObject?.iconEmoji?.takeIf { it.isNotBlank() }, - image = currentObject?.iconImage?.takeIf { it.isNotBlank() }?.let { - urlBuilder.medium(it) - }, isFocused = resolveIsFocused(focus, block), cursor = cursor, coverColor = coverContainer.coverColor, coverImage = coverContainer.coverImage, coverGradient = coverContainer.coverGradient, background = block.parseThemeBackgroundColor(), - color = block.textColor() + color = block.textColor(), + icon = currentObject.objectIcon(builder = urlBuilder, objType = objType), ).also { - Timber.w("Unexpected layout for title: ${currentObject?.layout}") + Timber.w("Unexpected layout for title: ${currentObject.layout}") } } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/ObjectSetRenderMapper.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/ObjectSetRenderMapper.kt index a6a297ce58..33878468ed 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/ObjectSetRenderMapper.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/ObjectSetRenderMapper.kt @@ -36,6 +36,8 @@ import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes import com.anytypeio.anytype.presentation.extension.getObject import com.anytypeio.anytype.presentation.editor.editor.model.BlockView import com.anytypeio.anytype.presentation.extension.isValueRequired +import com.anytypeio.anytype.presentation.mapper.objectIcon +import com.anytypeio.anytype.presentation.objects.ObjectIcon import com.anytypeio.anytype.presentation.mapper.toCheckboxView import com.anytypeio.anytype.presentation.mapper.toDateView import com.anytypeio.anytype.presentation.mapper.toGridRecordRows @@ -230,11 +232,11 @@ fun title( return BlockView.Title.Basic( id = title.id, text = wrapper?.name.orEmpty(), - emoji = wrapper?.iconEmoji.orNull(), image = wrapper?.iconImage?.takeIf { it.isNotBlank() }?.let { urlBuilder.medium(it) }, coverImage = coverContainer.coverImage, coverColor = coverContainer.coverColor, - coverGradient = coverContainer.coverGradient + coverGradient = coverContainer.coverGradient, + icon = wrapper?.objectIcon(builder = urlBuilder) ?: ObjectIcon.None ) } From cf37bfe2528709a6deb7a975feba4dcc0d2d7240 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 09:59:36 +0200 Subject: [PATCH 02/24] DROID-3805 import --- .../core_ui/features/editor/holders/other/Title.kt | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 0aeb57bc6b..a4b914909e 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -7,20 +7,14 @@ import android.view.View import android.widget.FrameLayout.LayoutParams import android.widget.ImageView import android.widget.TextView - import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.updateLayoutParams -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver -import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.RecyclerView import coil3.ImageLoader import coil3.imageLoader import coil3.load import coil3.request.ImageRequest - import coil3.request.crossfade -import coil3.request.transformations import coil3.target.Target import coil3.video.VideoFrameDecoder import coil3.video.videoFrameMillis @@ -44,17 +38,12 @@ import com.anytypeio.anytype.core_utils.ext.dimen import com.anytypeio.anytype.core_utils.ext.gone import com.anytypeio.anytype.core_utils.ext.removeSpans import com.anytypeio.anytype.core_utils.ext.visible - import com.anytypeio.anytype.presentation.editor.cover.CoverColor import com.anytypeio.anytype.presentation.editor.cover.CoverGradient import com.anytypeio.anytype.presentation.editor.editor.KeyPressedEvent import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType import com.anytypeio.anytype.presentation.editor.editor.model.BlockView import com.anytypeio.anytype.presentation.objects.ObjectIcon -import com.google.android.exoplayer2.DefaultLoadControl -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.ui.StyledPlayerView import timber.log.Timber sealed class Title(view: View) : BlockViewHolder(view), TextHolder { From 30f04284712d17c3f77b89b54e653fe825c219e0 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 10:21:14 +0200 Subject: [PATCH 03/24] DROID-3805 icon background --- .../anytype/core_ui/widgets/ObjectIconWidget.kt | 14 ++++++++++++-- .../drawable/bg_object_header_emoji_container.xml | 9 +++++++++ core-ui/src/main/res/layout/item_block_title.xml | 1 + core-ui/src/main/res/values/attrs.xml | 1 + 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 core-ui/src/main/res/drawable/bg_object_header_emoji_container.xml diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt index 436392ccf2..bbe31ad8a8 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt @@ -49,6 +49,7 @@ class ObjectIconWidget @JvmOverloads constructor( private var imageCornerRadius: Float = 0F private var isImageWithCorners: Boolean = false private val density = context.resources.displayMetrics.density + private var emojiContainerBackground: Boolean = false init { setupAttributeValues(attrs) @@ -63,6 +64,7 @@ class ObjectIconWidget @JvmOverloads constructor( val emojiSize = attrs.getDimensionPixelSize(R.styleable.ObjectIconWidget_emojiSize, DEFAULT_SIZE) + emojiContainerBackground = attrs.getBoolean(R.styleable.ObjectIconWidget_emojiContainerBackground, false) val imageSize = attrs.getDimensionPixelSize(R.styleable.ObjectIconWidget_imageSize, DEFAULT_SIZE) val checkboxSize = @@ -106,7 +108,11 @@ class ObjectIconWidget @JvmOverloads constructor( fun setIcon(icon: ObjectIcon) { // Reset backgrounds - binding.emojiContainer.background = null + if (emojiContainerBackground) { + binding.emojiContainer.setBackgroundResource(R.drawable.bg_object_header_emoji_container) + } else { + binding.emojiContainer.background = null + } binding.ivImage.background = null when (icon) { @@ -213,7 +219,11 @@ class ObjectIconWidget @JvmOverloads constructor( emoji: String?, fallback: ObjectIcon.TypeIcon.Fallback ) { - binding.emojiContainer.background = null + if (emojiContainerBackground) { + binding.emojiContainer.setBackgroundResource(R.drawable.bg_object_header_emoji_container) + } else { + binding.emojiContainer.background = null + } if (!emoji.isNullOrBlank()) { with(binding) { ivCheckbox.invisible() diff --git a/core-ui/src/main/res/drawable/bg_object_header_emoji_container.xml b/core-ui/src/main/res/drawable/bg_object_header_emoji_container.xml new file mode 100644 index 0000000000..c221025155 --- /dev/null +++ b/core-ui/src/main/res/drawable/bg_object_header_emoji_container.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/core-ui/src/main/res/layout/item_block_title.xml b/core-ui/src/main/res/layout/item_block_title.xml index 0ad434762f..ab0db5076b 100644 --- a/core-ui/src/main/res/layout/item_block_title.xml +++ b/core-ui/src/main/res/layout/item_block_title.xml @@ -29,6 +29,7 @@ android:transitionName="@string/logo_transition" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" + app:emojiContainerBackground="true" app:emojiSize="48dp" app:imageSize="96dp" app:imageCornerRadius="12dp" /> diff --git a/core-ui/src/main/res/values/attrs.xml b/core-ui/src/main/res/values/attrs.xml index 284a047956..957a8227ab 100644 --- a/core-ui/src/main/res/values/attrs.xml +++ b/core-ui/src/main/res/values/attrs.xml @@ -10,6 +10,7 @@ + From 3619916507773bd066dee0e4b62c33bef0329f54 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 11:00:17 +0200 Subject: [PATCH 04/24] DROID-3805 icon update --- .../features/editor/holders/other/Title.kt | 42 +++++++++++++++++++ .../main/res/layout/widget_object_icon.xml | 1 + core-ui/src/main/res/values/dimens.xml | 2 + 3 files changed, 45 insertions(+) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index a4b914909e..81d9a108be 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -283,6 +283,48 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { private fun setIcon(item: BlockView.Title.Basic) { icon.setIcon(item.icon) + + // Adjust ObjectIconWidget size based on icon type + when (item.icon) { + is ObjectIcon.Basic.Emoji -> { + icon.updateLayoutParams { + width = dimen(R.dimen.dp_88) + height = dimen(R.dimen.dp_88) + } + } + is ObjectIcon.Basic.Image -> { + icon.updateLayoutParams { + width = dimen(R.dimen.dp_104) + height = dimen(R.dimen.dp_104) + } + } + is ObjectIcon.Profile.Avatar -> { + icon.updateLayoutParams { + width = dimen(R.dimen.dp_120) + height = dimen(R.dimen.dp_120) + } + } + is ObjectIcon.Profile.Image -> { + icon.updateLayoutParams { + width = dimen(R.dimen.dp_120) + height = dimen(R.dimen.dp_120) + } + } + is ObjectIcon.Task -> { + // Task icon uses default size from layout + icon.updateLayoutParams { + width = dimen(R.dimen.dp_96) + height = dimen(R.dimen.dp_96) + } + } + else -> { + // Default size for other icon types + icon.updateLayoutParams { + width = dimen(R.dimen.dp_96) + height = dimen(R.dimen.dp_96) + } + } + } } private fun setupIconVisibility(item: BlockView.Title.Basic) { diff --git a/core-ui/src/main/res/layout/widget_object_icon.xml b/core-ui/src/main/res/layout/widget_object_icon.xml index 78b37d7dd8..f7596b3183 100644 --- a/core-ui/src/main/res/layout/widget_object_icon.xml +++ b/core-ui/src/main/res/layout/widget_object_icon.xml @@ -24,6 +24,7 @@ android:id="@+id/ivImage" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_gravity="center" android:scaleType="centerCrop"/> 80dp 51dp 60dp + 88dp 92dp + 96dp 102dp 120dp 203dp From 60465636a5a61db7e08fb1fe998df22e61de27ff Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 12:12:37 +0200 Subject: [PATCH 05/24] DROID-3805 object basic header refactoring --- .../features/editor/holders/other/Title.kt | 32 ++++---- .../core_ui/widgets/ObjectIconWidget.kt | 2 + .../bg_object_header_icon_container.xml | 6 ++ .../src/main/res/layout/item_block_title.xml | 78 +++++++++---------- core-ui/src/main/res/values/dimens.xml | 4 +- 5 files changed, 66 insertions(+), 56 deletions(-) create mode 100644 core-ui/src/main/res/drawable/bg_object_header_icon_container.xml diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 81d9a108be..14127857a7 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -4,8 +4,10 @@ import android.content.Context import android.text.Spannable import android.util.TypedValue import android.view.View +import android.widget.FrameLayout import android.widget.FrameLayout.LayoutParams import android.widget.ImageView +import android.widget.LinearLayout import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.updateLayoutParams @@ -278,7 +280,7 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { if (item.mode == BlockView.Mode.EDIT) { icon.setOnClickListener { onPageIconClicked() } } - setupIconVisibility(item) + //setupIconVisibility(item) } private fun setIcon(item: BlockView.Title.Basic) { @@ -287,39 +289,41 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { // Adjust ObjectIconWidget size based on icon type when (item.icon) { is ObjectIcon.Basic.Emoji -> { - icon.updateLayoutParams { + icon.updateLayoutParams { width = dimen(R.dimen.dp_88) height = dimen(R.dimen.dp_88) + topMargin = dimen(R.dimen.dp_164) } } is ObjectIcon.Basic.Image -> { - icon.updateLayoutParams { + icon.updateLayoutParams { width = dimen(R.dimen.dp_104) height = dimen(R.dimen.dp_104) + topMargin = dimen(R.dimen.dp_148) } } is ObjectIcon.Profile.Avatar -> { - icon.updateLayoutParams { + icon.updateLayoutParams { width = dimen(R.dimen.dp_120) height = dimen(R.dimen.dp_120) } } is ObjectIcon.Profile.Image -> { - icon.updateLayoutParams { + icon.updateLayoutParams { width = dimen(R.dimen.dp_120) height = dimen(R.dimen.dp_120) } } is ObjectIcon.Task -> { // Task icon uses default size from layout - icon.updateLayoutParams { + icon.updateLayoutParams { width = dimen(R.dimen.dp_96) height = dimen(R.dimen.dp_96) } } else -> { // Default size for other icon types - icon.updateLayoutParams { + icon.updateLayoutParams { width = dimen(R.dimen.dp_96) height = dimen(R.dimen.dp_96) } @@ -331,10 +335,10 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { when { item.icon !is ObjectIcon.None -> { icon.visible() - binding.title.updateLayoutParams { + binding.title.updateLayoutParams { topMargin = dimen(R.dimen.dp_10) } - icon.updateLayoutParams { + icon.updateLayoutParams { topMargin = if (!item.hasCover) dimen(R.dimen.dp_51) else dimen(R.dimen.dp_102) } @@ -342,11 +346,11 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { else -> { icon.gone() if (!item.hasCover) { - content.updateLayoutParams { + content.updateLayoutParams { topMargin = dimen(R.dimen.dp_80) } } else { - content.updateLayoutParams { + content.updateLayoutParams { topMargin = dimen(R.dimen.dp_16) } } @@ -361,12 +365,8 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { super.processPayloads(payloads, item) if (item is BlockView.Title.Basic) { payloads.forEach { payload -> - if (payload.isTitleIconChanged) { + if (payload.isTitleIconChanged || payload.isCoverChanged) { setIcon(item) - setupIconVisibility(item) - } - if (payload.isCoverChanged) { - setupIconVisibility(item) } if (payload.isSearchHighlightChanged) { applySearchHighlights(item) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt index bbe31ad8a8..59aeaffc89 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt @@ -109,6 +109,7 @@ class ObjectIconWidget @JvmOverloads constructor( fun setIcon(icon: ObjectIcon) { // Reset backgrounds if (emojiContainerBackground) { + binding.root.setBackgroundResource(R.drawable.bg_object_header_icon_container) binding.emojiContainer.setBackgroundResource(R.drawable.bg_object_header_emoji_container) } else { binding.emojiContainer.background = null @@ -220,6 +221,7 @@ class ObjectIconWidget @JvmOverloads constructor( fallback: ObjectIcon.TypeIcon.Fallback ) { if (emojiContainerBackground) { + binding.root.setBackgroundResource(R.drawable.bg_object_header_icon_container) binding.emojiContainer.setBackgroundResource(R.drawable.bg_object_header_emoji_container) } else { binding.emojiContainer.background = null diff --git a/core-ui/src/main/res/drawable/bg_object_header_icon_container.xml b/core-ui/src/main/res/drawable/bg_object_header_icon_container.xml new file mode 100644 index 0000000000..f9a59aac42 --- /dev/null +++ b/core-ui/src/main/res/drawable/bg_object_header_icon_container.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/core-ui/src/main/res/layout/item_block_title.xml b/core-ui/src/main/res/layout/item_block_title.xml index ab0db5076b..20789b307b 100644 --- a/core-ui/src/main/res/layout/item_block_title.xml +++ b/core-ui/src/main/res/layout/item_block_title.xml @@ -1,59 +1,59 @@ - - + - + + + + + - + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/core-ui/src/main/res/values/dimens.xml b/core-ui/src/main/res/values/dimens.xml index 2295e89f78..61822e786f 100644 --- a/core-ui/src/main/res/values/dimens.xml +++ b/core-ui/src/main/res/values/dimens.xml @@ -45,6 +45,8 @@ 96dp 102dp 120dp + 148dp + 164dp 203dp 443dp @@ -139,7 +141,7 @@ 24dp 22dp 24sp - 188dp + 232dp 14dp 12sp 11sp From 985a7f57d5333aa41b8582ca149bd9c371cb4559 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 13:36:04 +0200 Subject: [PATCH 06/24] DROID-3805 title icon fixes --- .../features/editor/holders/other/Title.kt | 33 ++----------------- .../core_ui/widgets/ObjectIconWidget.kt | 11 ++++--- .../src/main/res/layout/item_block_title.xml | 20 ++++------- 3 files changed, 14 insertions(+), 50 deletions(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 14127857a7..6ecbc43af3 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -7,7 +7,6 @@ import android.view.View import android.widget.FrameLayout import android.widget.FrameLayout.LayoutParams import android.widget.ImageView -import android.widget.LinearLayout import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.updateLayoutParams @@ -280,7 +279,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { if (item.mode == BlockView.Mode.EDIT) { icon.setOnClickListener { onPageIconClicked() } } - //setupIconVisibility(item) } private fun setIcon(item: BlockView.Title.Basic) { @@ -292,14 +290,14 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { icon.updateLayoutParams { width = dimen(R.dimen.dp_88) height = dimen(R.dimen.dp_88) - topMargin = dimen(R.dimen.dp_164) + topMargin = if (item.hasCover) dimen(R.dimen.dp_164) else dimen(R.dimen.dp_120) } } is ObjectIcon.Basic.Image -> { icon.updateLayoutParams { width = dimen(R.dimen.dp_104) height = dimen(R.dimen.dp_104) - topMargin = dimen(R.dimen.dp_148) + topMargin = if (item.hasCover) dimen(R.dimen.dp_148) else dimen(R.dimen.dp_120) } } is ObjectIcon.Profile.Avatar -> { @@ -331,33 +329,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { } } - private fun setupIconVisibility(item: BlockView.Title.Basic) { - when { - item.icon !is ObjectIcon.None -> { - icon.visible() - binding.title.updateLayoutParams { - topMargin = dimen(R.dimen.dp_10) - } - icon.updateLayoutParams { - topMargin = - if (!item.hasCover) dimen(R.dimen.dp_51) else dimen(R.dimen.dp_102) - } - } - else -> { - icon.gone() - if (!item.hasCover) { - content.updateLayoutParams { - topMargin = dimen(R.dimen.dp_80) - } - } else { - content.updateLayoutParams { - topMargin = dimen(R.dimen.dp_16) - } - } - } - } - } - override fun processPayloads( payloads: List, item: BlockView.Title diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt index 59aeaffc89..ada63b9185 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt @@ -30,7 +30,6 @@ import com.anytypeio.anytype.presentation.objects.ObjectIcon import com.anytypeio.anytype.presentation.objects.ObjectIcon.TypeIcon import com.anytypeio.anytype.presentation.objects.custom_icon.CustomIconColor import coil3.load -import coil3.request.CachePolicy import timber.log.Timber class ObjectIconWidget @JvmOverloads constructor( @@ -49,7 +48,7 @@ class ObjectIconWidget @JvmOverloads constructor( private var imageCornerRadius: Float = 0F private var isImageWithCorners: Boolean = false private val density = context.resources.displayMetrics.density - private var emojiContainerBackground: Boolean = false + private var withBackgrounds: Boolean = false init { setupAttributeValues(attrs) @@ -64,7 +63,7 @@ class ObjectIconWidget @JvmOverloads constructor( val emojiSize = attrs.getDimensionPixelSize(R.styleable.ObjectIconWidget_emojiSize, DEFAULT_SIZE) - emojiContainerBackground = attrs.getBoolean(R.styleable.ObjectIconWidget_emojiContainerBackground, false) + withBackgrounds = attrs.getBoolean(R.styleable.ObjectIconWidget_emojiContainerBackground, false) val imageSize = attrs.getDimensionPixelSize(R.styleable.ObjectIconWidget_imageSize, DEFAULT_SIZE) val checkboxSize = @@ -108,10 +107,11 @@ class ObjectIconWidget @JvmOverloads constructor( fun setIcon(icon: ObjectIcon) { // Reset backgrounds - if (emojiContainerBackground) { + if (withBackgrounds) { binding.root.setBackgroundResource(R.drawable.bg_object_header_icon_container) binding.emojiContainer.setBackgroundResource(R.drawable.bg_object_header_emoji_container) } else { + binding.root.background = null binding.emojiContainer.background = null } binding.ivImage.background = null @@ -220,10 +220,11 @@ class ObjectIconWidget @JvmOverloads constructor( emoji: String?, fallback: ObjectIcon.TypeIcon.Fallback ) { - if (emojiContainerBackground) { + if (withBackgrounds) { binding.root.setBackgroundResource(R.drawable.bg_object_header_icon_container) binding.emojiContainer.setBackgroundResource(R.drawable.bg_object_header_emoji_container) } else { + binding.root.background = null binding.emojiContainer.background = null } if (!emoji.isNullOrBlank()) { diff --git a/core-ui/src/main/res/layout/item_block_title.xml b/core-ui/src/main/res/layout/item_block_title.xml index 20789b307b..82c0aeacca 100644 --- a/core-ui/src/main/res/layout/item_block_title.xml +++ b/core-ui/src/main/res/layout/item_block_title.xml @@ -3,9 +3,9 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/root" - android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:orientation="vertical"> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> @@ -41,19 +41,11 @@ style="@style/BlockTitleContentStyle" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="12dp" android:hint="@string/untitled" android:paddingStart="20dp" android:paddingEnd="20dp" app:ignoreDragAndDrop="true" - android:layout_marginTop="12dp" app:onlyPasteAsPlaneText="true" tools:text="Title" /> - - - - - - - - \ No newline at end of file From 7d7046aa59a813b40d3d4a5d6d4506fbd9c9365d Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 14:47:27 +0200 Subject: [PATCH 07/24] DROID-3805 task header update --- .../core_ui/features/editor/BlockAdapter.kt | 1 - .../features/editor/holders/other/Title.kt | 40 ++++--------------- .../core_ui/widgets/ObjectIconWidget.kt | 2 +- .../drawable/ic_object_icon_task_selector.xml | 5 +++ .../res/drawable/ic_todo_title_checkbox.xml | 2 +- .../main/res/layout/item_block_title_todo.xml | 25 +++++------- core-ui/src/main/res/values/dimens.xml | 8 +++- .../editor/editor/model/BlockView.kt | 3 +- .../editor/render/DefaultBlockViewRenderer.kt | 3 +- 9 files changed, 36 insertions(+), 53 deletions(-) create mode 100644 core-ui/src/main/res/drawable/ic_object_icon_task_selector.xml diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt index c86b0bec55..83d4bb9bc1 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt @@ -1384,7 +1384,6 @@ class BlockAdapter( holder.apply { bind( item = blocks[position] as BlockView.Title.Todo, - onPageIconClicked = onPageIconClicked, onCoverClicked = onCoverClicked, click = onClickListener ) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 6ecbc43af3..66080fc6d3 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -300,31 +300,8 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { topMargin = if (item.hasCover) dimen(R.dimen.dp_148) else dimen(R.dimen.dp_120) } } - is ObjectIcon.Profile.Avatar -> { - icon.updateLayoutParams { - width = dimen(R.dimen.dp_120) - height = dimen(R.dimen.dp_120) - } - } - is ObjectIcon.Profile.Image -> { - icon.updateLayoutParams { - width = dimen(R.dimen.dp_120) - height = dimen(R.dimen.dp_120) - } - } - is ObjectIcon.Task -> { - // Task icon uses default size from layout - icon.updateLayoutParams { - width = dimen(R.dimen.dp_96) - height = dimen(R.dimen.dp_96) - } - } else -> { - // Default size for other icon types - icon.updateLayoutParams { - width = dimen(R.dimen.dp_96) - height = dimen(R.dimen.dp_96) - } + //do nothing for other icon types } } } @@ -465,7 +442,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { fun bind( item: BlockView.Title.Todo, - onPageIconClicked: () -> Unit, onCoverClicked: () -> Unit, click: (ListenerType) -> Unit ) { @@ -475,14 +451,10 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { click = click ) setLocked(item.mode) - setTaskIcon(item.isChecked) + setIcon(item) applySearchHighlights(item) } - private fun setTaskIcon(isChecked: Boolean) { - icon.setIcon(ObjectIcon.Task(isChecked)) - } - private fun setLocked(mode: BlockView.Mode) { isLocked = mode == BlockView.Mode.READ } @@ -500,13 +472,17 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { if (payload.isSearchHighlightChanged) { applySearchHighlights(item) } - if (payload.isTitleCheckboxChanged) { - setTaskIcon(item.isChecked) + if (payload.isTitleCheckboxChanged || payload.isCoverChanged) { + setIcon(item) } } } } + private fun setIcon(item: BlockView.Title.Todo) { + icon.setIcon(item.icon) + } + override fun applyTextColor(item: BlockView.Title) { setTextColor(item.color) } diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt index ada63b9185..0b8ef8050a 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt @@ -305,7 +305,7 @@ class ObjectIconWidget @JvmOverloads constructor( private fun setTask(isChecked: Boolean?) { with(binding) { ivCheckbox.visible() - ivCheckbox.background = context.drawable(R.drawable.ic_data_view_grid_checkbox_selector) + ivCheckbox.background = context.drawable(R.drawable.ic_object_icon_task_selector) ivCheckbox.isActivated = isChecked ?: false initialContainer.invisible() emojiContainer.invisible() diff --git a/core-ui/src/main/res/drawable/ic_object_icon_task_selector.xml b/core-ui/src/main/res/drawable/ic_object_icon_task_selector.xml new file mode 100644 index 0000000000..f45a4185a3 --- /dev/null +++ b/core-ui/src/main/res/drawable/ic_object_icon_task_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/core-ui/src/main/res/drawable/ic_todo_title_checkbox.xml b/core-ui/src/main/res/drawable/ic_todo_title_checkbox.xml index ecf8eb24b3..d441351763 100644 --- a/core-ui/src/main/res/drawable/ic_todo_title_checkbox.xml +++ b/core-ui/src/main/res/drawable/ic_todo_title_checkbox.xml @@ -7,5 +7,5 @@ android:strokeWidth="1" android:pathData="M7.563,1.5L20.563,1.5A6,6 0,0 1,26.563 7.5L26.563,20.5A6,6 0,0 1,20.563 26.5L7.563,26.5A6,6 0,0 1,1.563 20.5L1.563,7.5A6,6 0,0 1,7.563 1.5z" android:fillColor="@color/transparent_black" - android:strokeColor="@color/gray"/> + android:strokeColor="@color/glyph_active"/> diff --git a/core-ui/src/main/res/layout/item_block_title_todo.xml b/core-ui/src/main/res/layout/item_block_title_todo.xml index 63fdf6f00f..4a79643592 100644 --- a/core-ui/src/main/res/layout/item_block_title_todo.xml +++ b/core-ui/src/main/res/layout/item_block_title_todo.xml @@ -12,7 +12,7 @@ android:layout_height="@dimen/default_cover_height" android:adjustViewBounds="true" android:contentDescription="@string/content_description_document_cover" - android:scaleType="fitXY" + android:scaleType="centerCrop" android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -22,32 +22,29 @@ + app:layout_goneMarginTop="@dimen/dp_126" /> diff --git a/core-ui/src/main/res/values/dimens.xml b/core-ui/src/main/res/values/dimens.xml index 61822e786f..6b9a8d9e83 100644 --- a/core-ui/src/main/res/values/dimens.xml +++ b/core-ui/src/main/res/values/dimens.xml @@ -27,10 +27,11 @@ 1dp 16dp 12dp - 32dp 20dp 24dp - 104dp + 28dp + 32dp + 34dp 40dp 46dp 48dp @@ -44,7 +45,10 @@ 92dp 96dp 102dp + 104dp 120dp + 124dp + 126dp 148dp 164dp 203dp diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt index ba47d870a3..d1f7f3b958 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt @@ -788,7 +788,8 @@ sealed class BlockView : ViewType { override var cursor: Int? = null, override val searchFields: List = emptyList(), var isChecked: Boolean = false, - override val hint: String? = null + override val hint: String? = null, + val icon: ObjectIcon ) : Title(), Searchable { override fun getViewType() = HOLDER_TODO_TITLE } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt index adb4588168..820a17d7c3 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt @@ -1488,7 +1488,8 @@ class DefaultBlockViewRenderer @Inject constructor( coverGradient = coverContainer.coverGradient, isChecked = content.isChecked == true, background = block.parseThemeBackgroundColor(), - color = block.textColor() + color = block.textColor(), + icon = currentObject.objectIcon(builder = urlBuilder, objType = objType) ) } ObjectType.Layout.PROFILE, ObjectType.Layout.PARTICIPANT -> { From 6552cf04d69c08f342802458146819aac5fd5f03 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 15:22:52 +0200 Subject: [PATCH 08/24] DROID-3805 profile title --- .../core_ui/features/editor/BlockAdapter.kt | 2 +- .../features/editor/holders/other/Title.kt | 33 ++---------------- ...g_object_header_icon_profile_container.xml | 5 +++ .../res/layout/item_block_title_profile.xml | 34 ++++++++++++------- .../editor/editor/model/BlockView.kt | 1 + .../editor/render/DefaultBlockViewRenderer.kt | 3 +- 6 files changed, 33 insertions(+), 45 deletions(-) create mode 100644 core-ui/src/main/res/drawable/bg_object_header_icon_profile_container.xml diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt index 83d4bb9bc1..304b4fb39e 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt @@ -347,7 +347,7 @@ class BlockAdapter( val text = editable.toString() view.text = text onTitleBlockTextChanged(view.id, text) - onTitleTextChanged(text) + onTitleTextChanged(view, text) } } ) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 66080fc6d3..67a5df2f0c 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -339,8 +339,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { override val content: TextInputWidget = binding.title override val selectionView: View = itemView - private var hasImage = false - init { content.setSpannableFactory(DefaultSpannableFactory()) } @@ -357,7 +355,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { click = click ) setIcon(item) - setupMargins(item) applySearchHighlights(item) if (item.mode == BlockView.Mode.EDIT) { icon.setOnClickListener { onProfileIconClicked(ListenerType.ProfileImageIcon) } @@ -365,26 +362,11 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { } private fun setIcon(item: BlockView.Title.Profile) { - when { - !item.image.isNullOrBlank() -> { - hasImage = true - icon.setIcon(ObjectIcon.Profile.Image(item.image!!, item.text.orEmpty())) - } - else -> { - hasImage = false - icon.setIcon(ObjectIcon.Profile.Avatar(item.text.orEmpty())) - } - } - } - - override fun setImage(item: BlockView.Title) { - if (item is BlockView.Title.Profile) { - setIcon(item) - } + icon.setIcon(item.icon) } - fun onTitleTextChanged(text: String) { - if (!hasImage) { + fun onTitleTextChanged(item: BlockView, text: String) { + if (item is BlockView.Title.Profile && item.icon is ObjectIcon.Profile.Avatar) { icon.setIcon(ObjectIcon.Profile.Avatar(text)) } } @@ -402,9 +384,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { if (payload.isSearchHighlightChanged) { applySearchHighlights(item) } - if (payload.isCoverChanged) { - setupMargins(item) - } } } } @@ -416,12 +395,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { override fun applyBackground(item: BlockView.Title) { binding.title.setBlockBackgroundColor(item.background) } - - private fun setupMargins(item: BlockView.Title.Profile) { - icon.updateLayoutParams { - topMargin = if (!item.hasCover) dimen(R.dimen.dp_46) else dimen(R.dimen.dp_92) - } - } } class Todo(val binding: ItemBlockTitleTodoBinding) : Title(binding.root) { diff --git a/core-ui/src/main/res/drawable/bg_object_header_icon_profile_container.xml b/core-ui/src/main/res/drawable/bg_object_header_icon_profile_container.xml new file mode 100644 index 0000000000..4cf7e3d7e2 --- /dev/null +++ b/core-ui/src/main/res/drawable/bg_object_header_icon_profile_container.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/core-ui/src/main/res/layout/item_block_title_profile.xml b/core-ui/src/main/res/layout/item_block_title_profile.xml index e4de82a13d..ff53f0efce 100644 --- a/core-ui/src/main/res/layout/item_block_title_profile.xml +++ b/core-ui/src/main/res/layout/item_block_title_profile.xml @@ -11,32 +11,40 @@ android:layout_width="0dp" android:layout_height="@dimen/default_cover_height" android:contentDescription="@string/content_description_document_cover" + android:scaleType="centerCrop" android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> - + app:layout_constraintTop_toTopOf="parent"> + + + diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt index d1f7f3b958..2ab70b0f65 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt @@ -765,6 +765,7 @@ sealed class BlockView : ViewType { override var cursor: Int? = null, override val searchFields: List = emptyList(), override val hint: String? = null, + val icon: ObjectIcon ) : Title(), Searchable { override fun getViewType() = HOLDER_PROFILE_TITLE } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt index 820a17d7c3..d22dd99915 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt @@ -1506,7 +1506,8 @@ class DefaultBlockViewRenderer @Inject constructor( coverImage = coverContainer.coverImage, coverGradient = coverContainer.coverGradient, background = block.parseThemeBackgroundColor(), - color = block.textColor() + color = block.textColor(), + icon = currentObject.objectIcon(builder = urlBuilder, objType = objType) ) } ObjectType.Layout.VIDEO -> { From 5ddf6bb5171e85938cf9905ddb03eedf45d41700 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 16:51:38 +0200 Subject: [PATCH 09/24] DROID-3805 icon fixes --- .../core_ui/features/editor/holders/other/Title.kt | 7 ------- .../anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt | 6 +++++- core-ui/src/main/res/layout/item_block_title_file.xml | 8 ++++---- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 67a5df2f0c..e5f7902473 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -473,13 +473,6 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { override val root: View = itemView override val content: TextInputWidget = binding.title - init { - icon.binding.ivImage.updateLayoutParams { - height = itemView.resources.getDimension(R.dimen.dp_80).toInt() - width = itemView.resources.getDimension(R.dimen.dp_64).toInt() - } - } - fun bind( item: BlockView.Title.File, ) { diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt index 0b8ef8050a..32b943441b 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt @@ -11,6 +11,7 @@ import android.view.LayoutInflater import android.view.View import android.widget.FrameLayout import android.widget.ImageView +import androidx.compose.ui.unit.dp import androidx.core.view.updateLayoutParams import com.anytypeio.anytype.core_models.Url import com.anytypeio.anytype.core_ui.R @@ -399,7 +400,10 @@ class ObjectIconWidget @JvmOverloads constructor( val backgroundDrawable = GradientDrawable().apply { shape = GradientDrawable.RECTANGLE setColor(context.color(R.color.shape_transparent_secondary)) - cornerRadius = getCornerRadiusInPx() + cornerRadius = if (imageCornerRadius > 2) + imageCornerRadius + else + getCornerRadiusInPx() } val layers = arrayOf(backgroundDrawable, iconDrawable) diff --git a/core-ui/src/main/res/layout/item_block_title_file.xml b/core-ui/src/main/res/layout/item_block_title_file.xml index fc8509ea10..35df724c9c 100644 --- a/core-ui/src/main/res/layout/item_block_title_file.xml +++ b/core-ui/src/main/res/layout/item_block_title_file.xml @@ -17,16 +17,16 @@ From d2d4d47245bb4093d51717e9fabb98816ffe714e Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 17:02:14 +0200 Subject: [PATCH 10/24] DROID-3805 top icons fixes --- .../anytypeio/anytype/ui/editor/EditorFragment.kt | 2 +- .../core_ui/features/editor/holders/other/Title.kt | 1 - .../core_ui/widgets/toolbar/ObjectTopToolbar.kt | 3 +-- .../main/res/layout/widget_object_top_toolbar.xml | 13 ++++++------- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index 551a39bfed..a26c062278 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -665,7 +665,7 @@ open class EditorFragment : NavigationFragment(R.layout.f .onEach { vm.onDocumentMenuClicked() } .launchIn(lifecycleScope) - binding.topToolbar.back + binding.topToolbar.backContainer .clicks() .throttleFirst() .onEach { vm.onBackButtonPressed() } diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index e5f7902473..b5f3a42528 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -5,7 +5,6 @@ import android.text.Spannable import android.util.TypedValue import android.view.View import android.widget.FrameLayout -import android.widget.FrameLayout.LayoutParams import android.widget.ImageView import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt index e51d910cd5..3cc9cf0af2 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt @@ -13,8 +13,6 @@ import com.anytypeio.anytype.core_ui.R import com.anytypeio.anytype.core_ui.databinding.WidgetObjectTopToolbarBinding import com.anytypeio.anytype.core_ui.widgets.ObjectIconWidget import com.anytypeio.anytype.core_ui.widgets.StatusBadgeWidget -import com.anytypeio.anytype.core_utils.ext.invisible -import com.anytypeio.anytype.core_utils.ext.visible class ObjectTopToolbar @JvmOverloads constructor( context: Context, @@ -26,6 +24,7 @@ class ObjectTopToolbar @JvmOverloads constructor( ) val status: StatusBadgeWidget get() = binding.statusBadge + val backContainer: View get() = binding.topBackButton val back: View get() = binding.ivTopBackButton val menu: View get() = binding.threeDotsButton val container: ViewGroup get() = binding.titleContainer diff --git a/core-ui/src/main/res/layout/widget_object_top_toolbar.xml b/core-ui/src/main/res/layout/widget_object_top_toolbar.xml index 153eed6137..f7d5c2110b 100644 --- a/core-ui/src/main/res/layout/widget_object_top_toolbar.xml +++ b/core-ui/src/main/res/layout/widget_object_top_toolbar.xml @@ -86,16 +86,15 @@ + android:layout_width="38dp" + android:layout_height="match_parent" + android:layout_gravity="end|center_vertical"> From 3e99409c12c0ac6da8da56d819599bd3c1f635b8 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 17:30:42 +0200 Subject: [PATCH 11/24] DROID-3805 fix --- .../anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt index 3cc9cf0af2..79a00cd074 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt @@ -40,10 +40,10 @@ class ObjectTopToolbar @JvmOverloads constructor( overCover: Boolean ) = with(binding) { if (overCover) { - menu.setBackgroundResource(R.drawable.rect_object_menu_button_default) + //ivThreeDots.setBackgroundResource(R.drawable.rect_object_menu_button_default) //topBackButton.setBackgroundResource(R.drawable.rect_object_menu_button_default) statusBadge.setBackgroundResource(R.drawable.rect_object_menu_button_default) - ivThreeDots.imageTintList = ColorStateList.valueOf(Color.WHITE) + //ivThreeDots.imageTintList = ColorStateList.valueOf(Color.WHITE) ivTopBackButton.imageTintList = ColorStateList.valueOf(Color.WHITE) } else { menu.background = null From b84caa056f4bbbc89ab3b79ed2ba6d23d2e8bfc4 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 17:37:37 +0200 Subject: [PATCH 12/24] DROID-3805 fix --- .../tools/EditorHeaderOverlayDetector.kt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/tools/EditorHeaderOverlayDetector.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/tools/EditorHeaderOverlayDetector.kt index 5a54d21543..7c6f60a02a 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/tools/EditorHeaderOverlayDetector.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/tools/EditorHeaderOverlayDetector.kt @@ -32,18 +32,12 @@ class EditorHeaderOverlayDetector( val root = holder.binding.root val title = holder.binding.title val cover = holder.binding.cover - val iconWidget = holder.binding.objectIconWidget - onHeaderOverlaid = when { - iconWidget.isVisible -> { - if (cover.isVisible) { - (iconWidget.top + root.top >= threshold + thresholdPadding) - } else { - (iconWidget.top + root.top >= (threshold / 2) + thresholdPadding) - } - } - else -> { - root.top + title.top >= threshold + thresholdPadding - } + onHeaderOverlaid = if (cover.isVisible) { + // When cover is visible, check if title top is below threshold + (root.top + title.top >= threshold + thresholdPadding) + } else { + // When cover is not visible, check if title top is below half threshold + (root.top + title.top >= (threshold / 2) + thresholdPadding) } } is Title.Todo -> { From 1f7832ccdf6735e8aa26ef395c7171414e9cf2ad Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 17:51:30 +0200 Subject: [PATCH 13/24] DROID-3805 edge to edge fixes --- .../anytype/ui/editor/EditorFragment.kt | 149 +++++++++++++++++- 1 file changed, 147 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index a26c062278..f26a98dc97 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -10,11 +10,13 @@ import android.graphics.Point import android.net.Uri import android.os.Build import android.os.Bundle +import android.util.Log import android.view.ContextThemeWrapper import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import android.view.animation.DecelerateInterpolator import android.view.animation.LinearInterpolator import android.view.animation.OvershootInterpolator @@ -44,6 +46,7 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible +import androidx.core.view.setPadding import androidx.core.view.updateLayoutParams import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -431,6 +434,8 @@ open class EditorFragment : NavigationFragment(R.layout.f threshold = dimen(R.dimen.default_toolbar_height), thresholdPadding = dimen(R.dimen.dp_8) ) { isHeaderOverlaid -> + Log.d("Test1983", "Header overlaid: $isHeaderOverlaid") + updateStatusBarForHeaderOverlay(isHeaderOverlaid) if (isHeaderOverlaid) { binding.topToolbar.setBackgroundColor(0) binding.topToolbar.container.animate().alpha(0f) @@ -445,7 +450,7 @@ open class EditorFragment : NavigationFragment(R.layout.f } } } else { - binding.topToolbar.setBackgroundColor(requireContext().color(R.color.defaultCanvasColor)) + binding.topToolbar.setBackgroundColor(requireContext().color(R.color.background_primary)) binding.topToolbar.container.animate().alpha(1f) .setDuration(DEFAULT_TOOLBAR_ANIM_DURATION) .start() @@ -555,6 +560,7 @@ open class EditorFragment : NavigationFragment(R.layout.f override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setupEdgeToEdge() setupWindowInsetAnimation() dndDelegate.init(blockAdapter, vm, this) @@ -862,8 +868,147 @@ open class EditorFragment : NavigationFragment(R.layout.f } } + private fun setupEdgeToEdge() { + // Start with transparent status bar for edge-to-edge experience + requireActivity().window.apply { + statusBarColor = android.graphics.Color.TRANSPARENT + } + + // Apply window insets to handle edge-to-edge display + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets -> + val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + val ime = insets.getInsets(WindowInsetsCompat.Type.ime()) + + // Apply top padding to top toolbar for status bar + binding.topToolbar.updateLayoutParams { + topMargin = systemBars.top + } +// +// // Apply top padding to search toolbar for status bar +// binding.searchToolbar.updateLayoutParams { +// topMargin = systemBars.top +// } +// +// // Apply top padding to multi-select toolbar for status bar +// binding.multiSelectTopToolbar.updateLayoutParams { +// topMargin = systemBars.top +// } +// +// // Apply top padding to scroll and move hint for status bar +// binding.scrollAndMoveHint.updateLayoutParams { +// topMargin = systemBars.top +// } +// +// // Apply padding to recycler view for navigation bar +// binding.recycler.setPadding( +// binding.recycler.paddingLeft, +// binding.recycler.paddingTop, +// binding.recycler.paddingRight, +// systemBars.bottom + dimen(R.dimen.editor_recycler_bottom_padding) +// ) +// +// // Apply bottom margin to bottom navigation panel +// binding.bottomToolbarContainer.updateLayoutParams { +// bottomMargin = systemBars.bottom + dimen(R.dimen.dp_20) +// } +// +// // Apply bottom padding to toolbars +// val bottomPadding = if (ime.bottom > 0) ime.bottom else systemBars.bottom +// binding.toolbar.setPadding( +// binding.toolbar.paddingLeft, +// binding.toolbar.paddingTop, +// binding.toolbar.paddingRight, +// bottomPadding +// ) +// binding.markupToolbar.setPadding( +// binding.markupToolbar.paddingLeft, +// binding.markupToolbar.paddingTop, +// binding.markupToolbar.paddingRight, +// bottomPadding +// ) +// +// // Handle Compose views at the bottom +// binding.chooseTypeWidget.updateLayoutParams { +// bottomMargin = bottomPadding +// } +// binding.syncStatusWidget.updateLayoutParams { +// bottomMargin = if (ime.bottom > 0) 0 else systemBars.bottom +// } +// binding.editorDatePicker.updateLayoutParams { +// bottomMargin = bottomPadding +// } +// binding.attachToChatPanel.updateLayoutParams { +// bottomMargin = bottomPadding +// } +// +// // Apply bottom padding to other bottom toolbars +// binding.mentionSuggesterToolbar.setPadding( +// binding.mentionSuggesterToolbar.paddingLeft, +// binding.mentionSuggesterToolbar.paddingTop, +// binding.mentionSuggesterToolbar.paddingRight, +// bottomPadding +// ) +// binding.slashWidget.setPadding( +// binding.slashWidget.paddingLeft, +// binding.slashWidget.paddingTop, +// binding.slashWidget.paddingRight, +// bottomPadding +// ) +// binding.scrollAndMoveBottomAction.setPadding( +// binding.scrollAndMoveBottomAction.paddingLeft, +// binding.scrollAndMoveBottomAction.paddingTop, +// binding.scrollAndMoveBottomAction.paddingRight, +// systemBars.bottom +// ) +// +// // Apply bottom margin to the bottom sheets container +// binding.panels.setPadding( +// binding.panels.paddingLeft, +// binding.panels.paddingTop, +// binding.panels.paddingRight, +// systemBars.bottom +// ) + + //insets + WindowInsetsCompat.CONSUMED + } + } + + private fun updateStatusBarForHeaderOverlay(isHeaderOverlaid: Boolean) { + requireActivity().window.apply { + if (isHeaderOverlaid) { + // When header is not overlaid (scrolled up), show transparent status bar + statusBarColor = android.graphics.Color.TRANSPARENT + } else { + // When header is overlaid (visible), show status bar with background + statusBarColor = requireContext().color(R.color.background_primary) + } + } + + // Update status bar icon color based on current state and theme + val windowInsetsController = ViewCompat.getWindowInsetsController(requireActivity().window.decorView) + val uiMode = requireContext().resources.configuration.uiMode + val isDarkMode = (uiMode and android.content.res.Configuration.UI_MODE_NIGHT_MASK) == android.content.res.Configuration.UI_MODE_NIGHT_YES + + if (isHeaderOverlaid) { + // When showing transparent status bar, adjust icons based on content + val firstView = blockAdapter.views.firstOrNull() + val hasCover = firstView is BlockView.Title && firstView.hasCover + windowInsetsController?.isAppearanceLightStatusBars = if (hasCover) { + // For covers, usually need light icons + false + } else { + // For regular content, follow theme + !isDarkMode + } + } else { + // When showing colored status bar, follow theme + windowInsetsController?.isAppearanceLightStatusBars = !isDarkMode + } + } + open fun setupWindowInsetAnimation() { - if (BuildConfig.USE_NEW_WINDOW_INSET_API && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { binding.toolbar.syncTranslationWithImeVisibility( dispatchMode = DISPATCH_MODE_STOP ) From f02f01f52094a45a6bf91a6cb02dd10fee0da6d8 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 18:00:47 +0200 Subject: [PATCH 14/24] DROID-3805 top bar color --- .../anytypeio/anytype/ui/editor/EditorFragment.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index f26a98dc97..7a3b155e57 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -975,14 +975,13 @@ open class EditorFragment : NavigationFragment(R.layout.f } private fun updateStatusBarForHeaderOverlay(isHeaderOverlaid: Boolean) { - requireActivity().window.apply { - if (isHeaderOverlaid) { - // When header is not overlaid (scrolled up), show transparent status bar - statusBarColor = android.graphics.Color.TRANSPARENT - } else { - // When header is overlaid (visible), show status bar with background - statusBarColor = requireContext().color(R.color.background_primary) - } + // Control topToolbar background visibility based on header overlay state + if (isHeaderOverlaid) { + // When header is not overlaid (scrolled up), make topToolbar transparent + binding.topToolbar.setBackgroundColor(android.graphics.Color.TRANSPARENT) + } else { + // When header is overlaid (visible), show topToolbar with background + binding.topToolbar.setBackgroundColor(requireContext().color(R.color.background_primary)) } // Update status bar icon color based on current state and theme From c9de04a3362be634f170aaf2b138ea84c3d57bd3 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 18:01:06 +0200 Subject: [PATCH 15/24] DROID-3805 fix --- .../anytype/ui/editor/EditorFragment.kt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index 7a3b155e57..f76807fd6f 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -869,7 +869,7 @@ open class EditorFragment : NavigationFragment(R.layout.f } private fun setupEdgeToEdge() { - // Start with transparent status bar for edge-to-edge experience + // Enable edge-to-edge display with transparent status bar requireActivity().window.apply { statusBarColor = android.graphics.Color.TRANSPARENT } @@ -877,13 +877,21 @@ open class EditorFragment : NavigationFragment(R.layout.f // Apply window insets to handle edge-to-edge display ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + val statusBars = insets.getInsets(WindowInsetsCompat.Type.statusBars()) val ime = insets.getInsets(WindowInsetsCompat.Type.ime()) - // Apply top padding to top toolbar for status bar + // Extend topToolbar to include status bar area binding.topToolbar.updateLayoutParams { - topMargin = systemBars.top - } -// + height = dimen(R.dimen.default_toolbar_height) + statusBars.top + } + // Add top padding to topToolbar content to push it below status bar + binding.topToolbar.setPadding( + binding.topToolbar.paddingLeft, + statusBars.top, + binding.topToolbar.paddingRight, + binding.topToolbar.paddingBottom + ) + // // Apply top padding to search toolbar for status bar // binding.searchToolbar.updateLayoutParams { // topMargin = systemBars.top From 7f19bbb1fda74936c19e6b6f43fcecbbffda27c2 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 18:11:01 +0200 Subject: [PATCH 16/24] DROID-3805 title icon preview --- .../anytype/ui/editor/EditorFragment.kt | 42 ++++--------------- .../res/layout/widget_object_top_toolbar.xml | 1 - 2 files changed, 9 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index f76807fd6f..912a43f3e6 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -869,11 +869,7 @@ open class EditorFragment : NavigationFragment(R.layout.f } private fun setupEdgeToEdge() { - // Enable edge-to-edge display with transparent status bar - requireActivity().window.apply { - statusBarColor = android.graphics.Color.TRANSPARENT - } - + // Apply window insets to handle edge-to-edge display ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) @@ -1683,10 +1679,10 @@ open class EditorFragment : NavigationFragment(R.layout.f if (title != null) { when (title) { is BlockView.Title.Basic -> { -// resetTopToolbarTitle( -// text = title.text, -// icon = title.icon, -// ) + resetTopToolbarTitle( + text = title.text, + icon = title.icon + ) if (title.hasCover) { val mng = binding.recycler.layoutManager as LinearLayoutManager val pos = mng.findFirstVisibleItemPosition() @@ -1700,8 +1696,7 @@ open class EditorFragment : NavigationFragment(R.layout.f is BlockView.Title.Profile -> { resetTopToolbarTitle( text = title.text, - emoji = null, - image = title.image, + icon = title.icon ) if (title.hasCover) { val mng = binding.recycler.layoutManager as LinearLayoutManager @@ -1716,8 +1711,7 @@ open class EditorFragment : NavigationFragment(R.layout.f is BlockView.Title.Todo -> { resetTopToolbarTitle( text = title.text, - emoji = null, - image = title.image, + icon = title.icon ) if (title.hasCover) { val mng = binding.recycler.layoutManager as LinearLayoutManager @@ -1735,27 +1729,9 @@ open class EditorFragment : NavigationFragment(R.layout.f } } - private fun resetTopToolbarTitle(text: String?, emoji: String?, image: String?) { + private fun resetTopToolbarTitle(text: String?, icon: ObjectIcon) { binding.topToolbar.title.text = text - val iconView = binding.topToolbar.icon - when { - text.isNullOrBlank() -> { - iconView.setIcon(ObjectIcon.None) - iconView.gone() - } - !emoji.isNullOrBlank() -> { - iconView.setIcon(ObjectIcon.Basic.Emoji(emoji)) - iconView.visible() - } - !image.isNullOrBlank() -> { - iconView.setIcon(ObjectIcon.Basic.Image(image)) - iconView.visible() - } - else -> { - iconView.setIcon(ObjectIcon.None) - iconView.gone() - } - } + binding.topToolbar.icon.setIcon(icon) } open fun render(state: ControlPanelState) { diff --git a/core-ui/src/main/res/layout/widget_object_top_toolbar.xml b/core-ui/src/main/res/layout/widget_object_top_toolbar.xml index f7d5c2110b..4f7946ca7d 100644 --- a/core-ui/src/main/res/layout/widget_object_top_toolbar.xml +++ b/core-ui/src/main/res/layout/widget_object_top_toolbar.xml @@ -52,7 +52,6 @@ app:emojiSize="18dp" app:checkboxSize="18dp" app:initialTextSize="11sp" - android:visibility="gone" tools:visibility="visible" /> Date: Tue, 5 Aug 2025 19:03:24 +0200 Subject: [PATCH 17/24] DROID-3805 fix --- .../anytype/ui/editor/EditorFragment.kt | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index 912a43f3e6..5dceb260a7 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -2206,7 +2206,7 @@ open class EditorFragment : NavigationFragment(R.layout.f showTargeterWithAnimation() binding.recycler.addOnScrollListener(scrollAndMoveStateListener) - binding.multiSelectTopToolbar.invisible() + binding.multiSelectTopToolbar.gone() showTopScrollAndMoveToolbar() binding.scrollAndMoveBottomAction.show() @@ -2249,26 +2249,32 @@ open class EditorFragment : NavigationFragment(R.layout.f } private fun hideSelectButton() { - if (binding.multiSelectTopToolbar.translationY >= 0) { + if (binding.multiSelectTopToolbar.alpha > 0f) { ObjectAnimator.ofFloat( binding.multiSelectTopToolbar, - SELECT_BUTTON_ANIMATION_PROPERTY, - -requireContext().dimen(R.dimen.dp_48) + "alpha", + 0f ).apply { duration = SELECT_BUTTON_HIDE_ANIMATION_DURATION interpolator = DecelerateInterpolator() - doOnEnd { if (hasBinding) binding.topToolbar.visible() } + doOnEnd { + if (hasBinding) { + binding.multiSelectTopToolbar.gone() + binding.topToolbar.visible() + } + } start() } } } private fun showSelectButton() { - if (binding.multiSelectTopToolbar.translationY < 0) { + if (binding.multiSelectTopToolbar.alpha < 1f) { + binding.multiSelectTopToolbar.visible() ObjectAnimator.ofFloat( binding.multiSelectTopToolbar, - SELECT_BUTTON_ANIMATION_PROPERTY, - 0f + "alpha", + 1f ).apply { duration = SELECT_BUTTON_SHOW_ANIMATION_DURATION interpolator = DecelerateInterpolator() From 23b0fe22056e7133d6141be3a6a46855bbe180cc Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 19:08:25 +0200 Subject: [PATCH 18/24] DROID-3805 legacy --- .../widgets/ScrollAndMoveHintWidget.kt | 66 ------------------- .../src/main/res/layout/widget_sam_hint.xml | 11 ---- 2 files changed, 77 deletions(-) delete mode 100644 core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ScrollAndMoveHintWidget.kt delete mode 100644 core-ui/src/main/res/layout/widget_sam_hint.xml diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ScrollAndMoveHintWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ScrollAndMoveHintWidget.kt deleted file mode 100644 index f659f43545..0000000000 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ScrollAndMoveHintWidget.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.anytypeio.anytype.core_ui.widgets - -import android.animation.ObjectAnimator -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.animation.AccelerateInterpolator -import android.view.animation.DecelerateInterpolator -import android.widget.FrameLayout -import androidx.core.animation.doOnEnd -import androidx.core.animation.doOnStart -import com.anytypeio.anytype.core_ui.R -import com.anytypeio.anytype.core_utils.ext.invisible -import com.anytypeio.anytype.core_utils.ext.visible - -class ScrollAndMoveHintWidget : FrameLayout { - - constructor(context: Context) : this(context, null) - constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) - constructor( - context: Context, - attrs: AttributeSet?, - defStyleAttr: Int - ) : super(context, attrs, defStyleAttr) { - init() - } - - fun init() { - LayoutInflater.from(context).inflate(R.layout.widget_sam_hint, this) - } - - fun showWithAnimation() { - ObjectAnimator.ofFloat( - this, - ANIMATED_PROPERTY, - TRANSLATION_INVISIBLE, - TRANSLATION_VISIBLE - ).apply { - duration = ANIMATION_DURATION - interpolator = DecelerateInterpolator() - doOnStart { visible() } - start() - } - } - - fun hideWithAnimation() { - ObjectAnimator.ofFloat( - this, - ANIMATED_PROPERTY, - TRANSLATION_VISIBLE, - TRANSLATION_INVISIBLE - ).apply { - duration = ANIMATION_DURATION - interpolator = AccelerateInterpolator() - doOnEnd { invisible() } - start() - } - } - - companion object { - const val ANIMATION_DURATION = 300L - const val ANIMATED_PROPERTY = "translationY" - const val TRANSLATION_VISIBLE = 0f - const val TRANSLATION_INVISIBLE = -1000f - } -} \ No newline at end of file diff --git a/core-ui/src/main/res/layout/widget_sam_hint.xml b/core-ui/src/main/res/layout/widget_sam_hint.xml deleted file mode 100644 index 24c3b0c011..0000000000 --- a/core-ui/src/main/res/layout/widget_sam_hint.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - \ No newline at end of file From 3a0fd210e94328ec20b45e3399fe422bc473346f Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 19:10:39 +0200 Subject: [PATCH 19/24] DROID-3805 legacy --- .../anytype/ui/editor/EditorFragment.kt | 26 ------------------- app/src/main/res/layout/fragment_editor.xml | 12 +-------- 2 files changed, 1 insertion(+), 37 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index 5dceb260a7..2bdc8459f4 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -2208,7 +2208,6 @@ open class EditorFragment : NavigationFragment(R.layout.f binding.recycler.addOnScrollListener(scrollAndMoveStateListener) binding.multiSelectTopToolbar.gone() - showTopScrollAndMoveToolbar() binding.scrollAndMoveBottomAction.show() hideBlockActionPanel() @@ -2242,7 +2241,6 @@ open class EditorFragment : NavigationFragment(R.layout.f removeItemDecoration(scrollAndMoveTargetHighlighter) removeOnScrollListener(scrollAndMoveStateListener) } - hideTopScrollAndMoveToolbar() binding.scrollAndMoveBottomAction.hide() binding.targeter.invisible() scrollAndMoveTargetDescriptor.clear() @@ -2283,30 +2281,6 @@ open class EditorFragment : NavigationFragment(R.layout.f } } - private fun hideTopScrollAndMoveToolbar() { - ObjectAnimator.ofFloat( - binding.scrollAndMoveHint, - SELECT_BUTTON_ANIMATION_PROPERTY, - -requireContext().dimen(R.dimen.dp_48) - ).apply { - duration = SELECT_BUTTON_HIDE_ANIMATION_DURATION - interpolator = DecelerateInterpolator() - start() - } - } - - private fun showTopScrollAndMoveToolbar() { - ObjectAnimator.ofFloat( - binding.scrollAndMoveHint, - SELECT_BUTTON_ANIMATION_PROPERTY, - 0f - ).apply { - duration = SELECT_BUTTON_SHOW_ANIMATION_DURATION - interpolator = DecelerateInterpolator() - start() - } - } - override fun onClipboardAction(action: ClipboardInterceptor.Action) { when (action) { is ClipboardInterceptor.Action.Copy -> vm.onCopy(action.selection) diff --git a/app/src/main/res/layout/fragment_editor.xml b/app/src/main/res/layout/fragment_editor.xml index 9ae27a19c0..8ec316c1db 100644 --- a/app/src/main/res/layout/fragment_editor.xml +++ b/app/src/main/res/layout/fragment_editor.xml @@ -53,7 +53,7 @@ android:layout_width="0dp" android:layout_height="48dp" android:background="@color/defaultCanvasColor" - android:translationY="-48dp" + android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -99,16 +99,6 @@ app:layout_constraintStart_toStartOf="parent" tools:visibility="visible" /> - - Date: Tue, 5 Aug 2025 19:24:34 +0200 Subject: [PATCH 20/24] DROID-3805 legacy --- .../anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt index 79a00cd074..8c32f33fd0 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/toolbar/ObjectTopToolbar.kt @@ -1,8 +1,6 @@ package com.anytypeio.anytype.core_ui.widgets.toolbar import android.content.Context -import android.content.res.ColorStateList -import android.graphics.Color import android.util.AttributeSet import android.view.LayoutInflater import android.view.View @@ -40,16 +38,10 @@ class ObjectTopToolbar @JvmOverloads constructor( overCover: Boolean ) = with(binding) { if (overCover) { - //ivThreeDots.setBackgroundResource(R.drawable.rect_object_menu_button_default) - //topBackButton.setBackgroundResource(R.drawable.rect_object_menu_button_default) statusBadge.setBackgroundResource(R.drawable.rect_object_menu_button_default) - //ivThreeDots.imageTintList = ColorStateList.valueOf(Color.WHITE) - ivTopBackButton.imageTintList = ColorStateList.valueOf(Color.WHITE) } else { menu.background = null topBackButton.background = null - ivTopBackButton.imageTintList = null - ivThreeDots.imageTintList = null statusBadge.background = null } } From 77de14d4b34e88ca6f4072d53223beaaf67dea72 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Tue, 5 Aug 2025 19:42:44 +0200 Subject: [PATCH 21/24] DROID-3805 fixes --- .../anytype/ui/editor/EditorFragment.kt | 124 +++++++++++------- 1 file changed, 75 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt index 2bdc8459f4..582afbe261 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/editor/EditorFragment.kt @@ -44,6 +44,8 @@ import androidx.core.animation.doOnStart import androidx.core.os.bundleOf import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP +import androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE +import androidx.core.view.WindowInsetsAnimationCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible import androidx.core.view.setPadding @@ -560,7 +562,7 @@ open class EditorFragment : NavigationFragment(R.layout.f override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setupEdgeToEdge() + setupEdgeToEdge(view) setupWindowInsetAnimation() dndDelegate.init(blockAdapter, vm, this) @@ -868,10 +870,10 @@ open class EditorFragment : NavigationFragment(R.layout.f } } - private fun setupEdgeToEdge() { + private fun setupEdgeToEdge(view: View) { // Apply window insets to handle edge-to-edge display - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets -> + ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) val statusBars = insets.getInsets(WindowInsetsCompat.Type.statusBars()) val ime = insets.getInsets(WindowInsetsCompat.Type.ime()) @@ -888,48 +890,32 @@ open class EditorFragment : NavigationFragment(R.layout.f binding.topToolbar.paddingBottom ) -// // Apply top padding to search toolbar for status bar -// binding.searchToolbar.updateLayoutParams { -// topMargin = systemBars.top -// } -// -// // Apply top padding to multi-select toolbar for status bar -// binding.multiSelectTopToolbar.updateLayoutParams { -// topMargin = systemBars.top -// } -// -// // Apply top padding to scroll and move hint for status bar -// binding.scrollAndMoveHint.updateLayoutParams { -// topMargin = systemBars.top -// } -// -// // Apply padding to recycler view for navigation bar -// binding.recycler.setPadding( -// binding.recycler.paddingLeft, -// binding.recycler.paddingTop, -// binding.recycler.paddingRight, -// systemBars.bottom + dimen(R.dimen.editor_recycler_bottom_padding) -// ) -// -// // Apply bottom margin to bottom navigation panel -// binding.bottomToolbarContainer.updateLayoutParams { -// bottomMargin = systemBars.bottom + dimen(R.dimen.dp_20) -// } -// -// // Apply bottom padding to toolbars -// val bottomPadding = if (ime.bottom > 0) ime.bottom else systemBars.bottom -// binding.toolbar.setPadding( -// binding.toolbar.paddingLeft, -// binding.toolbar.paddingTop, -// binding.toolbar.paddingRight, -// bottomPadding -// ) -// binding.markupToolbar.setPadding( -// binding.markupToolbar.paddingLeft, -// binding.markupToolbar.paddingTop, -// binding.markupToolbar.paddingRight, -// bottomPadding -// ) + // Apply top padding to search toolbar for status bar + binding.searchToolbar.updateLayoutParams { + topMargin = systemBars.top + } + + // Apply top padding to multi-select toolbar for status bar + binding.multiSelectTopToolbar.updateLayoutParams { + topMargin = systemBars.top + } + + // Handle BlockToolbarWidget positioning with IME + val imeHeight = ime.bottom + if (imeHeight > 0) { + // IME is visible, position toolbar above it + binding.toolbar.visibility = View.VISIBLE + binding.toolbar.translationY = -imeHeight.toFloat() + } else { + // IME is hidden, hide toolbar or position at bottom + binding.toolbar.translationY = 0f + } + + // Handle other bottom toolbars + val bottomPadding = if (ime.bottom > 0) ime.bottom else systemBars.bottom + binding.markupToolbar.updateLayoutParams { + bottomMargin = bottomPadding + } // // // Handle Compose views at the bottom // binding.chooseTypeWidget.updateLayoutParams { @@ -976,6 +962,46 @@ open class EditorFragment : NavigationFragment(R.layout.f //insets WindowInsetsCompat.CONSUMED } + + // Animate toolbar in sync with IME + ViewCompat.setWindowInsetsAnimationCallback( + view, + object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) { + + override fun onPrepare(animation: WindowInsetsAnimationCompat) { + super.onPrepare(animation) + // Ensure toolbar is visible when IME animation starts + if (animation.typeMask and WindowInsetsCompat.Type.ime() != 0) { + binding.toolbar.visibility = View.VISIBLE + } + } + + override fun onProgress( + insets: WindowInsetsCompat, + runningAnimations: MutableList + ): WindowInsetsCompat { + val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom + + // Always keep toolbar visible during animation + // Only animate the translation + binding.toolbar.translationY = if (imeHeight > 0) -imeHeight.toFloat() else 0f + + return insets + } + + override fun onEnd(animation: WindowInsetsAnimationCompat) { + super.onEnd(animation) + // Only hide toolbar after animation completely ends and IME is hidden + if (animation.typeMask and WindowInsetsCompat.Type.ime() != 0) { + val currentImeHeight = ViewCompat.getRootWindowInsets(view) + ?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0 + if (currentImeHeight == 0) { + binding.toolbar.visibility = View.INVISIBLE + } + } + } + } + ) } private fun updateStatusBarForHeaderOverlay(isHeaderOverlaid: Boolean) { @@ -1012,9 +1038,9 @@ open class EditorFragment : NavigationFragment(R.layout.f open fun setupWindowInsetAnimation() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - binding.toolbar.syncTranslationWithImeVisibility( - dispatchMode = DISPATCH_MODE_STOP - ) +// binding.toolbar.syncTranslationWithImeVisibility( +// dispatchMode = DISPATCH_MODE_STOP +// ) binding.chooseTypeWidget.syncTranslationWithImeVisibility( dispatchMode = DISPATCH_MODE_STOP ) @@ -1757,7 +1783,7 @@ open class EditorFragment : NavigationFragment(R.layout.f Main.TargetBlockType.Description -> BlockToolbarWidget.State.Description } } else { - binding.toolbar.invisible() + //binding.toolbar.invisible() } setMainMarkupToolbarState(state) From 9b3ae6b8c29b960ba0de9a2f4a9f899f3df9703d Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Wed, 13 Aug 2025 18:20:10 +0200 Subject: [PATCH 22/24] DROID-3805 fix --- core-ui/src/main/res/layout/item_block_title_profile.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/core-ui/src/main/res/layout/item_block_title_profile.xml b/core-ui/src/main/res/layout/item_block_title_profile.xml index f5fd5309a2..323265fa90 100644 --- a/core-ui/src/main/res/layout/item_block_title_profile.xml +++ b/core-ui/src/main/res/layout/item_block_title_profile.xml @@ -11,7 +11,6 @@ android:layout_width="0dp" android:layout_height="@dimen/default_cover_height" android:contentDescription="@string/content_description_document_cover" - android:scaleType="centerCrop" android:visibility="gone" android:scaleType="centerCrop" app:layout_constraintEnd_toEndOf="parent" From ec54ee4cf4366b9381d3ed23fd9f23c25cf20af3 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Wed, 13 Aug 2025 18:51:55 +0200 Subject: [PATCH 23/24] DROID-3805 object fragment --- .../anytype/ui/objects/ObjectFragment.kt | 96 +++++ app/src/main/res/layout/fragment_object.xml | 380 ++++++++++++++++++ 2 files changed, 476 insertions(+) create mode 100644 app/src/main/java/com/anytypeio/anytype/ui/objects/ObjectFragment.kt create mode 100644 app/src/main/res/layout/fragment_object.xml diff --git a/app/src/main/java/com/anytypeio/anytype/ui/objects/ObjectFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/objects/ObjectFragment.kt new file mode 100644 index 0000000000..2496b5aff4 --- /dev/null +++ b/app/src/main/java/com/anytypeio/anytype/ui/objects/ObjectFragment.kt @@ -0,0 +1,96 @@ +package com.anytypeio.anytype.ui.objects + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.anytypeio.anytype.R +import com.anytypeio.anytype.databinding.FragmentObjectBinding + +class ObjectFragment : Fragment(R.layout.fragment_object) { + + private var _binding: FragmentObjectBinding? = null + private val binding get() = _binding!! + + override fun onViewCreated(view: android.view.View, savedInstanceState: android.os.Bundle?) { + super.onViewCreated(view, savedInstanceState) + _binding = FragmentObjectBinding.bind(view) + + // Temporary: show recycler to verify layout + binding.recycler.visibility = View.VISIBLE + + val adapter = DemoAdapter() + binding.recycler.adapter = adapter + adapter.submitList(listOf("Heading", "Paragraph", "Checklist", "Code Block")) + + // Enable edge-to-edge for system bars + val window = requireActivity().window + androidx.core.view.WindowCompat.setDecorFitsSystemWindows(window, false) + + // Make status bar icons dark on light backgrounds (toggle as needed when testing) + val controller = androidx.core.view.WindowInsetsControllerCompat(window, binding.root) + controller.isAppearanceLightStatusBars = true + + // Give the top cover a random background so changes are obvious during testing + val rnd = kotlin.random.Random(System.currentTimeMillis()) + val color = + android.graphics.Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256)) + binding.topEdgeCover.setBackgroundColor(color) + + // Apply WindowInsets to size the top cover to the status bar height and + // provide bottom padding for the sheet (recycler + toolbars) vs nav/IME. + androidx.core.view.ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets -> + val status = insets.getInsets(androidx.core.view.WindowInsetsCompat.Type.statusBars()) + val nav = insets.getInsets(androidx.core.view.WindowInsetsCompat.Type.navigationBars()) + val ime = insets.getInsets(androidx.core.view.WindowInsetsCompat.Type.ime()) + + // Match the top cover height to status bar height + binding.topEdgeCover.layoutParams = binding.topEdgeCover.layoutParams.apply { + height = status.top + } + binding.topEdgeCover.requestLayout() + + // Pad the content bottom by the largest of nav bar or IME + val bottom = maxOf(nav.bottom, ime.bottom) + binding.sheet.setPadding( + binding.sheet.paddingLeft, + binding.sheet.paddingTop, + binding.sheet.paddingRight, + bottom + ) + + insets + } + } + + override fun onDestroyView() { + _binding = null + super.onDestroyView() + } +} + + +private class DemoAdapter : ListAdapter(diff) { + override fun onCreateViewHolder(p: ViewGroup, vt: Int) = + DemoVH(LayoutInflater.from(p.context).inflate(R.layout.item_block_title, p, false)) + + override fun onBindViewHolder(h: DemoVH, i: Int) = h.bind(getItem(i)) + + companion object { + private val diff = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(a: String, b: String) = a === b + override fun areContentsTheSame(a: String, b: String) = a == b + } + } +} + +private class DemoVH(v: View) : RecyclerView.ViewHolder(v) { + fun bind(title: String) { + // Bind to your title TextView inside item_block_title + itemView.findViewById(R.id.title)?.text = title + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_object.xml b/app/src/main/res/layout/fragment_object.xml new file mode 100644 index 0000000000..43eca87511 --- /dev/null +++ b/app/src/main/res/layout/fragment_object.xml @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 83e9c8da37ca9a4d2335bfd1fe4c8ccfc563d858 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Thu, 14 Aug 2025 14:30:41 +0200 Subject: [PATCH 24/24] DROID-3805 object prototype --- .../anytype/ui/objects/ObjectFragment.kt | 125 ++++++++++++++---- app/src/main/res/layout/fragment_object.xml | 31 +++-- app/src/main/res/navigation/graph.xml | 2 +- 3 files changed, 124 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/ui/objects/ObjectFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/objects/ObjectFragment.kt index 2496b5aff4..f42cc2fd38 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/objects/ObjectFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/objects/ObjectFragment.kt @@ -1,9 +1,15 @@ package com.anytypeio.anytype.ui.objects +import android.graphics.Color import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.PopupMenu import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.WindowInsetsControllerCompat +import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter @@ -16,16 +22,31 @@ class ObjectFragment : Fragment(R.layout.fragment_object) { private var _binding: FragmentObjectBinding? = null private val binding get() = _binding!! + private var lightStatusBars: Boolean = true + private var coverVisible: Boolean = true + private var imePaddingEnabled: Boolean = true + private var lastInsets: WindowInsetsCompat? = null + override fun onViewCreated(view: android.view.View, savedInstanceState: android.os.Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentObjectBinding.bind(view) // Temporary: show recycler to verify layout + // --- Demo Recycler for refactor bring-up --- + val demoAdapter = DemoAdapter() + binding.recycler.adapter = demoAdapter binding.recycler.visibility = View.VISIBLE - - val adapter = DemoAdapter() - binding.recycler.adapter = adapter - adapter.submitList(listOf("Heading", "Paragraph", "Checklist", "Code Block")) + demoAdapter.submitList( + listOf( + "Title block", + "Paragraph block", + "Checklist block", + "Code block", + "Divider", + "Callout", + "Quote" + ) + ) // Enable edge-to-edge for system bars val window = requireActivity().window @@ -35,11 +56,8 @@ class ObjectFragment : Fragment(R.layout.fragment_object) { val controller = androidx.core.view.WindowInsetsControllerCompat(window, binding.root) controller.isAppearanceLightStatusBars = true - // Give the top cover a random background so changes are obvious during testing - val rnd = kotlin.random.Random(System.currentTimeMillis()) - val color = - android.graphics.Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256)) - binding.topEdgeCover.setBackgroundColor(color) + binding.topEdgeCover.setBackgroundColor(Color.parseColor("#FF00AA")) // magenta + binding.topEdgeCover.visibility = if (coverVisible) View.VISIBLE else View.GONE // Apply WindowInsets to size the top cover to the status bar height and // provide bottom padding for the sheet (recycler + toolbars) vs nav/IME. @@ -48,11 +66,17 @@ class ObjectFragment : Fragment(R.layout.fragment_object) { val nav = insets.getInsets(androidx.core.view.WindowInsetsCompat.Type.navigationBars()) val ime = insets.getInsets(androidx.core.view.WindowInsetsCompat.Type.ime()) - // Match the top cover height to status bar height - binding.topEdgeCover.layoutParams = binding.topEdgeCover.layoutParams.apply { - height = status.top + // Extend topToolbar to include status bar area + binding.topToolbar.updateLayoutParams { + height += status.top } - binding.topEdgeCover.requestLayout() + // Add top padding to topToolbar content to push it below status bar + binding.topToolbar.setPadding( + binding.topToolbar.paddingLeft, + status.top, + binding.topToolbar.paddingRight, + binding.topToolbar.paddingBottom + ) // Pad the content bottom by the largest of nav bar or IME val bottom = maxOf(nav.bottom, ime.bottom) @@ -65,6 +89,55 @@ class ObjectFragment : Fragment(R.layout.fragment_object) { insets } + + //testing + binding.debugFab.setOnClickListener { view -> + val popup = PopupMenu(requireContext(), view) + popup.menu.add(0, 1, 0, "Light status bars: toggle") + popup.menu.add(0, 2, 1, "Top cover: show/hide") + popup.menu.add(0, 3, 2, "Top cover: randomize color") + popup.menu.add(0, 4, 3, "Bottom padding: toggle IME/nav") + + popup.setOnMenuItemClickListener { item -> + when (item.itemId) { + 1 -> { + lightStatusBars = !lightStatusBars + val controller = WindowInsetsControllerCompat(requireActivity().window, binding.root) + controller.isAppearanceLightStatusBars = lightStatusBars + true + } + 2 -> { + coverVisible = !coverVisible + binding.topEdgeCover.visibility = if (coverVisible) View.VISIBLE else View.GONE + true + } + 3 -> { + val rnd = kotlin.random.Random(System.currentTimeMillis()) + val color = android.graphics.Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256)) + binding.topEdgeCover.setBackgroundColor(color) + true + } + 4 -> { + imePaddingEnabled = !imePaddingEnabled + // re-apply latest insets immediately + lastInsets?.let { insets -> + val nav = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) + val ime = insets.getInsets(WindowInsetsCompat.Type.ime()) + val bottom = if (imePaddingEnabled) maxOf(nav.bottom, ime.bottom) else 0 + binding.sheet.setPadding( + binding.sheet.paddingLeft, + binding.sheet.paddingTop, + binding.sheet.paddingRight, + bottom + ) + } + true + } + else -> false + } + } + popup.show() + } } override fun onDestroyView() { @@ -74,23 +147,29 @@ class ObjectFragment : Fragment(R.layout.fragment_object) { } -private class DemoAdapter : ListAdapter(diff) { - override fun onCreateViewHolder(p: ViewGroup, vt: Int) = - DemoVH(LayoutInflater.from(p.context).inflate(R.layout.item_block_title, p, false)) +// --- Minimal demo adapter to visualize the refactored layout --- +private class DemoAdapter : ListAdapter(DIFF) { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DemoVH { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_block_title, parent, false) + return DemoVH(view) + } - override fun onBindViewHolder(h: DemoVH, i: Int) = h.bind(getItem(i)) + override fun onBindViewHolder(holder: DemoVH, position: Int) { + holder.bind(getItem(position)) + } companion object { - private val diff = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(a: String, b: String) = a === b - override fun areContentsTheSame(a: String, b: String) = a == b + private val DIFF = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: String, newItem: String) = oldItem === newItem + override fun areContentsTheSame(oldItem: String, newItem: String) = oldItem == newItem } } } -private class DemoVH(v: View) : RecyclerView.ViewHolder(v) { +private class DemoVH(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(title: String) { - // Bind to your title TextView inside item_block_title - itemView.findViewById(R.id.title)?.text = title + // `item_block_title` is assumed to contain a TextView with id `title` + itemView.findViewById(com.anytypeio.anytype.R.id.title)?.text = title } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_object.xml b/app/src/main/res/layout/fragment_object.xml index 43eca87511..9d01dd4dab 100644 --- a/app/src/main/res/layout/fragment_object.xml +++ b/app/src/main/res/layout/fragment_object.xml @@ -8,15 +8,6 @@ android:background="@color/defaultCanvasColor" tools:context="com.anytypeio.anytype.ui.objects.ObjectFragment"> - - + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/graph.xml b/app/src/main/res/navigation/graph.xml index 0534305932..546d6415b5 100644 --- a/app/src/main/res/navigation/graph.xml +++ b/app/src/main/res/navigation/graph.xml @@ -10,7 +10,7 @@ app:startDestination="@id/pageScreen">