diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 07f6287..b1f35e9 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -68,6 +68,7 @@ android {
dependencies {
implementation(project(":feature:home"))
implementation(project(":feature:photo"))
+ implementation(project(":feature:video"))
implementation(project(":feature:contactme"))
implementation(project(":core:designsystem"))
diff --git a/app/src/main/java/com/wei/picquest/navigation/PqNavHost.kt b/app/src/main/java/com/wei/picquest/navigation/PqNavHost.kt
index d9dd5de..b907916 100644
--- a/app/src/main/java/com/wei/picquest/navigation/PqNavHost.kt
+++ b/app/src/main/java/com/wei/picquest/navigation/PqNavHost.kt
@@ -10,6 +10,7 @@ import com.wei.picquest.feature.home.home.navigation.homeGraph
import com.wei.picquest.feature.home.home.navigation.homeRoute
import com.wei.picquest.feature.photo.photolibrary.navigation.photoLibraryGraph
import com.wei.picquest.feature.photo.photosearch.navigation.photoSearchGraph
+import com.wei.picquest.feature.video.videolibrary.navigation.videoLibraryGraph
import com.wei.picquest.ui.PqAppState
/**
@@ -45,6 +46,9 @@ fun PqNavHost(
photoLibraryGraph(navController = navController)
},
)
+ videoLibraryGraph(
+ navController = navController,
+ )
contactMeGraph(
navController = navController,
contentType = contentType,
diff --git a/app/src/main/java/com/wei/picquest/navigation/TopLevelDestination.kt b/app/src/main/java/com/wei/picquest/navigation/TopLevelDestination.kt
index 751c06e..6c081e5 100644
--- a/app/src/main/java/com/wei/picquest/navigation/TopLevelDestination.kt
+++ b/app/src/main/java/com/wei/picquest/navigation/TopLevelDestination.kt
@@ -27,6 +27,12 @@ enum class TopLevelDestination(
iconTextId = R.string.photo,
titleTextId = R.string.photo,
),
+ VIDEO(
+ selectedIcon = PqIcons.VideoLibrary,
+ unselectedIcon = PqIcons.VideoLibraryBorder,
+ iconTextId = R.string.video,
+ titleTextId = R.string.video,
+ ),
CONTACT_ME(
selectedIcon = PqIcons.ContactMe,
unselectedIcon = PqIcons.ContactMeBorder,
diff --git a/app/src/main/java/com/wei/picquest/ui/PqAppState.kt b/app/src/main/java/com/wei/picquest/ui/PqAppState.kt
index 7928336..2dc2bff 100644
--- a/app/src/main/java/com/wei/picquest/ui/PqAppState.kt
+++ b/app/src/main/java/com/wei/picquest/ui/PqAppState.kt
@@ -31,6 +31,8 @@ import com.wei.picquest.feature.home.home.navigation.homeRoute
import com.wei.picquest.feature.home.home.navigation.navigateToHome
import com.wei.picquest.feature.photo.photosearch.navigation.navigateToPhotoSearch
import com.wei.picquest.feature.photo.photosearch.navigation.photoSearchRoute
+import com.wei.picquest.feature.video.videolibrary.navigation.navigateToVideoLibrary
+import com.wei.picquest.feature.video.videolibrary.navigation.videoLibraryRoute
import com.wei.picquest.navigation.TopLevelDestination
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -154,6 +156,7 @@ class PqAppState(
@Composable get() = when (currentDestination?.route) {
homeRoute -> TopLevelDestination.HOME
photoSearchRoute -> TopLevelDestination.PHOTO
+ videoLibraryRoute -> TopLevelDestination.VIDEO
contactMeRoute -> TopLevelDestination.CONTACT_ME
else -> null
}
@@ -206,6 +209,10 @@ class PqAppState(
topLevelNavOptions,
)
+ TopLevelDestination.VIDEO -> navController.navigateToVideoLibrary(
+ topLevelNavOptions,
+ )
+
TopLevelDestination.CONTACT_ME -> navController.navigateToContactMe(
topLevelNavOptions,
)
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 27cda33..02387f9 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -5,4 +5,5 @@
照片
首頁
聯絡我
+ 影片
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d34aeec..b29b70a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -4,6 +4,7 @@
Photo
Home
Contact Me
+ Video
PqBottomBar
diff --git a/core/data/src/main/java/com/wei/picquest/core/data/model/VideoDetail.kt b/core/data/src/main/java/com/wei/picquest/core/data/model/VideoDetail.kt
new file mode 100644
index 0000000..d45241d
--- /dev/null
+++ b/core/data/src/main/java/com/wei/picquest/core/data/model/VideoDetail.kt
@@ -0,0 +1,67 @@
+package com.wei.picquest.core.data.model
+
+import com.wei.picquest.core.network.model.NetworkVideoDetail
+import com.wei.picquest.core.network.model.NetworkVideoDetailSize
+import com.wei.picquest.core.network.model.NetworkVideoStreams
+
+data class VideoDetail(
+ val id: Int,
+ val pageURL: String,
+ val type: String,
+ val tags: String,
+ val duration: Int,
+ val pictureId: String,
+ val videos: VideoStreams,
+ val views: Int,
+ val downloads: Int,
+ val likes: Int,
+ val comments: Int,
+ val userId: Int,
+ val user: String,
+ val userImageURL: String,
+)
+
+data class VideoStreams(
+ val large: VideoDetailSize,
+ val medium: VideoDetailSize,
+ val small: VideoDetailSize,
+ val tiny: VideoDetailSize,
+)
+
+data class VideoDetailSize(
+ val url: String,
+ val width: Int,
+ val height: Int,
+ val size: Long,
+)
+
+fun NetworkVideoDetail.asExternalModel() = VideoDetail(
+ id = this.id,
+ pageURL = this.pageURL,
+ type = this.type,
+ tags = this.tags,
+ duration = this.duration,
+ pictureId = this.pictureId,
+ videos = this.videos.asExternalModel(),
+ views = this.views,
+ downloads = this.downloads,
+ likes = this.likes,
+ comments = this.comments,
+ userId = this.userId,
+ user = this.user,
+ userImageURL = this.userImageURL,
+)
+
+fun NetworkVideoStreams.asExternalModel() = VideoStreams(
+ large = this.large.asExternalModel(),
+ medium = this.medium.asExternalModel(),
+ small = this.small.asExternalModel(),
+ tiny = this.tiny.asExternalModel(),
+)
+
+fun NetworkVideoDetailSize.asExternalModel() = VideoDetailSize(
+ url = this.url,
+ width = this.width,
+ height = this.height,
+ size = this.size,
+)
diff --git a/core/data/src/main/java/com/wei/picquest/core/data/repository/DefaultSearchImagesRepository.kt b/core/data/src/main/java/com/wei/picquest/core/data/repository/DefaultSearchImagesRepository.kt
index 3f922cd..d0b41a8 100644
--- a/core/data/src/main/java/com/wei/picquest/core/data/repository/DefaultSearchImagesRepository.kt
+++ b/core/data/src/main/java/com/wei/picquest/core/data/repository/DefaultSearchImagesRepository.kt
@@ -7,7 +7,7 @@ import androidx.paging.map
import com.wei.picquest.core.data.model.ImageDetail
import com.wei.picquest.core.data.model.asExternalModel
import com.wei.picquest.core.network.PqNetworkDataSource
-import com.wei.picquest.core.network.pagingsource.PixabayPagingSource
+import com.wei.picquest.core.network.pagingsource.PixabayImagePagingSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
@@ -23,7 +23,7 @@ class DefaultSearchImagesRepository @Inject constructor(
prefetchDistance = 5,
enablePlaceholders = false,
),
- pagingSourceFactory = { PixabayPagingSource(pqNetworkDataSource, query) },
+ pagingSourceFactory = { PixabayImagePagingSource(pqNetworkDataSource, query) },
).flow.map { pagingData ->
pagingData.map { it.asExternalModel() }
}
diff --git a/core/data/src/main/java/com/wei/picquest/core/data/repository/DefaultSearchVideoRepository.kt b/core/data/src/main/java/com/wei/picquest/core/data/repository/DefaultSearchVideoRepository.kt
new file mode 100644
index 0000000..8f75180
--- /dev/null
+++ b/core/data/src/main/java/com/wei/picquest/core/data/repository/DefaultSearchVideoRepository.kt
@@ -0,0 +1,31 @@
+package com.wei.picquest.core.data.repository
+
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import androidx.paging.map
+import com.wei.picquest.core.data.model.VideoDetail
+import com.wei.picquest.core.data.model.asExternalModel
+import com.wei.picquest.core.network.PqNetworkDataSource
+import com.wei.picquest.core.network.pagingsource.PixabayVideoPagingSource
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+
+class DefaultSearchVideoRepository @Inject constructor(
+ private val pqNetworkDataSource: PqNetworkDataSource,
+) : SearchVideoRepository {
+
+ override suspend fun getSearchVideo(query: String): Flow> {
+ return Pager(
+ config = PagingConfig(
+ pageSize = 20,
+ prefetchDistance = 5,
+ enablePlaceholders = false,
+ ),
+ pagingSourceFactory = { PixabayVideoPagingSource(pqNetworkDataSource, query) },
+ ).flow.map { pagingData ->
+ pagingData.map { it.asExternalModel() }
+ }
+ }
+}
diff --git a/core/data/src/main/java/com/wei/picquest/core/data/repository/SearchVideoRepository.kt b/core/data/src/main/java/com/wei/picquest/core/data/repository/SearchVideoRepository.kt
new file mode 100644
index 0000000..cda6f63
--- /dev/null
+++ b/core/data/src/main/java/com/wei/picquest/core/data/repository/SearchVideoRepository.kt
@@ -0,0 +1,10 @@
+package com.wei.picquest.core.data.repository
+
+import androidx.paging.PagingData
+import com.wei.picquest.core.data.model.VideoDetail
+import kotlinx.coroutines.flow.Flow
+
+interface SearchVideoRepository {
+
+ suspend fun getSearchVideo(query: String): Flow>
+}
diff --git a/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/icon/PqIcons.kt b/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/icon/PqIcons.kt
index 1f4b5c5..de09ede 100644
--- a/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/icon/PqIcons.kt
+++ b/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/icon/PqIcons.kt
@@ -6,6 +6,7 @@ import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.PhotoLibrary
import androidx.compose.material.icons.outlined.SupportAgent
import androidx.compose.material.icons.outlined.Upcoming
+import androidx.compose.material.icons.outlined.VideoLibrary
import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.ArrowBackIosNew
@@ -25,6 +26,7 @@ import androidx.compose.material.icons.rounded.Settings
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material.icons.rounded.SupportAgent
import androidx.compose.material.icons.rounded.Upcoming
+import androidx.compose.material.icons.rounded.VideoLibrary
import androidx.compose.ui.graphics.vector.ImageVector
/**
@@ -46,6 +48,8 @@ object PqIcons {
val ContactMeBorder = Icons.Outlined.SupportAgent
val PhotoLibrary = Icons.Rounded.PhotoLibrary
val PhotoLibraryBorder = Icons.Outlined.PhotoLibrary
+ val VideoLibrary = Icons.Rounded.VideoLibrary
+ val VideoLibraryBorder = Icons.Outlined.VideoLibrary
val Phone = Icons.Rounded.Phone
val Upcoming = Icons.Rounded.Upcoming
val UpcomingBorder = Icons.Outlined.Upcoming
diff --git a/core/network/src/main/java/com/wei/picquest/core/network/PqNetworkDataSource.kt b/core/network/src/main/java/com/wei/picquest/core/network/PqNetworkDataSource.kt
index 2ddc85c..8f71795 100644
--- a/core/network/src/main/java/com/wei/picquest/core/network/PqNetworkDataSource.kt
+++ b/core/network/src/main/java/com/wei/picquest/core/network/PqNetworkDataSource.kt
@@ -1,6 +1,7 @@
package com.wei.picquest.core.network
import com.wei.picquest.core.network.model.NetworkSearchImages
+import com.wei.picquest.core.network.model.NetworkSearchVideos
/**
* Interface representing network calls to the PicQuest backend
@@ -8,4 +9,6 @@ import com.wei.picquest.core.network.model.NetworkSearchImages
interface PqNetworkDataSource {
suspend fun searchImages(query: String, page: Int, perPage: Int): NetworkSearchImages
+
+ suspend fun searchVideos(query: String, page: Int, perPage: Int): NetworkSearchVideos
}
diff --git a/core/network/src/main/java/com/wei/picquest/core/network/model/NetworkSearchImages.kt b/core/network/src/main/java/com/wei/picquest/core/network/model/NetworkSearchImages.kt
index 3394a5b..a2e82bf 100644
--- a/core/network/src/main/java/com/wei/picquest/core/network/model/NetworkSearchImages.kt
+++ b/core/network/src/main/java/com/wei/picquest/core/network/model/NetworkSearchImages.kt
@@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
- * Network representation of [SearchImages] when fetched from /
+ * Network representation of [NetworkSearchImages] when fetched from /
*/
@Serializable
data class NetworkSearchImages(
diff --git a/core/network/src/main/java/com/wei/picquest/core/network/model/NetworkSearchVideos.kt b/core/network/src/main/java/com/wei/picquest/core/network/model/NetworkSearchVideos.kt
new file mode 100644
index 0000000..2381156
--- /dev/null
+++ b/core/network/src/main/java/com/wei/picquest/core/network/model/NetworkSearchVideos.kt
@@ -0,0 +1,73 @@
+package com.wei.picquest.core.network.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+/**
+ * Network representation of [NetworkSearchVideos] when fetched from /videos/
+ */
+@Serializable
+data class NetworkSearchVideos(
+ @SerialName("total")
+ val total: Int,
+ @SerialName("totalHits")
+ val totalHits: Int,
+ @SerialName("hits")
+ val hits: List,
+)
+
+@Serializable
+data class NetworkVideoDetail(
+ @SerialName("id")
+ val id: Int,
+ @SerialName("pageURL")
+ val pageURL: String,
+ @SerialName("type")
+ val type: String,
+ @SerialName("tags")
+ val tags: String,
+ @SerialName("duration")
+ val duration: Int,
+ @SerialName("picture_id")
+ val pictureId: String,
+ @SerialName("videos")
+ val videos: NetworkVideoStreams,
+ @SerialName("views")
+ val views: Int,
+ @SerialName("downloads")
+ val downloads: Int,
+ @SerialName("likes")
+ val likes: Int,
+ @SerialName("comments")
+ val comments: Int,
+ @SerialName("user_id")
+ val userId: Int,
+ @SerialName("user")
+ val user: String,
+ @SerialName("userImageURL")
+ val userImageURL: String,
+)
+
+@Serializable
+data class NetworkVideoStreams(
+ @SerialName("large")
+ val large: NetworkVideoDetailSize,
+ @SerialName("medium")
+ val medium: NetworkVideoDetailSize,
+ @SerialName("small")
+ val small: NetworkVideoDetailSize,
+ @SerialName("tiny")
+ val tiny: NetworkVideoDetailSize,
+)
+
+@Serializable
+data class NetworkVideoDetailSize(
+ @SerialName("url")
+ val url: String,
+ @SerialName("width")
+ val width: Int,
+ @SerialName("height")
+ val height: Int,
+ @SerialName("size")
+ val size: Long,
+)
diff --git a/core/network/src/main/java/com/wei/picquest/core/network/pagingsource/PixabayPagingSource.kt b/core/network/src/main/java/com/wei/picquest/core/network/pagingsource/PixabayImagePagingSource.kt
similarity index 97%
rename from core/network/src/main/java/com/wei/picquest/core/network/pagingsource/PixabayPagingSource.kt
rename to core/network/src/main/java/com/wei/picquest/core/network/pagingsource/PixabayImagePagingSource.kt
index a77c907..290ed18 100644
--- a/core/network/src/main/java/com/wei/picquest/core/network/pagingsource/PixabayPagingSource.kt
+++ b/core/network/src/main/java/com/wei/picquest/core/network/pagingsource/PixabayImagePagingSource.kt
@@ -5,7 +5,7 @@ import androidx.paging.PagingState
import com.wei.picquest.core.network.PqNetworkDataSource
import com.wei.picquest.core.network.model.NetworkImageDetail
-class PixabayPagingSource(
+class PixabayImagePagingSource(
private val pqNetworkDataSource: PqNetworkDataSource,
private val query: String,
) : PagingSource() {
diff --git a/core/network/src/main/java/com/wei/picquest/core/network/pagingsource/PixabayVideoPagingSource.kt b/core/network/src/main/java/com/wei/picquest/core/network/pagingsource/PixabayVideoPagingSource.kt
new file mode 100644
index 0000000..c8603aa
--- /dev/null
+++ b/core/network/src/main/java/com/wei/picquest/core/network/pagingsource/PixabayVideoPagingSource.kt
@@ -0,0 +1,37 @@
+package com.wei.picquest.core.network.pagingsource
+
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+import com.wei.picquest.core.network.PqNetworkDataSource
+import com.wei.picquest.core.network.model.NetworkVideoDetail
+
+class PixabayVideoPagingSource(
+ private val pqNetworkDataSource: PqNetworkDataSource,
+ private val query: String,
+) : PagingSource() {
+
+ override suspend fun load(params: LoadParams): LoadResult {
+ try {
+ val currentPage = params.key ?: 1
+ val response = pqNetworkDataSource.searchVideos(
+ query = query,
+ page = currentPage,
+ perPage = 20,
+ )
+
+ val endOfPaginationReached = response.hits.isEmpty()
+
+ return LoadResult.Page(
+ data = response.hits,
+ prevKey = if (currentPage == 1) null else currentPage - 1,
+ nextKey = if (endOfPaginationReached) null else currentPage + 1,
+ )
+ } catch (exception: Exception) {
+ return LoadResult.Error(exception)
+ }
+ }
+
+ override fun getRefreshKey(state: PagingState): Int? {
+ return state.anchorPosition
+ }
+}
diff --git a/core/network/src/main/java/com/wei/picquest/core/network/retrofit/RetrofitPqNetwork.kt b/core/network/src/main/java/com/wei/picquest/core/network/retrofit/RetrofitPqNetwork.kt
index 6315504..d0148d2 100644
--- a/core/network/src/main/java/com/wei/picquest/core/network/retrofit/RetrofitPqNetwork.kt
+++ b/core/network/src/main/java/com/wei/picquest/core/network/retrofit/RetrofitPqNetwork.kt
@@ -4,6 +4,7 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact
import com.wei.core.network.BuildConfig
import com.wei.picquest.core.network.PqNetworkDataSource
import com.wei.picquest.core.network.model.NetworkSearchImages
+import com.wei.picquest.core.network.model.NetworkSearchVideos
import kotlinx.serialization.json.Json
import okhttp3.Call
import okhttp3.MediaType.Companion.toMediaType
@@ -32,6 +33,19 @@ interface RetrofitPixabayApi {
@Query("perPage") perPage: Int,
// Add more parameters as needed
): NetworkSearchImages
+
+ /**
+ * https://pixabay.com/api/videos/?key=${api key}&q=yellow+flowers
+ */
+ @GET("/videos/")
+ suspend fun searchVideos(
+ @Query("key") apiKey: String = API_KEY,
+ @Query("q") query: String,
+ @Query("video_type") videoType: String = "film",
+ @Query("page") page: Int,
+ @Query("perPage") perPage: Int,
+ // Add more parameters as needed
+ ): NetworkSearchVideos
}
/**
@@ -55,4 +69,8 @@ class RetrofitPqNetwork @Inject constructor(
override suspend fun searchImages(query: String, page: Int, perPage: Int): NetworkSearchImages {
return pixabayApi.searchImages(query = query, page = page, perPage = perPage)
}
+
+ override suspend fun searchVideos(query: String, page: Int, perPage: Int): NetworkSearchVideos {
+ return pixabayApi.searchVideos(query = query, page = page, perPage = perPage)
+ }
}
diff --git a/feature/video/.gitignore b/feature/video/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/feature/video/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/feature/video/build.gradle.kts b/feature/video/build.gradle.kts
new file mode 100644
index 0000000..0bb8a1f
--- /dev/null
+++ b/feature/video/build.gradle.kts
@@ -0,0 +1,16 @@
+plugins {
+ alias(libs.plugins.pq.android.feature)
+ alias(libs.plugins.pq.android.library.compose)
+ alias(libs.plugins.pq.android.hilt)
+}
+
+android {
+ namespace = "com.wei.picquest.feature.video"
+}
+
+dependencies {
+ // Navigation
+ implementation(libs.media3.exoplayer)
+ implementation(libs.media3.exoplayer.dash)
+ implementation(libs.media3.ui)
+}
\ No newline at end of file
diff --git a/feature/video/src/main/AndroidManifest.xml b/feature/video/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a5918e6
--- /dev/null
+++ b/feature/video/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt
new file mode 100644
index 0000000..ac331d7
--- /dev/null
+++ b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt
@@ -0,0 +1,112 @@
+package com.wei.picquest.feature.video.videolibrary
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.safeDrawing
+import androidx.compose.foundation.layout.windowInsetsBottomHeight
+import androidx.compose.foundation.layout.windowInsetsTopHeight
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.navigation.NavController
+import com.wei.picquest.core.designsystem.component.FunctionalityNotAvailablePopup
+import com.wei.picquest.core.designsystem.component.ThemePreviews
+import com.wei.picquest.core.designsystem.theme.PqTheme
+
+/**
+ *
+ * UI 事件決策樹
+ * 下圖顯示了一個決策樹,用於查找處理特定事件用例的最佳方法。
+ *
+ * ┌───────┐
+ * │ Start │
+ * └───┬───┘
+ * ↓
+ * ┌───────────────────────────────────┐
+ * │ Where is event originated? │
+ * └──────┬─────────────────────┬──────┘
+ * ↓ ↓
+ * UI ViewModel
+ * │ │
+ * ┌─────────────────────────┐ ┌───────────────┐
+ * │ When the event requires │ │ Update the UI │
+ * │ ... │ │ State │
+ * └─┬─────────────────────┬─┘ └───────────────┘
+ * ↓ ↓
+ * Business logic UI behavior logic
+ * │ │
+ * ┌─────────────────────────────────┐ ┌──────────────────────────────────────┐
+ * │ Delegate the business logic to │ │ Modify the UI element state in the │
+ * │ the ViewModel │ │ UI directly │
+ * └─────────────────────────────────┘ └──────────────────────────────────────┘
+ *
+ *
+ */
+@Composable
+internal fun VideoLibraryRoute(
+ navController: NavController,
+) {
+ VideoLibraryScreen()
+}
+
+@Composable
+internal fun VideoLibraryScreen(
+ withTopSpacer: Boolean = true,
+ withBottomSpacer: Boolean = true,
+) {
+ val showPopup = remember { mutableStateOf(false) }
+
+ if (showPopup.value) {
+ FunctionalityNotAvailablePopup(
+ onDismiss = {
+ showPopup.value = false
+ },
+ )
+ }
+
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background,
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ if (withTopSpacer) {
+ Spacer(Modifier.windowInsetsTopHeight(WindowInsets.safeDrawing))
+ }
+
+ Column {
+ Spacer(modifier = Modifier.weight(1f))
+ Text(
+ text = "Screen not available \uD83D\uDE48",
+ color = MaterialTheme.colorScheme.error,
+ style = MaterialTheme.typography.headlineMedium,
+ modifier = Modifier
+ .semantics { contentDescription = "" },
+ )
+ Spacer(modifier = Modifier.weight(1f))
+ }
+
+ if (withBottomSpacer) {
+ Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
+ }
+ }
+ }
+}
+
+@ThemePreviews
+@Composable
+fun VideoLibraryScreenPreview() {
+ PqTheme {
+ VideoLibraryScreen()
+ }
+}
diff --git a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/navigation/VideoLibraryNavigation.kt b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/navigation/VideoLibraryNavigation.kt
new file mode 100644
index 0000000..caeef7f
--- /dev/null
+++ b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/navigation/VideoLibraryNavigation.kt
@@ -0,0 +1,23 @@
+package com.wei.picquest.feature.video.videolibrary.navigation
+
+import androidx.navigation.NavController
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavOptions
+import androidx.navigation.compose.composable
+import com.wei.picquest.feature.video.videolibrary.VideoLibraryRoute
+
+const val videoLibraryRoute = "video_library_route"
+
+fun NavController.navigateToVideoLibrary(navOptions: NavOptions? = null) {
+ this.navigate(videoLibraryRoute, navOptions)
+}
+
+fun NavGraphBuilder.videoLibraryGraph(
+ navController: NavController,
+) {
+ composable(route = videoLibraryRoute) {
+ VideoLibraryRoute(
+ navController = navController,
+ )
+ }
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 5c78329..42725cf 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -41,6 +41,7 @@ robolectric = "4.10.3"
roborazzi = "1.5.0-alpha-2"
paging = "3.2.1"
pagingCompose = "3.3.0-alpha02"
+media = "1.2.0"
[libraries]
@@ -177,6 +178,11 @@ paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.r
paging-common = { group = "androidx.paging", name = "paging-common", version.ref = "paging" }
paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "pagingCompose" }
+# media3
+media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media" }
+media3-exoplayer-dash = { group = "androidx.media3", name = "media3-exoplayer-dash", version.ref = "media" }
+media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media" }
+
# Dependencies of the included build-logic
android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 0648db4..8ced8de 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -32,3 +32,4 @@ include(":ui-test-hilt-manifest")
include(":feature:home")
include(":feature:photo")
include(":feature:contactme")
+include(":feature:video")