diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/Relations.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/Relations.kt
index 172c14bcca..55588a8c6c 100644
--- a/core-models/src/main/java/com/anytypeio/anytype/core_models/Relations.kt
+++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/Relations.kt
@@ -103,6 +103,7 @@ object Relations {
const val WRITERS_LIMIT = "writersLimit"
const val SHARED_SPACES_LIMIT = "sharedSpacesLimit"
+ const val FILE_ID = "fileId"
const val LAYOUT_ALIGN = "layoutAlign"
@@ -203,6 +204,7 @@ object Relations {
"layoutWidth",
"defaultViewType",
"defaultTypeId",
- "resolvedLayout"
+ "resolvedLayout",
+ "fileId",
)
}
\ No newline at end of file
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 5390f19240..7d713ca27e 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
@@ -59,6 +59,7 @@ import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleFileBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleProfileBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleTodoBinding
+import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleVideoBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTocBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockToggleBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockUnsupportedBinding
@@ -194,6 +195,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO_ERROR
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO_PLACEHOLDER
+import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO_TITLE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO_UPLOAD
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
import com.anytypeio.anytype.presentation.objects.ObjectIcon
@@ -271,6 +273,9 @@ class BlockAdapter(
is Video -> {
holder.recycle()
}
+ is Title.Video -> {
+ holder.release()
+ }
}
}
@@ -370,6 +375,11 @@ class BlockAdapter(
ItemBlockTitleFileBinding.inflate(inflater, parent, false)
)
}
+ HOLDER_VIDEO_TITLE -> Title.Video(
+ ItemBlockTitleVideoBinding.inflate(inflater, parent, false),
+ lifecycle
+ )
+
HOLDER_TODO_TITLE -> {
Title.Todo(
ItemBlockTitleTodoBinding.inflate(inflater, parent, false)
@@ -1026,6 +1036,13 @@ class BlockAdapter(
item = blocks[position] as BlockView.Title.File
)
}
+ is Title.Video -> {
+ holder.processPayloads(
+ payloads = payloads.typeOf(),
+ item = blocks[position] as BlockView.Title.Video
+ )
+ }
+
is Numbered -> {
holder.processChangePayload(
payloads = payloads.typeOf(),
@@ -1390,7 +1407,13 @@ class BlockAdapter(
}
is Title.File -> {
holder.apply {
- bind(item = blocks[position] as BlockView.Title.File,)
+ bind(item = blocks[position] as BlockView.Title.File)
+ }
+ }
+
+ is Title.Video -> {
+ holder.apply {
+ bind(item = blocks[position] as BlockView.Title.Video)
}
}
is Code -> {
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 7732fe6b0f..9264265f25 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
@@ -12,6 +12,9 @@ import android.widget.TextView
import androidx.compose.ui.platform.ComposeView
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 com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.SearchHighlightSpan
@@ -20,6 +23,7 @@ import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleFileBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleProfileBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleTodoBinding
+import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleVideoBinding
import com.anytypeio.anytype.core_ui.extensions.setBlockBackgroundColor
import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil
import com.anytypeio.anytype.core_ui.features.editor.BlockViewHolder
@@ -41,6 +45,10 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.Target
+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 com.bumptech.glide.request.transition.Transition
import timber.log.Timber
@@ -645,4 +653,95 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder {
//do nothing
}
}
+
+
+ class Video(
+ private val videoBinding: ItemBlockTitleVideoBinding,
+ lifecycle: Lifecycle
+ ) : Title(videoBinding.root), LifecycleEventObserver {
+
+ override val icon: ObjectIconWidget = videoBinding.objectIconWidget
+ override val image: ImageView = videoBinding.cover
+ override val content: TextInputWidget = videoBinding.title
+ override val selectionView: View = itemView
+
+ private var player: ExoPlayer? = null
+ private var videoUrl: String? = null
+
+ private val playerView: StyledPlayerView = videoBinding.playerView
+ private val playButton: ImageView = videoBinding.playButton
+
+ init {
+ lifecycle.addObserver(this)
+ }
+
+ fun bind(item: BlockView.Title.Video) {
+ super.bind(
+ item = item,
+ onCoverClicked = {},
+ click = {}
+ )
+ content.setText(item.text)
+ setupPreview(item)
+ }
+
+ private fun setupPreview(item: BlockView.Title.Video) {
+ videoUrl = item.videoUrl
+ with(videoBinding) {
+ objectIconWidget.gone()
+ playerView.visible()
+ playButton.visible()
+ playButton.setOnClickListener { togglePlayback() }
+ }
+ }
+
+ private fun togglePlayback() {
+ videoUrl?.let { url ->
+ if (player?.isPlaying == true) pause() else play(url)
+ }
+ }
+
+ fun play(url: String) {
+ release()
+ player = ExoPlayer.Builder(itemView.context)
+ .setLoadControl(
+ DefaultLoadControl.Builder()
+ .setBufferDurationsMs(600, 1000, 250, 500)
+ .build()
+ )
+ .build()
+ .apply {
+ playerView.player = this
+ setMediaItem(MediaItem.fromUri(url))
+ prepare()
+ playWhenReady = true
+ }
+ playButton.gone()
+ }
+
+ fun pause() {
+ player?.pause()
+ videoBinding.playButton.visible()
+ }
+
+ fun release() {
+ player?.release()
+ player = null
+ }
+
+
+ override fun applyTextColor(item: BlockView.Title) {
+ }
+
+ override fun applyBackground(item: BlockView.Title) {
+ }
+
+ override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+ when (event) {
+ Lifecycle.Event.ON_PAUSE -> pause()
+ Lifecycle.Event.ON_DESTROY -> release()
+ else -> {}
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/core-ui/src/main/res/drawable/play.xml b/core-ui/src/main/res/drawable/play.xml
new file mode 100644
index 0000000000..c3571e86d4
--- /dev/null
+++ b/core-ui/src/main/res/drawable/play.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/core-ui/src/main/res/layout/item_block_title_video.xml b/core-ui/src/main/res/layout/item_block_title_video.xml
new file mode 100644
index 0000000000..59a7788da2
--- /dev/null
+++ b/core-ui/src/main/res/layout/item_block_title_video.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
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 2692da8209..b969654bea 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
@@ -72,6 +72,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO_ERROR
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO_PLACEHOLDER
+import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO_TITLE
import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_VIDEO_UPLOAD
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.objects.appearance.choose.ObjectAppearanceChooseSettingsView
@@ -688,11 +689,39 @@ sealed class BlockView : ViewType {
override var cursor: Int? = null,
override val searchFields: List = emptyList(),
override val hint: String? = null,
+ val url: String?,
val icon: ObjectIcon
) : Title(), Searchable {
override fun getViewType() = HOLDER_FILE_TITLE
}
+ /**
+ * UI-model for a video-layout title block.
+ * @property id block's id
+ * @property text text content (i.e. title text)
+ * @property videoUrl direct URL to video content
+ */
+ data class Video(
+ override val id: String,
+ override var isFocused: Boolean = false,
+ override var text: String,
+ override var coverColor: CoverColor? = null,
+ override var coverImage: Url? = null,
+ override var coverGradient: String? = null,
+ override val background: ThemeColor = ThemeColor.DEFAULT,
+ override val color: ThemeColor = ThemeColor.DEFAULT,
+ val emoji: String? = null,
+ override val image: String? = null,
+ override val mode: Mode = Mode.READ,
+ override var cursor: Int? = null,
+ override val searchFields: List = emptyList(),
+ override val hint: String? = null,
+ val videoUrl: String?,
+ val icon: ObjectIcon
+ ) : Title(), Searchable {
+ override fun getViewType() = HOLDER_VIDEO_TITLE
+ }
+
/**
* UI-model for a profile-layout title block.
* @property id block's id
@@ -1433,6 +1462,7 @@ sealed class BlockView : ViewType {
@JvmInline
value class RowId(val value: String)
+
@JvmInline
value class RowIndex(val value: Int)
@@ -1440,6 +1470,7 @@ sealed class BlockView : ViewType {
@JvmInline
value class ColumnId(val value: String)
+
@JvmInline
value class ColumnIndex(val value: Int)
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt
index c1fb63c24a..32a0694a4e 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt
@@ -6,6 +6,7 @@ object Types {
const val HOLDER_PROFILE_TITLE = 35
const val HOLDER_ARCHIVE_TITLE = 36
const val HOLDER_FILE_TITLE = 37
+ const val HOLDER_VIDEO_TITLE = 60
const val HOLDER_TODO_TITLE = 48
const val HOLDER_HEADER_ONE = 2
const val HOLDER_HEADER_TWO = 3
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 07e0013912..9a96d9243e 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
@@ -29,6 +29,9 @@ import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.InEditor
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Mode
import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
+import com.anytypeio.anytype.presentation.mapper.getFileUrl
+import com.anytypeio.anytype.presentation.mapper.marks
+import com.anytypeio.anytype.presentation.extension.getTypeForObject
import com.anytypeio.anytype.presentation.mapper.objectIcon
import com.anytypeio.anytype.presentation.mapper.marks
import com.anytypeio.anytype.presentation.mapper.toFileView
@@ -1497,8 +1500,24 @@ class DefaultBlockViewRenderer @Inject constructor(
color = block.textColor()
)
}
+ ObjectType.Layout.VIDEO -> {
+ BlockView.Title.Video(
+ mode = Mode.READ,
+ id = block.id,
+ text = fieldParser.getObjectName(currentObject),
+ videoUrl = currentObject.getFileUrl(urlBuilder),
+ icon = currentObject.objectIcon(builder = urlBuilder),
+ isFocused = resolveIsFocused(focus, block),
+ cursor = cursor,
+ coverColor = coverContainer.coverColor,
+ coverImage = coverContainer.coverImage,
+ coverGradient = coverContainer.coverGradient,
+ background = block.parseThemeBackgroundColor(),
+ color = block.textColor()
+ )
+ }
+
ObjectType.Layout.FILE,
- ObjectType.Layout.VIDEO,
ObjectType.Layout.AUDIO,
ObjectType.Layout.PDF -> {
val objType = storeOfObjectTypes.getTypeOfObject(currentObject)
@@ -1513,7 +1532,8 @@ 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),
+ url = currentObject.getFileUrl(urlBuilder)
)
}
ObjectType.Layout.IMAGE -> {
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt
index 403a17e199..2b66ff2ea2 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt
@@ -355,6 +355,7 @@ fun List.toggleTableMode(
is BlockView.DataView.Deleted -> view.copy(isSelected = false)
is BlockView.ButtonOpenFile.ImageButton -> view
is BlockView.ButtonOpenFile.FileButton -> view
+ is BlockView.Title.Video -> view
}
}
}
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/ObjectIconMapper.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/ObjectIconMapper.kt
index 3bfe117290..15bd1ab07b 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/ObjectIconMapper.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/ObjectIconMapper.kt
@@ -2,6 +2,7 @@ package com.anytypeio.anytype.presentation.mapper
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectWrapper
+import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.objects.ObjectIcon.Basic
@@ -144,3 +145,8 @@ private fun ObjectWrapper.Type.objectFallbackIcon(): ObjectIcon.TypeIcon.Fallbac
fun ObjectWrapper.Basic.objectIcon(builder: UrlBuilder): ObjectIcon {
return ObjectIcon.None
}
+
+fun ObjectWrapper.Basic.getFileUrl(urlBuilder: UrlBuilder): String? {
+ val fileId = getValue(Relations.FILE_ID) ?: return null
+ return urlBuilder.file(fileId)
+}
\ No newline at end of file