mirror of
https://github.com/oxyroid/M3UAndroid.git
synced 2025-05-20 21:06:34 +08:00
refactor: move ui logic to smartphone module.
This commit is contained in:
@ -22,7 +22,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
implementation(project(":ui"))
|
implementation(project(":data"))
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
|
||||||
@ -39,6 +39,5 @@ dependencies {
|
|||||||
ksp(libs.androidx.hilt.compiler)
|
ksp(libs.androidx.hilt.compiler)
|
||||||
implementation(libs.androidx.hilt.work)
|
implementation(libs.androidx.hilt.work)
|
||||||
|
|
||||||
implementation(libs.minabox)
|
|
||||||
implementation(libs.net.mm2d.mmupnp.mmupnp)
|
implementation(libs.net.mm2d.mmupnp.mmupnp)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import com.m3u.core.architecture.logger.Logger
|
|||||||
import com.m3u.core.architecture.logger.Profiles
|
import com.m3u.core.architecture.logger.Profiles
|
||||||
import com.m3u.core.architecture.logger.install
|
import com.m3u.core.architecture.logger.install
|
||||||
import com.m3u.core.util.coroutine.flatmapCombined
|
import com.m3u.core.util.coroutine.flatmapCombined
|
||||||
|
import com.m3u.core.wrapper.Sort
|
||||||
import com.m3u.data.database.model.AdjacentChannels
|
import com.m3u.data.database.model.AdjacentChannels
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.data.database.model.DataSource
|
import com.m3u.data.database.model.DataSource
|
||||||
@ -74,15 +75,15 @@ class ChannelViewModel @Inject constructor(
|
|||||||
private val logger = delegate.install(Profiles.VIEWMODEL_CHANNEL)
|
private val logger = delegate.install(Profiles.VIEWMODEL_CHANNEL)
|
||||||
|
|
||||||
// searched screencast devices
|
// searched screencast devices
|
||||||
internal var devices by mutableStateOf(emptyList<Device>())
|
var devices by mutableStateOf(emptyList<Device>())
|
||||||
|
|
||||||
private val _volume: MutableStateFlow<Float> by lazy {
|
private val _volume: MutableStateFlow<Float> by lazy {
|
||||||
MutableStateFlow(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) / 100f)
|
MutableStateFlow(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) / 100f)
|
||||||
}
|
}
|
||||||
internal val volume = _volume.asStateFlow()
|
val volume = _volume.asStateFlow()
|
||||||
|
|
||||||
internal val channel: StateFlow<Channel?> = playerManager.channel
|
val channel: StateFlow<Channel?> = playerManager.channel
|
||||||
internal val playlist: StateFlow<Playlist?> = playerManager.playlist
|
val playlist: StateFlow<Playlist?> = playerManager.playlist
|
||||||
|
|
||||||
val adjacentChannels: StateFlow<AdjacentChannels?> = flatmapCombined(
|
val adjacentChannels: StateFlow<AdjacentChannels?> = flatmapCombined(
|
||||||
playlist,
|
playlist,
|
||||||
@ -103,9 +104,9 @@ class ChannelViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
internal val isSeriesPlaylist: Flow<Boolean> = playlist.map { it?.isSeries ?: false }
|
val isSeriesPlaylist: Flow<Boolean> = playlist.map { it?.isSeries ?: false }
|
||||||
|
|
||||||
internal val isProgrammeSupported: Flow<Boolean> = playlist.map {
|
val isProgrammeSupported: Flow<Boolean> = playlist.map {
|
||||||
it ?: return@map false
|
it ?: return@map false
|
||||||
if (it.isSeries || it.isVod) return@map false
|
if (it.isSeries || it.isVod) return@map false
|
||||||
when (it.source) {
|
when (it.source) {
|
||||||
@ -115,16 +116,16 @@ class ChannelViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val tracks: Flow<Map<Int, List<Format>>> = playerManager.tracks
|
val tracks: Flow<Map<Int, List<Format>>> = playerManager.tracks
|
||||||
.map { all ->
|
.map { all ->
|
||||||
all
|
all
|
||||||
.mapValues { (_, formats) -> formats }
|
.mapValues { (_, formats) -> formats }
|
||||||
.toMap()
|
.toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val currentTracks: Flow<Map<@C.TrackType Int, Format?>> = playerManager.currentTracks
|
val currentTracks: Flow<Map<@C.TrackType Int, Format?>> = playerManager.currentTracks
|
||||||
|
|
||||||
internal fun chooseTrack(type: @C.TrackType Int, format: Format) {
|
fun chooseTrack(type: @C.TrackType Int, format: Format) {
|
||||||
val groups = playerManager.tracksGroups.value
|
val groups = playerManager.tracksGroups.value
|
||||||
val group = groups.find { it.type == type } ?: return
|
val group = groups.find { it.type == type } ?: return
|
||||||
val trackGroup = group.mediaTrackGroup
|
val trackGroup = group.mediaTrackGroup
|
||||||
@ -139,12 +140,12 @@ class ChannelViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun clearTrack(type: @C.TrackType Int) {
|
fun clearTrack(type: @C.TrackType Int) {
|
||||||
playerManager.clearTrack(type)
|
playerManager.clearTrack(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// channel playing state
|
// channel playing state
|
||||||
internal val playerState: StateFlow<PlayerState> = combine(
|
val playerState: StateFlow<PlayerState> = combine(
|
||||||
playerManager.player,
|
playerManager.player,
|
||||||
playerManager.playbackState,
|
playerManager.playbackState,
|
||||||
playerManager.size,
|
playerManager.size,
|
||||||
@ -168,14 +169,14 @@ class ChannelViewModel @Inject constructor(
|
|||||||
private val _isDevicesVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
private val _isDevicesVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||||
|
|
||||||
// show searching devices dialog or not
|
// show searching devices dialog or not
|
||||||
internal val isDevicesVisible = _isDevicesVisible.asStateFlow()
|
val isDevicesVisible = _isDevicesVisible.asStateFlow()
|
||||||
|
|
||||||
private val _searching: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
private val _searching: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||||
|
|
||||||
// searching or not
|
// searching or not
|
||||||
internal val searching = _searching.asStateFlow()
|
val searching = _searching.asStateFlow()
|
||||||
|
|
||||||
internal fun openDlnaDevices() {
|
fun openDlnaDevices() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
delay(800.milliseconds)
|
delay(800.milliseconds)
|
||||||
_searching.value = true
|
_searching.value = true
|
||||||
@ -189,7 +190,7 @@ class ChannelViewModel @Inject constructor(
|
|||||||
_isDevicesVisible.value = true
|
_isDevicesVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun closeDlnaDevices() {
|
fun closeDlnaDevices() {
|
||||||
runCatching {
|
runCatching {
|
||||||
_searching.value = false
|
_searching.value = false
|
||||||
_isDevicesVisible.value = false
|
_isDevicesVisible.value = false
|
||||||
@ -213,7 +214,7 @@ class ChannelViewModel @Inject constructor(
|
|||||||
|
|
||||||
private var controlPoint: ControlPoint? = null
|
private var controlPoint: ControlPoint? = null
|
||||||
|
|
||||||
internal fun connectDlnaDevice(device: Device) {
|
fun connectDlnaDevice(device: Device) {
|
||||||
val url = channel.value?.url ?: return
|
val url = channel.value?.url ?: return
|
||||||
device.findAction(ACTION_SET_AV_TRANSPORT_URI)?.invoke(
|
device.findAction(ACTION_SET_AV_TRANSPORT_URI)?.invoke(
|
||||||
argumentValues = mapOf(
|
argumentValues = mapOf(
|
||||||
@ -223,18 +224,18 @@ class ChannelViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun disconnectDlnaDevice(device: Device) {
|
fun disconnectDlnaDevice(device: Device) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onFavourite() {
|
fun onFavourite() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val id = channel.value?.id ?: return@launch
|
val id = channel.value?.id ?: return@launch
|
||||||
channelRepository.favouriteOrUnfavourite(id)
|
channelRepository.favouriteOrUnfavourite(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onVolume(target: Float) {
|
fun onVolume(target: Float) {
|
||||||
_volume.update { target }
|
_volume.update { target }
|
||||||
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||||
audioManager.setStreamVolume(
|
audioManager.setStreamVolume(
|
||||||
@ -246,7 +247,7 @@ class ChannelViewModel @Inject constructor(
|
|||||||
// controlPoint?.setVolume((target * 100).roundToInt(), null)
|
// controlPoint?.setVolume((target * 100).roundToInt(), null)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getPreviousChannel() {
|
fun getPreviousChannel() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val previousChannelId = adjacentChannels.value?.prevId
|
val previousChannelId = adjacentChannels.value?.prevId
|
||||||
if (adjacentChannels.value != null && previousChannelId != null) {
|
if (adjacentChannels.value != null && previousChannelId != null) {
|
||||||
@ -255,7 +256,7 @@ class ChannelViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getNextChannel() {
|
fun getNextChannel() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val nextChannelId = adjacentChannels.value?.nextId
|
val nextChannelId = adjacentChannels.value?.nextId
|
||||||
if (adjacentChannels.value != null && nextChannelId != null) {
|
if (adjacentChannels.value != null && nextChannelId != null) {
|
||||||
@ -279,7 +280,7 @@ class ChannelViewModel @Inject constructor(
|
|||||||
playerManager.pauseOrContinue(isContinued)
|
playerManager.pauseOrContinue(isContinued)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val programmeReminderIds: StateFlow<List<Int>> = workManager.getWorkInfosFlow(
|
val programmeReminderIds: StateFlow<List<Int>> = workManager.getWorkInfosFlow(
|
||||||
WorkQuery.fromStates(
|
WorkQuery.fromStates(
|
||||||
WorkInfo.State.ENQUEUED
|
WorkInfo.State.ENQUEUED
|
||||||
)
|
)
|
||||||
@ -319,14 +320,14 @@ class ChannelViewModel @Inject constructor(
|
|||||||
|
|
||||||
// the channels which is in the same category with the current channel
|
// the channels which is in the same category with the current channel
|
||||||
// or the episodes which is in the same series.
|
// or the episodes which is in the same series.
|
||||||
internal val pagingChannels: Flow<PagingData<Channel>> = playlist.flatMapLatest { playlist ->
|
val pagingChannels: Flow<PagingData<Channel>> = playlist.flatMapLatest { playlist ->
|
||||||
playlist ?: return@flatMapLatest flowOf(PagingData.empty())
|
playlist ?: return@flatMapLatest flowOf(PagingData.empty())
|
||||||
Pager(PagingConfig(10)) {
|
Pager(PagingConfig(10)) {
|
||||||
channelRepository.pagingAllByPlaylistUrl(
|
channelRepository.pagingAllByPlaylistUrl(
|
||||||
playlist.url,
|
playlist.url,
|
||||||
channel.value?.category.orEmpty(),
|
channel.value?.category.orEmpty(),
|
||||||
"",
|
"",
|
||||||
ChannelRepository.Sort.UNSPECIFIED
|
Sort.UNSPECIFIED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.flow
|
.flow
|
||||||
@ -340,7 +341,7 @@ class ChannelViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val programmes: Flow<PagingData<Programme>> = channel.flatMapLatest { channel ->
|
val programmes: Flow<PagingData<Programme>> = channel.flatMapLatest { channel ->
|
||||||
channel ?: return@flatMapLatest flowOf(PagingData.empty())
|
channel ?: return@flatMapLatest flowOf(PagingData.empty())
|
||||||
val relationId = channel.relationId ?: return@flatMapLatest flowOf(PagingData.empty())
|
val relationId = channel.relationId ?: return@flatMapLatest flowOf(PagingData.empty())
|
||||||
val playlist = channel.playlistUrl.let { playlistRepository.get(it) }
|
val playlist = channel.playlistUrl.let { playlistRepository.get(it) }
|
||||||
@ -360,7 +361,7 @@ class ChannelViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val programmeRange: StateFlow<ProgrammeRange> = channel.flatMapLatest { channel ->
|
val programmeRange: StateFlow<ProgrammeRange> = channel.flatMapLatest { channel ->
|
||||||
channel ?: return@flatMapLatest flowOf(defaultProgrammeRange)
|
channel ?: return@flatMapLatest flowOf(defaultProgrammeRange)
|
||||||
val relationId = channel.relationId ?: return@flatMapLatest flowOf(defaultProgrammeRange)
|
val relationId = channel.relationId ?: return@flatMapLatest flowOf(defaultProgrammeRange)
|
||||||
programmeRepository
|
programmeRepository
|
||||||
@ -377,7 +378,7 @@ class ChannelViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun onSpeedUpdated(race: Float) {
|
fun onSpeedUpdated(race: Float) {
|
||||||
playerManager.updateSpeed(race)
|
playerManager.updateSpeed(race)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import androidx.media3.common.PlaybackException
|
|||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
internal data class PlayerState(
|
data class PlayerState(
|
||||||
val playState: @Player.State Int = Player.STATE_IDLE,
|
val playState: @Player.State Int = Player.STATE_IDLE,
|
||||||
val videoSize: Rect = Rect(),
|
val videoSize: Rect = Rect(),
|
||||||
val playerError: PlaybackException? = null,
|
val playerError: PlaybackException? = null,
|
||||||
|
1
business/crash/.gitignore
vendored
1
business/crash/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/build
|
|
@ -1,35 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.com.android.library)
|
|
||||||
alias(libs.plugins.org.jetbrains.kotlin.android)
|
|
||||||
alias(libs.plugins.com.google.devtools.ksp)
|
|
||||||
alias(libs.plugins.com.google.dagger.hilt.android)
|
|
||||||
alias(libs.plugins.compose.compiler)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "com.m3u.business.crash"
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "17"
|
|
||||||
}
|
|
||||||
buildFeatures {
|
|
||||||
compose = true
|
|
||||||
}
|
|
||||||
packaging {
|
|
||||||
resources.excludes += "META-INF/**"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":core"))
|
|
||||||
implementation(project(":ui"))
|
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
|
||||||
implementation(libs.androidx.activity.compose)
|
|
||||||
|
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
||||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
|
||||||
|
|
||||||
implementation(libs.google.dagger.hilt)
|
|
||||||
implementation(libs.androidx.hilt.navigation.compose)
|
|
||||||
ksp(libs.google.dagger.hilt.compiler)
|
|
||||||
}
|
|
21
business/crash/proguard-rules.pro
vendored
21
business/crash/proguard-rules.pro
vendored
@ -1,21 +0,0 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.kts.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest>
|
|
||||||
|
|
||||||
</manifest>
|
|
@ -1,3 +0,0 @@
|
|||||||
package com.m3u.business.crash.screen.list.navigation
|
|
||||||
|
|
||||||
typealias NavigateToDetail = (path: String) -> Unit
|
|
@ -21,7 +21,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
implementation(project(":ui"))
|
implementation(project(":data"))
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import com.m3u.core.architecture.logger.Profiles
|
|||||||
import com.m3u.core.architecture.logger.install
|
import com.m3u.core.architecture.logger.install
|
||||||
import com.m3u.core.architecture.preferences.Preferences
|
import com.m3u.core.architecture.preferences.Preferences
|
||||||
import com.m3u.core.wrapper.Resource
|
import com.m3u.core.wrapper.Resource
|
||||||
|
import com.m3u.core.wrapper.Sort
|
||||||
import com.m3u.core.wrapper.asResource
|
import com.m3u.core.wrapper.asResource
|
||||||
import com.m3u.core.wrapper.mapResource
|
import com.m3u.core.wrapper.mapResource
|
||||||
import com.m3u.core.wrapper.resource
|
import com.m3u.core.wrapper.resource
|
||||||
@ -29,7 +30,6 @@ import com.m3u.data.repository.playlist.PlaylistRepository
|
|||||||
import com.m3u.data.repository.channel.ChannelRepository
|
import com.m3u.data.repository.channel.ChannelRepository
|
||||||
import com.m3u.data.service.MediaCommand
|
import com.m3u.data.service.MediaCommand
|
||||||
import com.m3u.data.service.PlayerManager
|
import com.m3u.data.service.PlayerManager
|
||||||
import com.m3u.ui.Sort
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@ -161,9 +161,9 @@ class FavouriteViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val series = MutableStateFlow<Channel?>(null)
|
val series = MutableStateFlow<Channel?>(null)
|
||||||
internal val seriesReplay = MutableStateFlow(0)
|
val seriesReplay = MutableStateFlow(0)
|
||||||
internal val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
||||||
.combine(seriesReplay) { series, _ -> series }
|
.combine(seriesReplay) { series, _ -> series }
|
||||||
.flatMapLatest { series ->
|
.flatMapLatest { series ->
|
||||||
if (series == null) flow { }
|
if (series == null) flow { }
|
||||||
@ -177,10 +177,10 @@ class FavouriteViewModel @Inject constructor(
|
|||||||
started = SharingStarted.Lazily
|
started = SharingStarted.Lazily
|
||||||
)
|
)
|
||||||
|
|
||||||
internal suspend fun getPlaylist(playlistUrl: String): Playlist? =
|
suspend fun getPlaylist(playlistUrl: String): Playlist? =
|
||||||
playlistRepository.get(playlistUrl)
|
playlistRepository.get(playlistUrl)
|
||||||
|
|
||||||
internal fun playRandomly() {
|
fun playRandomly() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val channel = channelRepository.getRandomIgnoreSeriesAndHidden() ?: return@launch
|
val channel = channelRepository.getRandomIgnoreSeriesAndHidden() ?: return@launch
|
||||||
playerManager.play(
|
playerManager.play(
|
||||||
|
@ -21,15 +21,13 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
implementation(project(":ui"))
|
implementation(project(":data"))
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||||
|
|
||||||
implementation(libs.airbnb.lottie.compose)
|
|
||||||
|
|
||||||
implementation(libs.google.dagger.hilt)
|
implementation(libs.google.dagger.hilt)
|
||||||
implementation(libs.androidx.hilt.navigation.compose)
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
ksp(libs.google.dagger.hilt.compiler)
|
ksp(libs.google.dagger.hilt.compiler)
|
||||||
|
@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.WorkQuery
|
import androidx.work.WorkQuery
|
||||||
|
import com.m3u.business.foryou.Recommend
|
||||||
import com.m3u.core.architecture.dispatcher.Dispatcher
|
import com.m3u.core.architecture.dispatcher.Dispatcher
|
||||||
import com.m3u.core.architecture.dispatcher.M3uDispatchers.IO
|
import com.m3u.core.architecture.dispatcher.M3uDispatchers.IO
|
||||||
import com.m3u.core.architecture.logger.Logger
|
import com.m3u.core.architecture.logger.Logger
|
||||||
@ -26,7 +27,6 @@ import com.m3u.data.repository.channel.ChannelRepository
|
|||||||
import com.m3u.data.repository.playlist.PlaylistRepository
|
import com.m3u.data.repository.playlist.PlaylistRepository
|
||||||
import com.m3u.data.repository.programme.ProgrammeRepository
|
import com.m3u.data.repository.programme.ProgrammeRepository
|
||||||
import com.m3u.data.worker.SubscriptionWorker
|
import com.m3u.data.worker.SubscriptionWorker
|
||||||
import com.m3u.business.foryou.components.recommend.Recommend
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@ -58,7 +58,7 @@ class ForyouViewModel @Inject constructor(
|
|||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val logger = delegate.install(Profiles.VIEWMODEL_FORYOU)
|
private val logger = delegate.install(Profiles.VIEWMODEL_FORYOU)
|
||||||
|
|
||||||
internal val playlistCounts: StateFlow<Resource<List<PlaylistWithCount>>> =
|
val playlistCounts: StateFlow<Resource<List<PlaylistWithCount>>> =
|
||||||
playlistRepository
|
playlistRepository
|
||||||
.observeAllCounts()
|
.observeAllCounts()
|
||||||
.asResource()
|
.asResource()
|
||||||
@ -68,7 +68,7 @@ class ForyouViewModel @Inject constructor(
|
|||||||
initialValue = Resource.Loading
|
initialValue = Resource.Loading
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val subscribingPlaylistUrls: StateFlow<List<String>> =
|
val subscribingPlaylistUrls: StateFlow<List<String>> =
|
||||||
workManager.getWorkInfosFlow(
|
workManager.getWorkInfosFlow(
|
||||||
WorkQuery.fromStates(
|
WorkQuery.fromStates(
|
||||||
WorkInfo.State.RUNNING,
|
WorkInfo.State.RUNNING,
|
||||||
@ -86,7 +86,7 @@ class ForyouViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val refreshingEpgUrls: Flow<List<String>> = programmeRepository.refreshingEpgUrls
|
val refreshingEpgUrls: Flow<List<String>> = programmeRepository.refreshingEpgUrls
|
||||||
|
|
||||||
private val unseensDuration = snapshotFlow { preferences.unseensMilliseconds }
|
private val unseensDuration = snapshotFlow { preferences.unseensMilliseconds }
|
||||||
.map { it.toDuration(DurationUnit.MILLISECONDS) }
|
.map { it.toDuration(DurationUnit.MILLISECONDS) }
|
||||||
@ -105,7 +105,7 @@ class ForyouViewModel @Inject constructor(
|
|||||||
initialValue = null,
|
initialValue = null,
|
||||||
started = SharingStarted.Lazily
|
started = SharingStarted.Lazily
|
||||||
)
|
)
|
||||||
internal val specs: StateFlow<List<Recommend.Spec>> = unseensDuration
|
val specs: StateFlow<List<Recommend.Spec>> = unseensDuration
|
||||||
.flatMapLatest { channelRepository.observeAllUnseenFavourites(it) }
|
.flatMapLatest { channelRepository.observeAllUnseenFavourites(it) }
|
||||||
.let { flow ->
|
.let { flow ->
|
||||||
combine(flow, newRelease) { channels, nr ->
|
combine(flow, newRelease) { channels, nr ->
|
||||||
@ -132,15 +132,15 @@ class ForyouViewModel @Inject constructor(
|
|||||||
initialValue = emptyList()
|
initialValue = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun onUnsubscribePlaylist(url: String) {
|
fun onUnsubscribePlaylist(url: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.unsubscribe(url)
|
playlistRepository.unsubscribe(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val series = MutableStateFlow<Channel?>(null)
|
val series = MutableStateFlow<Channel?>(null)
|
||||||
internal val seriesReplay = MutableStateFlow(0)
|
val seriesReplay = MutableStateFlow(0)
|
||||||
internal val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
||||||
.combine(seriesReplay) { series, _ -> series }
|
.combine(seriesReplay) { series, _ -> series }
|
||||||
.flatMapLatest { series ->
|
.flatMapLatest { series ->
|
||||||
if (series == null) flow { }
|
if (series == null) flow { }
|
||||||
@ -154,6 +154,6 @@ class ForyouViewModel @Inject constructor(
|
|||||||
started = SharingStarted.Lazily
|
started = SharingStarted.Lazily
|
||||||
)
|
)
|
||||||
|
|
||||||
internal suspend fun getPlaylist(playlistUrl: String): Playlist? =
|
suspend fun getPlaylist(playlistUrl: String): Playlist? =
|
||||||
playlistRepository.get(playlistUrl)
|
playlistRepository.get(playlistUrl)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.m3u.business.foryou.components.recommend
|
package com.m3u.business.foryou
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.m3u.core.unit.DataUnit
|
import com.m3u.core.unit.DataUnit
|
||||||
import com.m3u.data.database.model.Playlist
|
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
|
import com.m3u.data.database.model.Playlist
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
class Recommend(
|
class Recommend(
|
@ -1,38 +0,0 @@
|
|||||||
package com.m3u.business.foryou.internal
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.hardware.Sensor
|
|
||||||
import android.hardware.SensorEvent
|
|
||||||
import android.hardware.SensorEventListener
|
|
||||||
import android.hardware.SensorManager
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.produceState
|
|
||||||
import androidx.compose.ui.geometry.Offset
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun produceSensorOffset(
|
|
||||||
context: Context = LocalContext.current
|
|
||||||
): State<Offset> = produceState(
|
|
||||||
initialValue = Offset.Zero
|
|
||||||
) {
|
|
||||||
val manager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
|
||||||
val listener = object : SensorEventListener {
|
|
||||||
override fun onSensorChanged(event: SensorEvent) {
|
|
||||||
val (x, y, _) = event.values
|
|
||||||
value = Offset(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
|
|
||||||
}
|
|
||||||
val sensor = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
|
||||||
manager.registerListener(
|
|
||||||
listener,
|
|
||||||
sensor,
|
|
||||||
SensorManager.SENSOR_DELAY_NORMAL
|
|
||||||
)
|
|
||||||
awaitDispose {
|
|
||||||
manager.unregisterListener(listener)
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
implementation(project(":ui"))
|
implementation(project(":data"))
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import androidx.navigation.navArgument
|
|||||||
private const val PLAYLIST_CONFIGURATION_ROUTE_PATH = "playlist_configuration_route"
|
private const val PLAYLIST_CONFIGURATION_ROUTE_PATH = "playlist_configuration_route"
|
||||||
|
|
||||||
object PlaylistConfigurationNavigation {
|
object PlaylistConfigurationNavigation {
|
||||||
internal const val TYPE_PLAYLIST_URL = "playlist_url"
|
const val TYPE_PLAYLIST_URL = "playlist_url"
|
||||||
|
|
||||||
const val PLAYLIST_CONFIGURATION_ROUTE =
|
const val PLAYLIST_CONFIGURATION_ROUTE =
|
||||||
"$PLAYLIST_CONFIGURATION_ROUTE_PATH?$TYPE_PLAYLIST_URL={$TYPE_PLAYLIST_URL}"
|
"$PLAYLIST_CONFIGURATION_ROUTE_PATH?$TYPE_PLAYLIST_URL={$TYPE_PLAYLIST_URL}"
|
||||||
@ -34,25 +34,3 @@ fun NavController.navigateToPlaylistConfiguration(
|
|||||||
val route = PlaylistConfigurationNavigation.createPlaylistConfigurationRoute(encodedUrl)
|
val route = PlaylistConfigurationNavigation.createPlaylistConfigurationRoute(encodedUrl)
|
||||||
this.navigate(route, navOptions)
|
this.navigate(route, navOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun NavGraphBuilder.playlistConfigurationScreen(
|
|
||||||
contentPadding: PaddingValues = PaddingValues(),
|
|
||||||
) {
|
|
||||||
composable(
|
|
||||||
route = PlaylistConfigurationNavigation.PLAYLIST_CONFIGURATION_ROUTE,
|
|
||||||
arguments = listOf(
|
|
||||||
navArgument(PlaylistConfigurationNavigation.TYPE_PLAYLIST_URL) {
|
|
||||||
type = NavType.StringType
|
|
||||||
}
|
|
||||||
),
|
|
||||||
enterTransition = { slideInVertically { it } },
|
|
||||||
exitTransition = { fadeOut() },
|
|
||||||
popEnterTransition = { fadeIn() },
|
|
||||||
popExitTransition = { slideOutVertically { it } }
|
|
||||||
) {
|
|
||||||
PlaylistConfigurationRoute(
|
|
||||||
contentPadding = contentPadding
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -38,7 +38,7 @@ import kotlinx.datetime.TimeZone
|
|||||||
import kotlinx.datetime.toLocalDateTime
|
import kotlinx.datetime.toLocalDateTime
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal typealias EpgManifest = Map<Playlist, Boolean>
|
typealias EpgManifest = Map<Playlist, Boolean>
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class PlaylistConfigurationViewModel @Inject constructor(
|
class PlaylistConfigurationViewModel @Inject constructor(
|
||||||
@ -53,7 +53,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
|||||||
private val logger = delegate.install(Profiles.VIEWMODEL_PLAYLIST_CONFIGURATION)
|
private val logger = delegate.install(Profiles.VIEWMODEL_PLAYLIST_CONFIGURATION)
|
||||||
private val playlistUrl: StateFlow<String> = savedStateHandle
|
private val playlistUrl: StateFlow<String> = savedStateHandle
|
||||||
.getStateFlow(PlaylistConfigurationNavigation.TYPE_PLAYLIST_URL, "")
|
.getStateFlow(PlaylistConfigurationNavigation.TYPE_PLAYLIST_URL, "")
|
||||||
internal val playlist: StateFlow<Playlist?> = playlistUrl.flatMapLatest {
|
val playlist: StateFlow<Playlist?> = playlistUrl.flatMapLatest {
|
||||||
playlistRepository.observe(it)
|
playlistRepository.observe(it)
|
||||||
}
|
}
|
||||||
.stateIn(
|
.stateIn(
|
||||||
@ -62,7 +62,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val xtreamUserInfo: StateFlow<Resource<XtreamInfo.UserInfo>> =
|
val xtreamUserInfo: StateFlow<Resource<XtreamInfo.UserInfo>> =
|
||||||
playlist.map { playlist ->
|
playlist.map { playlist ->
|
||||||
playlist ?: return@map null
|
playlist ?: return@map null
|
||||||
if (playlist.source != DataSource.Xtream) return@map null
|
if (playlist.source != DataSource.Xtream) return@map null
|
||||||
@ -81,7 +81,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
|||||||
started = SharingStarted.Lazily
|
started = SharingStarted.Lazily
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val manifest: StateFlow<EpgManifest> = combine(
|
val manifest: StateFlow<EpgManifest> = combine(
|
||||||
playlistRepository.observeAllEpgs(),
|
playlistRepository.observeAllEpgs(),
|
||||||
playlist
|
playlist
|
||||||
) { epgs, playlist ->
|
) { epgs, playlist ->
|
||||||
@ -93,7 +93,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
|||||||
started = SharingStarted.Lazily,
|
started = SharingStarted.Lazily,
|
||||||
initialValue = emptyMap()
|
initialValue = emptyMap()
|
||||||
)
|
)
|
||||||
internal val subscribingOrRefreshingWorkInfo: StateFlow<WorkInfo?> = workManager
|
val subscribingOrRefreshingWorkInfo: StateFlow<WorkInfo?> = workManager
|
||||||
.getWorkInfosFlow(
|
.getWorkInfosFlow(
|
||||||
WorkQuery.fromStates(
|
WorkQuery.fromStates(
|
||||||
WorkInfo.State.RUNNING,
|
WorkInfo.State.RUNNING,
|
||||||
@ -114,7 +114,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val expired: StateFlow<LocalDateTime?> = playlistUrl
|
val expired: StateFlow<LocalDateTime?> = playlistUrl
|
||||||
.flatMapLatest { playlistUrl ->
|
.flatMapLatest { playlistUrl ->
|
||||||
programmeRepository.observeProgrammeRange(playlistUrl)
|
programmeRepository.observeProgrammeRange(playlistUrl)
|
||||||
}
|
}
|
||||||
@ -131,39 +131,39 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
internal fun onUpdatePlaylistTitle(title: String) {
|
fun onUpdatePlaylistTitle(title: String) {
|
||||||
val playlistUrl = playlistUrl.value
|
val playlistUrl = playlistUrl.value
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.onUpdatePlaylistTitle(playlistUrl, title)
|
playlistRepository.onUpdatePlaylistTitle(playlistUrl, title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onUpdatePlaylistUserAgent(userAgent: String?) {
|
fun onUpdatePlaylistUserAgent(userAgent: String?) {
|
||||||
val playlistUrl = playlistUrl.value
|
val playlistUrl = playlistUrl.value
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.onUpdatePlaylistUserAgent(playlistUrl, userAgent)
|
playlistRepository.onUpdatePlaylistUserAgent(playlistUrl, userAgent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onUpdateEpgPlaylist(usecase: PlaylistRepository.EpgPlaylistUseCase) {
|
fun onUpdateEpgPlaylist(usecase: PlaylistRepository.EpgPlaylistUseCase) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.onUpdateEpgPlaylist(usecase)
|
playlistRepository.onUpdateEpgPlaylist(usecase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onUpdatePlaylistAutoRefreshProgrammes() {
|
fun onUpdatePlaylistAutoRefreshProgrammes() {
|
||||||
val playlistUrl = playlistUrl.value
|
val playlistUrl = playlistUrl.value
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.onUpdatePlaylistAutoRefreshProgrammes(playlistUrl)
|
playlistRepository.onUpdatePlaylistAutoRefreshProgrammes(playlistUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onSyncProgrammes() {
|
fun onSyncProgrammes() {
|
||||||
val playlistUrl = playlistUrl.value
|
val playlistUrl = playlistUrl.value
|
||||||
SubscriptionWorker.epg(workManager, playlistUrl, true)
|
SubscriptionWorker.epg(workManager, playlistUrl, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onCancelSyncProgrammes() {
|
fun onCancelSyncProgrammes() {
|
||||||
val workInfo = subscribingOrRefreshingWorkInfo.value
|
val workInfo = subscribingOrRefreshingWorkInfo.value
|
||||||
workInfo?.id?.let { workManager.cancelWorkById(it) }
|
workInfo?.id?.let { workManager.cancelWorkById(it) }
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,10 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
implementation(project(":ui"))
|
implementation(project(":data"))
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
|
||||||
// for m2 BackdropScaffold only
|
|
||||||
implementation("androidx.compose.material:material")
|
|
||||||
|
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||||
|
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.m3u.business.playlist
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavOptions
|
||||||
|
|
||||||
|
const val PLAYLIST_ROUTE_PATH = "playlist_route"
|
||||||
|
|
||||||
|
object PlaylistNavigation {
|
||||||
|
const val TYPE_URL = "url"
|
||||||
|
|
||||||
|
const val PLAYLIST_ROUTE =
|
||||||
|
"$PLAYLIST_ROUTE_PATH?$TYPE_URL={$TYPE_URL}"
|
||||||
|
|
||||||
|
internal fun createPlaylistRoute(url: String): String {
|
||||||
|
return "$PLAYLIST_ROUTE_PATH?$TYPE_URL=$url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavController.navigateToPlaylist(
|
||||||
|
playlistUrl: String,
|
||||||
|
navOptions: NavOptions? = null,
|
||||||
|
) {
|
||||||
|
val encodedUrl = Uri.encode(playlistUrl)
|
||||||
|
val route = PlaylistNavigation.createPlaylistRoute(encodedUrl)
|
||||||
|
this.navigate(route, navOptions)
|
||||||
|
}
|
@ -52,9 +52,7 @@ import com.m3u.data.service.Messager
|
|||||||
import com.m3u.data.service.PlayerManager
|
import com.m3u.data.service.PlayerManager
|
||||||
import com.m3u.data.worker.SubscriptionWorker
|
import com.m3u.data.worker.SubscriptionWorker
|
||||||
import com.m3u.business.playlist.PlaylistMessage.ChannelCoverSaved
|
import com.m3u.business.playlist.PlaylistMessage.ChannelCoverSaved
|
||||||
import com.m3u.business.playlist.navigation.PlaylistNavigation
|
import com.m3u.core.wrapper.Sort
|
||||||
import com.m3u.ui.Sort
|
|
||||||
import com.m3u.ui.toCommonSort
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
@ -99,10 +97,10 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val logger = delegate.install(Profiles.VIEWMODEL_PLAYLIST)
|
private val logger = delegate.install(Profiles.VIEWMODEL_PLAYLIST)
|
||||||
|
|
||||||
internal val playlistUrl: StateFlow<String> = savedStateHandle
|
val playlistUrl: StateFlow<String> = savedStateHandle
|
||||||
.getStateFlow(PlaylistNavigation.TYPE_URL, "")
|
.getStateFlow(PlaylistNavigation.TYPE_URL, "")
|
||||||
|
|
||||||
internal val playlist: StateFlow<Playlist?> = playlistUrl.flatMapLatest {
|
val playlist: StateFlow<Playlist?> = playlistUrl.flatMapLatest {
|
||||||
playlistRepository.observe(it)
|
playlistRepository.observe(it)
|
||||||
}
|
}
|
||||||
.stateIn(
|
.stateIn(
|
||||||
@ -111,7 +109,7 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val zapping: StateFlow<Channel?> = combine(
|
val zapping: StateFlow<Channel?> = combine(
|
||||||
snapshotFlow { preferences.zappingMode },
|
snapshotFlow { preferences.zappingMode },
|
||||||
playerManager.channel,
|
playerManager.channel,
|
||||||
playlistUrl.flatMapLatest { channelRepository.observeAllByPlaylistUrl(it) }
|
playlistUrl.flatMapLatest { channelRepository.observeAllByPlaylistUrl(it) }
|
||||||
@ -125,7 +123,7 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000)
|
started = SharingStarted.WhileSubscribed(5_000)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val subscribingOrRefreshing: StateFlow<Boolean> = workManager
|
val subscribingOrRefreshing: StateFlow<Boolean> = workManager
|
||||||
.getWorkInfosFlow(
|
.getWorkInfosFlow(
|
||||||
WorkQuery.fromStates(
|
WorkQuery.fromStates(
|
||||||
WorkInfo.State.RUNNING,
|
WorkInfo.State.RUNNING,
|
||||||
@ -146,20 +144,20 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5000)
|
started = SharingStarted.WhileSubscribed(5000)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun refresh() {
|
fun refresh() {
|
||||||
val url = playlistUrl.value
|
val url = playlistUrl.value
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.refresh(url)
|
playlistRepository.refresh(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun favourite(id: Int) {
|
fun favourite(id: Int) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
channelRepository.favouriteOrUnfavourite(id)
|
channelRepository.favouriteOrUnfavourite(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun savePicture(id: Int) {
|
fun savePicture(id: Int) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val channel = channelRepository.get(id)
|
val channel = channelRepository.get(id)
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
@ -188,7 +186,7 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun hide(id: Int) {
|
fun hide(id: Int) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val channel = channelRepository.get(id)
|
val channel = channelRepository.get(id)
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
@ -199,7 +197,7 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun createShortcut(context: Context, id: Int) {
|
fun createShortcut(context: Context, id: Int) {
|
||||||
val shortcutId = "channel_$id"
|
val shortcutId = "channel_$id"
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val channel = channelRepository.get(id) ?: return@launch
|
val channel = channelRepository.get(id) ?: return@launch
|
||||||
@ -227,7 +225,7 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
internal fun createTvRecommend(activityContext: Context, id: Int) {
|
fun createTvRecommend(activityContext: Context, id: Int) {
|
||||||
val channelInternalProviderId = "M3U"
|
val channelInternalProviderId = "M3U"
|
||||||
val programInternalProviderId = "Program_$id"
|
val programInternalProviderId = "Program_$id"
|
||||||
val contentResolver = activityContext.contentResolver
|
val contentResolver = activityContext.contentResolver
|
||||||
@ -301,13 +299,13 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal suspend fun getProgrammeCurrently(channelId: Int): Programme? {
|
suspend fun getProgrammeCurrently(channelId: Int): Programme? {
|
||||||
return programmeRepository.getProgrammeCurrently(channelId)
|
return programmeRepository.getProgrammeCurrently(channelId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sortIndex: MutableStateFlow<Int> = MutableStateFlow(0)
|
private val sortIndex: MutableStateFlow<Int> = MutableStateFlow(0)
|
||||||
|
|
||||||
internal val sort: StateFlow<Sort> = sortIndex
|
val sort: StateFlow<Sort> = sortIndex
|
||||||
.map { Sort.entries[it] }
|
.map { Sort.entries[it] }
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
@ -315,12 +313,12 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun sort(sort: Sort) {
|
fun sort(sort: Sort) {
|
||||||
sortIndex.value = Sort.entries.indexOf(sort).coerceAtLeast(0)
|
sortIndex.value = Sort.entries.indexOf(sort).coerceAtLeast(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val query = MutableStateFlow("")
|
val query = MutableStateFlow("")
|
||||||
internal val scrollUp: MutableStateFlow<Event<Unit>> = MutableStateFlow(handledEvent())
|
val scrollUp: MutableStateFlow<Event<Unit>> = MutableStateFlow(handledEvent())
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class ChannelParameters(
|
data class ChannelParameters(
|
||||||
@ -354,7 +352,7 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
started = SharingStarted.Lazily
|
started = SharingStarted.Lazily
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val channels: StateFlow<List<CategoryWithChannels>> = combine(
|
val channels: StateFlow<List<CategoryWithChannels>> = combine(
|
||||||
playlistUrl,
|
playlistUrl,
|
||||||
categories,
|
categories,
|
||||||
query, sort
|
query, sort
|
||||||
@ -376,7 +374,7 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
playlistUrl,
|
playlistUrl,
|
||||||
"",
|
"",
|
||||||
query,
|
query,
|
||||||
sort.toCommonSort()
|
sort
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.flow
|
.flow
|
||||||
@ -392,7 +390,7 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
playlistUrl,
|
playlistUrl,
|
||||||
category,
|
category,
|
||||||
query,
|
query,
|
||||||
sort.toCommonSort()
|
sort
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.flow
|
.flow
|
||||||
@ -407,7 +405,7 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
started = SharingStarted.Lazily
|
started = SharingStarted.Lazily
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val pinnedCategories: StateFlow<List<String>> = playlist
|
val pinnedCategories: StateFlow<List<String>> = playlist
|
||||||
.map { it?.pinnedCategories ?: emptyList() }
|
.map { it?.pinnedCategories ?: emptyList() }
|
||||||
|
|
||||||
.flowOn(ioDispatcher)
|
.flowOn(ioDispatcher)
|
||||||
@ -417,21 +415,21 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun pinOrUnpinCategory(category: String) {
|
fun pinOrUnpinCategory(category: String) {
|
||||||
val currentPlaylistUrl = playlistUrl.value
|
val currentPlaylistUrl = playlistUrl.value
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.pinOrUnpinCategory(currentPlaylistUrl, category)
|
playlistRepository.pinOrUnpinCategory(currentPlaylistUrl, category)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun hideCategory(category: String) {
|
fun hideCategory(category: String) {
|
||||||
val currentPlaylistUrl = playlistUrl.value
|
val currentPlaylistUrl = playlistUrl.value
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.hideOrUnhideCategory(currentPlaylistUrl, category)
|
playlistRepository.hideOrUnhideCategory(currentPlaylistUrl, category)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun setup(
|
fun setup(
|
||||||
channelId: Int,
|
channelId: Int,
|
||||||
onPlayMediaCommand: (MediaCommand) -> Unit
|
onPlayMediaCommand: (MediaCommand) -> Unit
|
||||||
) {
|
) {
|
||||||
@ -448,10 +446,10 @@ class PlaylistViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val series = MutableStateFlow<Channel?>(null)
|
val series = MutableStateFlow<Channel?>(null)
|
||||||
internal val seriesReplay = MutableStateFlow(0)
|
val seriesReplay = MutableStateFlow(0)
|
||||||
|
|
||||||
internal val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
||||||
.combine(seriesReplay) { series, _ -> series }
|
.combine(seriesReplay) { series, _ -> series }
|
||||||
.flatMapLatest { series ->
|
.flatMapLatest { series ->
|
||||||
if (series == null) flow {}
|
if (series == null) flow {}
|
||||||
|
@ -21,7 +21,7 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
implementation(project(":ui"))
|
implementation(project(":data"))
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
|
||||||
@ -35,8 +35,4 @@ dependencies {
|
|||||||
implementation(libs.androidx.work.runtime.ktx)
|
implementation(libs.androidx.work.runtime.ktx)
|
||||||
ksp(libs.androidx.hilt.compiler)
|
ksp(libs.androidx.hilt.compiler)
|
||||||
implementation(libs.androidx.hilt.work)
|
implementation(libs.androidx.hilt.work)
|
||||||
|
|
||||||
implementation(libs.androidx.compose.material3.adaptive)
|
|
||||||
implementation(libs.androidx.compose.material3.adaptive.navigation)
|
|
||||||
implementation(libs.androidx.compose.material3.adaptive.layout)
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.m3u.business.setting
|
package com.m3u.business.setting
|
||||||
|
|
||||||
internal enum class BackingUpAndRestoringState {
|
enum class BackingUpAndRestoringState {
|
||||||
NONE, BACKING_UP, RESTORING, BOTH;
|
NONE, BACKING_UP, RESTORING, BOTH;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -68,7 +68,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val logger = delegate.install(Profiles.VIEWMODEL_SETTING)
|
private val logger = delegate.install(Profiles.VIEWMODEL_SETTING)
|
||||||
|
|
||||||
internal val epgs: StateFlow<List<Playlist>> = playlistRepository
|
val epgs: StateFlow<List<Playlist>> = playlistRepository
|
||||||
.observeAllEpgs()
|
.observeAllEpgs()
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
@ -76,7 +76,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val hiddenChannels: StateFlow<List<Channel>> = channelRepository
|
val hiddenChannels: StateFlow<List<Channel>> = channelRepository
|
||||||
.observeAllHidden()
|
.observeAllHidden()
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
@ -84,7 +84,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val hiddenCategoriesWithPlaylists: StateFlow<List<Pair<Playlist, String>>> =
|
val hiddenCategoriesWithPlaylists: StateFlow<List<Pair<Playlist, String>>> =
|
||||||
playlistRepository
|
playlistRepository
|
||||||
.observeAll()
|
.observeAll()
|
||||||
.map { playlists ->
|
.map { playlists ->
|
||||||
@ -99,13 +99,13 @@ class SettingViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5_000L)
|
started = SharingStarted.WhileSubscribed(5_000L)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun onUnhidePlaylistCategory(playlistUrl: String, group: String) {
|
fun onUnhidePlaylistCategory(playlistUrl: String, group: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.hideOrUnhideCategory(playlistUrl, group)
|
playlistRepository.hideOrUnhideCategory(playlistUrl, group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val colorSchemes: StateFlow<List<ColorScheme>> = combine(
|
val colorSchemes: StateFlow<List<ColorScheme>> = combine(
|
||||||
colorSchemeDao.observeAll().catch { emit(emptyList()) },
|
colorSchemeDao.observeAll().catch { emit(emptyList()) },
|
||||||
snapshotFlow { preferences.followSystemTheme }
|
snapshotFlow { preferences.followSystemTheme }
|
||||||
) { all, followSystemTheme -> if (followSystemTheme) all.filter { !it.isDark } else all }
|
) { all, followSystemTheme -> if (followSystemTheme) all.filter { !it.isDark } else all }
|
||||||
@ -116,7 +116,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
initialValue = emptyList()
|
initialValue = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun onClipboard(url: String) {
|
fun onClipboard(url: String) {
|
||||||
val title = run {
|
val title = run {
|
||||||
val filePath = url.split("/")
|
val filePath = url.split("/")
|
||||||
val fileSplit = filePath.lastOrNull()?.split(".") ?: emptyList()
|
val fileSplit = filePath.lastOrNull()?.split(".") ?: emptyList()
|
||||||
@ -137,7 +137,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onUnhideChannel(channelId: Int) {
|
fun onUnhideChannel(channelId: Int) {
|
||||||
val hidden = hiddenChannels.value.find { it.id == channelId }
|
val hidden = hiddenChannels.value.find { it.id == channelId }
|
||||||
if (hidden != null) {
|
if (hidden != null) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@ -146,7 +146,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun subscribe() {
|
fun subscribe() {
|
||||||
val title = titleState.value
|
val title = titleState.value
|
||||||
val url = urlState.value
|
val url = urlState.value
|
||||||
val uri = uriState.value
|
val uri = uriState.value
|
||||||
@ -237,7 +237,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
resetAllInputs()
|
resetAllInputs()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val backingUpOrRestoring: StateFlow<BackingUpAndRestoringState> = workManager
|
val backingUpOrRestoring: StateFlow<BackingUpAndRestoringState> = workManager
|
||||||
.getWorkInfosFlow(
|
.getWorkInfosFlow(
|
||||||
WorkQuery.fromStates(
|
WorkQuery.fromStates(
|
||||||
WorkInfo.State.RUNNING,
|
WorkInfo.State.RUNNING,
|
||||||
@ -266,7 +266,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
started = SharingStarted.WhileSubscribed(5000)
|
started = SharingStarted.WhileSubscribed(5000)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun backup(uri: Uri) {
|
fun backup(uri: Uri) {
|
||||||
workManager.cancelAllWorkByTag(BackupWorker.TAG)
|
workManager.cancelAllWorkByTag(BackupWorker.TAG)
|
||||||
val request = OneTimeWorkRequestBuilder<BackupWorker>()
|
val request = OneTimeWorkRequestBuilder<BackupWorker>()
|
||||||
.setInputData(
|
.setInputData(
|
||||||
@ -281,7 +281,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
messager.emit(SettingMessage.BackingUp)
|
messager.emit(SettingMessage.BackingUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun restore(uri: Uri) {
|
fun restore(uri: Uri) {
|
||||||
workManager.cancelAllWorkByTag(RestoreWorker.TAG)
|
workManager.cancelAllWorkByTag(RestoreWorker.TAG)
|
||||||
val request = OneTimeWorkRequestBuilder<RestoreWorker>()
|
val request = OneTimeWorkRequestBuilder<RestoreWorker>()
|
||||||
.setInputData(
|
.setInputData(
|
||||||
@ -296,7 +296,7 @@ class SettingViewModel @Inject constructor(
|
|||||||
messager.emit(SettingMessage.Restoring)
|
messager.emit(SettingMessage.Restoring)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val cacheSpace: StateFlow<DataUnit> = playerManager
|
val cacheSpace: StateFlow<DataUnit> = playerManager
|
||||||
.cacheSpace
|
.cacheSpace
|
||||||
.map { DataUnit.of(it) }
|
.map { DataUnit.of(it) }
|
||||||
.stateIn(
|
.stateIn(
|
||||||
@ -315,18 +315,18 @@ class SettingViewModel @Inject constructor(
|
|||||||
epgState.value = ""
|
epgState.value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun clearCache() {
|
fun clearCache() {
|
||||||
playerManager.clearCache()
|
playerManager.clearCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun deleteEpgPlaylist(epgUrl: String) {
|
fun deleteEpgPlaylist(epgUrl: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playlistRepository.deleteEpgPlaylistAndProgrammes(epgUrl)
|
playlistRepository.deleteEpgPlaylistAndProgrammes(epgUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
internal fun applyColor(
|
fun applyColor(
|
||||||
prev: ColorScheme?,
|
prev: ColorScheme?,
|
||||||
argb: Int,
|
argb: Int,
|
||||||
isDark: Boolean
|
isDark: Boolean
|
||||||
@ -347,24 +347,24 @@ class SettingViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun restoreSchemes() {
|
fun restoreSchemes() {
|
||||||
val schemes = ColorSchemeExample.schemes
|
val schemes = ColorSchemeExample.schemes
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
colorSchemeDao.insertAll(*schemes.toTypedArray())
|
colorSchemeDao.insertAll(*schemes.toTypedArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val versionName: String = publisher.versionName
|
val versionName: String = publisher.versionName
|
||||||
internal val versionCode: Int = publisher.versionCode
|
val versionCode: Int = publisher.versionCode
|
||||||
|
|
||||||
internal val titleState = mutableStateOf("")
|
val titleState = mutableStateOf("")
|
||||||
internal val urlState = mutableStateOf("")
|
val urlState = mutableStateOf("")
|
||||||
internal val uriState = mutableStateOf(Uri.EMPTY)
|
val uriState = mutableStateOf(Uri.EMPTY)
|
||||||
internal val localStorageState = mutableStateOf(false)
|
val localStorageState = mutableStateOf(false)
|
||||||
internal val forTvState = mutableStateOf(false)
|
val forTvState = mutableStateOf(false)
|
||||||
internal val basicUrlState = mutableStateOf("")
|
val basicUrlState = mutableStateOf("")
|
||||||
internal val usernameState = mutableStateOf("")
|
val usernameState = mutableStateOf("")
|
||||||
internal val passwordState = mutableStateOf("")
|
val passwordState = mutableStateOf("")
|
||||||
internal val epgState = mutableStateOf("")
|
val epgState = mutableStateOf("")
|
||||||
internal val selectedState: MutableState<DataSource> = mutableStateOf(DataSource.M3U)
|
val selectedState: MutableState<DataSource> = mutableStateOf(DataSource.M3U)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.google.material)
|
|
||||||
|
|
||||||
implementation(libs.androidx.media3.exoplayer)
|
implementation(libs.androidx.media3.exoplayer)
|
||||||
}
|
}
|
@ -13,7 +13,6 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.google.material)
|
|
||||||
|
|
||||||
implementation(libs.androidx.media3.exoplayer)
|
implementation(libs.androidx.media3.exoplayer)
|
||||||
implementation(libs.nextlib.media3.ext)
|
implementation(libs.nextlib.media3.ext)
|
||||||
|
14
core/src/main/java/com/m3u/core/wrapper/Sort.kt
Normal file
14
core/src/main/java/com/m3u/core/wrapper/Sort.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.m3u.core.wrapper
|
||||||
|
|
||||||
|
import com.m3u.i18n.R.string
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
enum class Sort(@StringRes val resId: Int) {
|
||||||
|
UNSPECIFIED(string.ui_sort_unspecified),
|
||||||
|
ASC(string.ui_sort_asc),
|
||||||
|
DESC(string.ui_sort_desc),
|
||||||
|
RECENTLY(string.ui_sort_recently),
|
||||||
|
MIXED(string.ui_sort_mixed)
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.m3u.data.repository.channel
|
package com.m3u.data.repository.channel
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
|
import com.m3u.core.wrapper.Sort
|
||||||
import com.m3u.data.database.model.AdjacentChannels
|
import com.m3u.data.database.model.AdjacentChannels
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@ -35,12 +36,4 @@ interface ChannelRepository {
|
|||||||
fun observeAllUnseenFavourites(limit: Duration): Flow<List<Channel>>
|
fun observeAllUnseenFavourites(limit: Duration): Flow<List<Channel>>
|
||||||
fun observeAllFavourite(): Flow<List<Channel>>
|
fun observeAllFavourite(): Flow<List<Channel>>
|
||||||
fun observeAllHidden(): Flow<List<Channel>>
|
fun observeAllHidden(): Flow<List<Channel>>
|
||||||
|
|
||||||
enum class Sort {
|
|
||||||
UNSPECIFIED,
|
|
||||||
ASC,
|
|
||||||
DESC,
|
|
||||||
RECENTLY,
|
|
||||||
MIXED
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import com.m3u.data.database.dao.PlaylistDao
|
|||||||
import com.m3u.data.database.model.AdjacentChannels
|
import com.m3u.data.database.model.AdjacentChannels
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.data.database.model.isSeries
|
import com.m3u.data.database.model.isSeries
|
||||||
import com.m3u.data.repository.channel.ChannelRepository.Sort
|
import com.m3u.core.wrapper.Sort
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
|
@ -55,7 +55,6 @@ ktor-server = "3.0.0-beta-1"
|
|||||||
mm2d-mmupnp = "3.1.6"
|
mm2d-mmupnp = "3.1.6"
|
||||||
symbolProcessingApi = "2.0.0-1.0.22"
|
symbolProcessingApi = "2.0.0-1.0.22"
|
||||||
profileinstaller = "1.4.1"
|
profileinstaller = "1.4.1"
|
||||||
tvFoundation = "1.0.0-alpha12"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
|
||||||
@ -123,6 +122,7 @@ androidx-tvprovider = { group = "androidx.tvprovider", name = "tvprovider", vers
|
|||||||
|
|
||||||
auto-service-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoServiceAnnotations" }
|
auto-service-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoServiceAnnotations" }
|
||||||
auto-service-ksp = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "autoServiceKsp" }
|
auto-service-ksp = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "autoServiceKsp" }
|
||||||
|
|
||||||
google-accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "google-accompanist" }
|
google-accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "google-accompanist" }
|
||||||
|
|
||||||
google-dagger-hilt = { group = "com.google.dagger", name = "hilt-android", version.ref = "google-dagger" }
|
google-dagger-hilt = { group = "com.google.dagger", name = "hilt-android", version.ref = "google-dagger" }
|
||||||
@ -166,14 +166,9 @@ slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j-api" }
|
|||||||
|
|
||||||
minabox = { group = "io.github.oleksandrbalan", name = "minabox", version.ref = "minabox" }
|
minabox = { group = "io.github.oleksandrbalan", name = "minabox", version.ref = "minabox" }
|
||||||
net-mm2d-mmupnp-mmupnp = { group = "net.mm2d.mmupnp", name = "mmupnp", version.ref = "mm2d-mmupnp" }
|
net-mm2d-mmupnp-mmupnp = { group = "net.mm2d.mmupnp", name = "mmupnp", version.ref = "mm2d-mmupnp" }
|
||||||
androidx-graphics-shapes-android = { group = "androidx.graphics", name = "graphics-shapes-android", version.ref = "androidx-graphics-shapes" }
|
androidx-graphics-shapes = { group = "androidx.graphics", name = "graphics-shapes-android", version.ref = "androidx-graphics-shapes" }
|
||||||
symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "symbolProcessingApi" }
|
symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "symbolProcessingApi" }
|
||||||
androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "profileinstaller" }
|
androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "profileinstaller" }
|
||||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
|
||||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
|
||||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
|
||||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
|
||||||
androidx-tv-foundation = { group = "androidx.tv", name = "tv-foundation", version.ref = "tvFoundation" }
|
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
com-android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
|
com-android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
|
||||||
|
3
material/.gitignore
vendored
3
material/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
/build
|
|
||||||
/src/test
|
|
||||||
/src/androidTest
|
|
@ -1,50 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.com.android.library)
|
|
||||||
alias(libs.plugins.org.jetbrains.kotlin.android)
|
|
||||||
alias(libs.plugins.compose.compiler)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "com.m3u.material"
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "17"
|
|
||||||
}
|
|
||||||
buildFeatures {
|
|
||||||
compose = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.androidx.core.ktx)
|
|
||||||
implementation(libs.androidx.activity.compose)
|
|
||||||
|
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
||||||
|
|
||||||
api(platform(libs.androidx.compose.bom))
|
|
||||||
api(libs.androidx.compose.foundation)
|
|
||||||
api(libs.androidx.compose.foundation.layout)
|
|
||||||
api(libs.androidx.compose.material.icons.extended)
|
|
||||||
api(libs.androidx.compose.material3)
|
|
||||||
api(libs.androidx.compose.runtime)
|
|
||||||
api(libs.androidx.compose.ui.util)
|
|
||||||
|
|
||||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
|
||||||
api(libs.androidx.compose.ui.tooling.preview)
|
|
||||||
debugApi(libs.androidx.compose.ui.tooling)
|
|
||||||
|
|
||||||
api(libs.androidx.compose.material3.window.size.clazz)
|
|
||||||
api(libs.androidx.constraintlayout.compose)
|
|
||||||
|
|
||||||
api(libs.androidx.navigation.compose)
|
|
||||||
|
|
||||||
api(libs.io.coil.kt)
|
|
||||||
api(libs.io.coil.kt.compose)
|
|
||||||
|
|
||||||
implementation(libs.airbnb.lottie.compose)
|
|
||||||
|
|
||||||
api(libs.androidx.graphics.shapes.android)
|
|
||||||
api(libs.google.material)
|
|
||||||
api(libs.haze)
|
|
||||||
|
|
||||||
api(libs.google.accompanist.permissions)
|
|
||||||
}
|
|
21
material/proguard-rules.pro
vendored
21
material/proguard-rules.pro
vendored
@ -1,21 +0,0 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.kts.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest>
|
|
||||||
|
|
||||||
</manifest>
|
|
@ -1,3 +0,0 @@
|
|||||||
package com.m3u.material.components.mask
|
|
||||||
|
|
||||||
typealias MaskInterceptor = (Boolean) -> Boolean
|
|
@ -1,6 +0,0 @@
|
|||||||
package com.m3u.material.model
|
|
||||||
|
|
||||||
import androidx.compose.runtime.staticCompositionLocalOf
|
|
||||||
import dev.chrisbanes.haze.HazeState
|
|
||||||
|
|
||||||
val LocalHazeState = staticCompositionLocalOf { HazeState() }
|
|
@ -18,16 +18,13 @@ rootProject.name = "M3U"
|
|||||||
include(":smartphone", ":tv")
|
include(":smartphone", ":tv")
|
||||||
include(":core")
|
include(":core")
|
||||||
include(":data")
|
include(":data")
|
||||||
include(":material")
|
|
||||||
include(":ui")
|
|
||||||
include(
|
include(
|
||||||
":business:foryou",
|
":business:foryou",
|
||||||
":business:favorite",
|
":business:favorite",
|
||||||
":business:setting",
|
":business:setting",
|
||||||
":business:playlist",
|
":business:playlist",
|
||||||
":business:playlist-configuration",
|
":business:playlist-configuration",
|
||||||
":business:channel",
|
":business:channel"
|
||||||
":business:crash"
|
|
||||||
)
|
)
|
||||||
include(":baselineprofile")
|
include(":baselineprofile")
|
||||||
include(":i18n")
|
include(":i18n")
|
||||||
|
@ -10,6 +10,7 @@ plugins {
|
|||||||
alias(libs.plugins.androidx.baselineprofile)
|
alias(libs.plugins.androidx.baselineprofile)
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.m3u.smartphone"
|
namespace = "com.m3u.smartphone"
|
||||||
compileSdk = 35
|
compileSdk = 35
|
||||||
@ -120,38 +121,68 @@ baselineProfile {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":core"))
|
implementation(project(":core"))
|
||||||
implementation(project(":ui"))
|
implementation(project(":data"))
|
||||||
|
// business
|
||||||
implementation(project(":business:foryou"))
|
implementation(project(":business:foryou"))
|
||||||
implementation(project(":business:favorite"))
|
implementation(project(":business:favorite"))
|
||||||
implementation(project(":business:setting"))
|
implementation(project(":business:setting"))
|
||||||
implementation(project(":business:playlist"))
|
implementation(project(":business:playlist"))
|
||||||
implementation(project(":business:channel"))
|
implementation(project(":business:channel"))
|
||||||
implementation(project(":business:playlist-configuration"))
|
implementation(project(":business:playlist-configuration"))
|
||||||
implementation(project(":business:crash"))
|
// baselineprofile
|
||||||
implementation(libs.androidx.profileinstaller)
|
implementation(libs.androidx.profileinstaller)
|
||||||
"baselineProfile"(project(":baselineprofile"))
|
"baselineProfile"(project(":baselineprofile"))
|
||||||
|
// base
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(libs.androidx.startup.runtime)
|
implementation(libs.androidx.startup.runtime)
|
||||||
|
implementation(libs.androidx.core.splashscreen)
|
||||||
|
implementation(libs.google.material)
|
||||||
|
// lifecycle
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||||
implementation(libs.androidx.lifecycle.process)
|
implementation(libs.androidx.lifecycle.process)
|
||||||
|
// work
|
||||||
implementation(libs.androidx.core.splashscreen)
|
implementation(libs.androidx.work.runtime.ktx)
|
||||||
|
// dagger
|
||||||
implementation(libs.google.dagger.hilt)
|
implementation(libs.google.dagger.hilt)
|
||||||
ksp(libs.google.dagger.hilt.compiler)
|
ksp(libs.google.dagger.hilt.compiler)
|
||||||
implementation(libs.androidx.hilt.navigation.compose)
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
|
|
||||||
implementation(libs.androidx.work.runtime.ktx)
|
|
||||||
ksp(libs.androidx.hilt.compiler)
|
ksp(libs.androidx.hilt.compiler)
|
||||||
implementation(libs.androidx.hilt.work)
|
implementation(libs.androidx.hilt.work)
|
||||||
|
// compose
|
||||||
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
|
implementation(libs.androidx.compose.foundation)
|
||||||
|
implementation(libs.androidx.compose.foundation.layout)
|
||||||
|
implementation(libs.androidx.compose.material.icons.extended)
|
||||||
|
implementation(libs.androidx.compose.runtime)
|
||||||
|
implementation(libs.androidx.compose.ui.util)
|
||||||
|
implementation(libs.androidx.navigation.compose)
|
||||||
|
// compose-material3
|
||||||
|
implementation(libs.androidx.compose.material3)
|
||||||
|
implementation(libs.androidx.compose.material3.window.size.clazz)
|
||||||
|
implementation(libs.androidx.compose.material3.adaptive)
|
||||||
|
implementation(libs.androidx.compose.material3.adaptive.navigation)
|
||||||
|
implementation(libs.androidx.compose.material3.adaptive.layout)
|
||||||
|
// glance
|
||||||
implementation(libs.androidx.glance.appwidget)
|
implementation(libs.androidx.glance.appwidget)
|
||||||
implementation(libs.androidx.glance.material3)
|
implementation(libs.androidx.glance.material3)
|
||||||
|
// accompanist
|
||||||
|
implementation(libs.google.accompanist.permissions)
|
||||||
|
// performance
|
||||||
debugImplementation(libs.squareup.leakcanary)
|
debugImplementation(libs.squareup.leakcanary)
|
||||||
|
// other
|
||||||
|
implementation(libs.androidx.graphics.shapes)
|
||||||
|
implementation(libs.androidx.constraintlayout.compose)
|
||||||
|
implementation(libs.io.coil.kt)
|
||||||
|
implementation(libs.io.coil.kt.compose)
|
||||||
|
implementation(libs.androidx.media3.ui)
|
||||||
|
implementation(libs.androidx.media3.exoplayer)
|
||||||
|
implementation(libs.airbnb.lottie.compose)
|
||||||
|
implementation(libs.minabox)
|
||||||
|
implementation(libs.net.mm2d.mmupnp.mmupnp)
|
||||||
|
implementation(libs.haze)
|
||||||
|
// FIXME: for m2 BackdropScaffold only
|
||||||
|
implementation("androidx.compose.material:material")
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import androidx.hilt.work.HiltWorkerFactory
|
|||||||
import androidx.work.Configuration
|
import androidx.work.Configuration
|
||||||
import com.m3u.core.architecture.logger.Logger
|
import com.m3u.core.architecture.logger.Logger
|
||||||
import com.m3u.core.architecture.preferences.Preferences
|
import com.m3u.core.architecture.preferences.Preferences
|
||||||
import com.m3u.business.crash.CrashHandler
|
import com.m3u.smartphone.ui.business.crash.CrashHandler
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import com.m3u.smartphone.ui.App
|
import com.m3u.smartphone.ui.App
|
||||||
import com.m3u.smartphone.ui.AppViewModel
|
import com.m3u.smartphone.ui.AppViewModel
|
||||||
import com.m3u.ui.Events.enableDPadReaction
|
import com.m3u.smartphone.ui.common.helper.Helper
|
||||||
import com.m3u.ui.Toolkit
|
import com.m3u.smartphone.ui.common.internal.Events.enableDPadReaction
|
||||||
import com.m3u.ui.helper.Helper
|
import com.m3u.smartphone.ui.common.internal.Toolkit
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.ui.util
|
package com.m3u.smartphone
|
||||||
|
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
|
@ -41,7 +41,7 @@ import com.m3u.smartphone.R
|
|||||||
import com.m3u.core.Contracts
|
import com.m3u.core.Contracts
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.data.database.model.Programme
|
import com.m3u.data.database.model.Programme
|
||||||
import com.m3u.ui.util.TimeUtils.formatEOrSh
|
import com.m3u.smartphone.TimeUtils.formatEOrSh
|
||||||
import dagger.hilt.android.EntryPointAccessors
|
import dagger.hilt.android.EntryPointAccessors
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
import kotlinx.datetime.TimeZone
|
import kotlinx.datetime.TimeZone
|
||||||
|
@ -35,12 +35,12 @@ import com.m3u.smartphone.ui.common.connect.RemoteControlSheetValue
|
|||||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||||
import com.m3u.data.tv.model.RemoteDirection
|
import com.m3u.data.tv.model.RemoteDirection
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.smartphone.ui.common.AppNavHost
|
import com.m3u.smartphone.ui.common.AppNavHost
|
||||||
import com.m3u.smartphone.ui.common.Scaffold
|
import com.m3u.smartphone.ui.common.Scaffold
|
||||||
import com.m3u.ui.Destination
|
import com.m3u.smartphone.ui.material.components.Destination
|
||||||
import com.m3u.ui.FontFamilies
|
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||||
import com.m3u.ui.SnackHost
|
import com.m3u.smartphone.ui.material.components.SnackHost
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun App(
|
fun App(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel
|
package com.m3u.smartphone.ui.business.channel
|
||||||
|
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||||
@ -71,25 +71,23 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
|
import com.m3u.business.channel.PlayerState
|
||||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||||
import com.m3u.core.util.basic.isNotEmpty
|
import com.m3u.core.util.basic.isNotEmpty
|
||||||
import com.m3u.data.database.model.AdjacentChannels
|
import com.m3u.data.database.model.AdjacentChannels
|
||||||
import com.m3u.business.channel.MaskCenterRole.Pause
|
|
||||||
import com.m3u.business.channel.MaskCenterRole.Play
|
|
||||||
import com.m3u.business.channel.MaskCenterRole.Replay
|
|
||||||
import com.m3u.business.channel.components.MaskTextButton
|
|
||||||
import com.m3u.business.channel.components.PlayerMask
|
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.components.mask.MaskButton
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.components.mask.MaskCircleButton
|
import com.m3u.smartphone.ui.business.channel.components.MaskTextButton
|
||||||
import com.m3u.material.components.mask.MaskPanel
|
import com.m3u.smartphone.ui.business.channel.components.PlayerMask
|
||||||
import com.m3u.material.components.mask.MaskState
|
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||||
import com.m3u.material.effects.currentBackStackEntry
|
import com.m3u.smartphone.ui.material.components.Image
|
||||||
import com.m3u.material.ktx.thenIf
|
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.components.mask.MaskButton
|
||||||
import com.m3u.ui.FontFamilies
|
import com.m3u.smartphone.ui.material.components.mask.MaskCircleButton
|
||||||
import com.m3u.ui.Image
|
import com.m3u.smartphone.ui.material.components.mask.MaskPanel
|
||||||
import com.m3u.ui.helper.LocalHelper
|
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||||
|
import com.m3u.smartphone.ui.material.effects.currentBackStackEntry
|
||||||
|
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
@ -164,10 +162,11 @@ internal fun ChannelMask(
|
|||||||
playerState.playState
|
playerState.playState
|
||||||
) {
|
) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
|
val currentPlayer = playerState.player
|
||||||
when {
|
when {
|
||||||
playerState.player == null -> false
|
currentPlayer == null -> false
|
||||||
!playerState.player.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM) -> false
|
!currentPlayer.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM) -> false
|
||||||
else -> with(playerState.player) {
|
else -> with(currentPlayer) {
|
||||||
!isCurrentMediaItemDynamic && isCurrentMediaItemSeekable
|
!isCurrentMediaItemDynamic && isCurrentMediaItemSeekable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,8 +178,7 @@ internal fun ChannelMask(
|
|||||||
) {
|
) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
playerState.player
|
playerState.player
|
||||||
?.isCommandAvailable(Player.COMMAND_SET_SPEED_AND_PITCH)
|
?.isCommandAvailable(Player.COMMAND_SET_SPEED_AND_PITCH) == true
|
||||||
?: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,14 +553,14 @@ private fun MaskCenterButton(
|
|||||||
scaleY = scale
|
scaleY = scale
|
||||||
},
|
},
|
||||||
icon = when (centerRole) {
|
icon = when (centerRole) {
|
||||||
Play -> Icons.Rounded.PlayArrow
|
MaskCenterRole.Play -> Icons.Rounded.PlayArrow
|
||||||
Pause -> Icons.Rounded.Pause
|
MaskCenterRole.Pause -> Icons.Rounded.Pause
|
||||||
else -> Icons.Rounded.Refresh
|
else -> Icons.Rounded.Refresh
|
||||||
},
|
},
|
||||||
onClick = when (centerRole) {
|
onClick = when (centerRole) {
|
||||||
Replay -> onRetry
|
MaskCenterRole.Replay -> onRetry
|
||||||
Play -> onPlay
|
MaskCenterRole.Play -> onPlay
|
||||||
Pause -> onPause
|
MaskCenterRole.Pause -> onPause
|
||||||
else -> {
|
else -> {
|
||||||
{}
|
{}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel
|
package com.m3u.smartphone.ui.business.channel
|
||||||
|
|
||||||
import android.database.ContentObserver
|
import android.database.ContentObserver
|
||||||
import android.os.Handler
|
import android.os.Handler
|
@ -1,9 +1,8 @@
|
|||||||
package com.m3u.business.channel
|
package com.m3u.smartphone.ui.business.channel
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.net.Uri
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@ -42,31 +41,33 @@ import com.m3u.core.util.basic.title
|
|||||||
import com.m3u.data.database.model.AdjacentChannels
|
import com.m3u.data.database.model.AdjacentChannels
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.data.database.model.Playlist
|
import com.m3u.data.database.model.Playlist
|
||||||
import com.m3u.business.channel.components.CoverPlaceholder
|
|
||||||
import com.m3u.business.channel.components.DlnaDevicesBottomSheet
|
|
||||||
import com.m3u.business.channel.components.FormatsBottomSheet
|
|
||||||
import com.m3u.business.channel.components.MaskGestureValuePanel
|
|
||||||
import com.m3u.business.channel.components.PlayerPanel
|
|
||||||
import com.m3u.business.channel.components.VerticalGestureArea
|
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.components.Background
|
import com.m3u.smartphone.ui.material.components.Player
|
||||||
import com.m3u.material.components.PullPanelLayout
|
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||||
import com.m3u.material.components.PullPanelLayoutValue
|
import com.m3u.smartphone.ui.material.components.rememberPlayerState
|
||||||
import com.m3u.material.components.mask.MaskInterceptor
|
|
||||||
import com.m3u.material.components.mask.MaskState
|
|
||||||
import com.m3u.material.components.mask.rememberMaskState
|
|
||||||
import com.m3u.material.components.mask.toggle
|
|
||||||
import com.m3u.material.components.rememberPullPanelLayoutState
|
|
||||||
import com.m3u.material.ktx.checkPermissionOrRationale
|
|
||||||
import com.m3u.ui.Player
|
|
||||||
import com.m3u.ui.helper.LocalHelper
|
|
||||||
import com.m3u.ui.helper.OnPipModeChanged
|
|
||||||
import com.m3u.ui.rememberPlayerState
|
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import com.m3u.business.channel.ChannelViewModel
|
||||||
|
import com.m3u.business.channel.PlayerState
|
||||||
|
import com.m3u.smartphone.ui.business.channel.components.CoverPlaceholder
|
||||||
|
import com.m3u.smartphone.ui.business.channel.components.DlnaDevicesBottomSheet
|
||||||
|
import com.m3u.smartphone.ui.business.channel.components.FormatsBottomSheet
|
||||||
|
import com.m3u.smartphone.ui.business.channel.components.MaskGestureValuePanel
|
||||||
|
import com.m3u.smartphone.ui.business.channel.components.PlayerPanel
|
||||||
|
import com.m3u.smartphone.ui.business.channel.components.VerticalGestureArea
|
||||||
|
import com.m3u.smartphone.ui.common.helper.OnPipModeChanged
|
||||||
|
import com.m3u.smartphone.ui.material.components.Background
|
||||||
|
import com.m3u.smartphone.ui.material.components.PullPanelLayout
|
||||||
|
import com.m3u.smartphone.ui.material.components.PullPanelLayoutValue
|
||||||
|
import com.m3u.smartphone.ui.material.components.mask.MaskInterceptor
|
||||||
|
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||||
|
import com.m3u.smartphone.ui.material.components.mask.rememberMaskState
|
||||||
|
import com.m3u.smartphone.ui.material.components.mask.toggle
|
||||||
|
import com.m3u.smartphone.ui.material.components.rememberPullPanelLayoutState
|
||||||
|
import com.m3u.smartphone.ui.material.ktx.checkPermissionOrRationale
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChannelRoute(
|
fun ChannelRoute(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel
|
package com.m3u.smartphone.ui.business.channel
|
||||||
|
|
||||||
internal enum class MaskGesture {
|
internal enum class MaskGesture {
|
||||||
VOLUME, BRIGHTNESS, SPEED
|
VOLUME, BRIGHTNESS, SPEED
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel
|
package com.m3u.smartphone.ui.business.channel
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
@ -9,14 +9,15 @@ import androidx.activity.enableEdgeToEdge
|
|||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.m3u.business.channel.ChannelViewModel
|
||||||
import com.m3u.core.Contracts
|
import com.m3u.core.Contracts
|
||||||
import com.m3u.data.database.model.isSeries
|
import com.m3u.data.database.model.isSeries
|
||||||
import com.m3u.data.repository.playlist.PlaylistRepository
|
import com.m3u.data.repository.playlist.PlaylistRepository
|
||||||
import com.m3u.data.repository.channel.ChannelRepository
|
import com.m3u.data.repository.channel.ChannelRepository
|
||||||
import com.m3u.data.service.MediaCommand
|
import com.m3u.data.service.MediaCommand
|
||||||
import com.m3u.ui.Events.enableDPadReaction
|
import com.m3u.smartphone.ui.common.helper.Helper
|
||||||
import com.m3u.ui.Toolkit
|
import com.m3u.smartphone.ui.common.internal.Events.enableDPadReaction
|
||||||
import com.m3u.ui.helper.Helper
|
import com.m3u.smartphone.ui.common.internal.Toolkit
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -72,7 +73,7 @@ class PlayerActivity : ComponentActivity() {
|
|||||||
val playlist = playlistRepository.get(channel.playlistUrl)
|
val playlist = playlistRepository.get(channel.playlistUrl)
|
||||||
when {
|
when {
|
||||||
// series can not be played from shortcuts
|
// series can not be played from shortcuts
|
||||||
playlist?.isSeries ?: false -> {}
|
playlist?.isSeries == true -> {}
|
||||||
else -> {
|
else -> {
|
||||||
helper.play(MediaCommand.Common(channel.id))
|
helper.play(MediaCommand.Common(channel.id))
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
@ -13,9 +13,9 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.m3u.material.model.LocalDuration
|
import com.m3u.smartphone.ui.material.model.LocalDuration
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.ui.Image
|
import com.m3u.smartphone.ui.material.components.Image
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun CoverPlaceholder(
|
internal fun CoverPlaceholder(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@ -27,12 +27,12 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.components.CircularProgressIndicator
|
import com.m3u.smartphone.ui.material.components.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.components.mask.MaskState
|
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.ui.UnstableBadge
|
import com.m3u.smartphone.ui.material.components.UnstableBadge
|
||||||
import com.m3u.ui.UnstableValue
|
import com.m3u.smartphone.ui.material.components.UnstableValue
|
||||||
import net.mm2d.upnp.Device
|
import net.mm2d.upnp.Device
|
||||||
|
|
||||||
@Composable
|
@Composable
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@ -36,8 +36,8 @@ import androidx.media3.common.C
|
|||||||
import androidx.media3.common.Format
|
import androidx.media3.common.Format
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.components.mask.MaskState
|
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@ -17,8 +17,8 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.ui.MonoText
|
import com.m3u.smartphone.ui.material.components.MonoText
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MaskGestureValuePanel(
|
internal fun MaskGestureValuePanel(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@ -16,8 +16,8 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import com.m3u.material.components.mask.MaskState
|
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||||
import com.m3u.ui.FontFamilies
|
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MaskTextButton(
|
fun MaskTextButton(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
@ -20,9 +20,9 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.unit.coerceAtLeast
|
import androidx.compose.ui.unit.coerceAtLeast
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.m3u.material.components.mask.Mask
|
import com.m3u.smartphone.ui.material.components.mask.Mask
|
||||||
import com.m3u.material.components.mask.MaskState
|
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun PlayerMask(
|
internal fun PlayerMask(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
@ -64,20 +64,20 @@ import com.m3u.data.database.model.Episode
|
|||||||
import com.m3u.data.database.model.Programme
|
import com.m3u.data.database.model.Programme
|
||||||
import com.m3u.data.database.model.ProgrammeRange
|
import com.m3u.data.database.model.ProgrammeRange
|
||||||
import com.m3u.data.service.MediaCommand
|
import com.m3u.data.service.MediaCommand
|
||||||
import com.m3u.material.components.Background
|
import com.m3u.smartphone.ui.material.components.Background
|
||||||
import com.m3u.material.components.CircularProgressIndicator
|
import com.m3u.smartphone.ui.material.components.CircularProgressIndicator
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import com.m3u.material.effects.BackStackEntry
|
import com.m3u.smartphone.ui.material.effects.BackStackEntry
|
||||||
import com.m3u.material.effects.BackStackHandler
|
import com.m3u.smartphone.ui.material.effects.BackStackHandler
|
||||||
import com.m3u.material.ktx.Edge
|
import com.m3u.smartphone.ui.material.ktx.Edge
|
||||||
import com.m3u.material.ktx.blurEdges
|
import com.m3u.smartphone.ui.material.ktx.blurEdges
|
||||||
import com.m3u.material.ktx.thenIf
|
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||||
import com.m3u.ui.FontFamilies
|
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||||
import com.m3u.ui.helper.LocalHelper
|
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||||
import com.m3u.ui.util.TimeUtils.formatEOrSh
|
import com.m3u.smartphone.TimeUtils.formatEOrSh
|
||||||
import com.m3u.ui.util.TimeUtils.toEOrSh
|
import com.m3u.smartphone.TimeUtils.toEOrSh
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.animation.core.Spring
|
import androidx.compose.animation.core.Spring
|
||||||
@ -63,12 +63,12 @@ import com.m3u.data.database.model.Programme
|
|||||||
import com.m3u.data.database.model.ProgrammeRange
|
import com.m3u.data.database.model.ProgrammeRange
|
||||||
import com.m3u.data.database.model.ProgrammeRange.Companion.HOUR_LENGTH
|
import com.m3u.data.database.model.ProgrammeRange.Companion.HOUR_LENGTH
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.ktx.Edge
|
import com.m3u.smartphone.ui.material.ktx.Edge
|
||||||
import com.m3u.material.ktx.blurEdges
|
import com.m3u.smartphone.ui.material.ktx.blurEdges
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.ui.FontFamilies
|
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||||
import com.m3u.ui.util.TimeUtils.formatEOrSh
|
import com.m3u.smartphone.TimeUtils.formatEOrSh
|
||||||
import com.m3u.ui.util.TimeUtils.toEOrSh
|
import com.m3u.smartphone.TimeUtils.toEOrSh
|
||||||
import eu.wewox.minabox.MinaBox
|
import eu.wewox.minabox.MinaBox
|
||||||
import eu.wewox.minabox.MinaBoxItem
|
import eu.wewox.minabox.MinaBoxItem
|
||||||
import eu.wewox.minabox.MinaBoxScrollDirection
|
import eu.wewox.minabox.MinaBoxScrollDirection
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.channel.components
|
package com.m3u.smartphone.ui.business.channel.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
@ -10,8 +10,8 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.m3u.business.channel.ChannelMaskUtils.detectVerticalGesture
|
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||||
import com.m3u.material.ktx.thenIf
|
import com.m3u.smartphone.ui.business.channel.ChannelMaskUtils.detectVerticalGesture
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun VerticalGestureArea(
|
internal fun VerticalGestureArea(
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.m3u.smartphone.ui.business.configuration
|
||||||
|
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavType
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.navArgument
|
||||||
|
import com.m3u.business.playlist.configuration.PlaylistConfigurationNavigation
|
||||||
|
|
||||||
|
fun NavGraphBuilder.playlistConfigurationScreen(
|
||||||
|
contentPadding: PaddingValues = PaddingValues(),
|
||||||
|
) {
|
||||||
|
composable(
|
||||||
|
route = PlaylistConfigurationNavigation.PLAYLIST_CONFIGURATION_ROUTE,
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument(PlaylistConfigurationNavigation.TYPE_PLAYLIST_URL) {
|
||||||
|
type = NavType.StringType
|
||||||
|
}
|
||||||
|
),
|
||||||
|
enterTransition = { slideInVertically { it } },
|
||||||
|
exitTransition = { fadeOut() },
|
||||||
|
popEnterTransition = { fadeIn() },
|
||||||
|
popExitTransition = { slideOutVertically { it } }
|
||||||
|
) {
|
||||||
|
PlaylistConfigurationRoute(
|
||||||
|
contentPadding = contentPadding
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.playlist.configuration
|
package com.m3u.smartphone.ui.business.configuration
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -26,6 +26,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Save
|
import androidx.compose.material.icons.rounded.Save
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
@ -43,6 +44,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.google.accompanist.permissions.rememberPermissionState
|
import com.google.accompanist.permissions.rememberPermissionState
|
||||||
|
import com.m3u.business.playlist.configuration.EpgManifest
|
||||||
|
import com.m3u.business.playlist.configuration.PlaylistConfigurationViewModel
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.core.wrapper.Resource
|
import com.m3u.core.wrapper.Resource
|
||||||
import com.m3u.data.database.model.DataSource
|
import com.m3u.data.database.model.DataSource
|
||||||
@ -50,19 +53,18 @@ import com.m3u.data.database.model.Playlist
|
|||||||
import com.m3u.data.database.model.epgUrlsOrXtreamXmlUrl
|
import com.m3u.data.database.model.epgUrlsOrXtreamXmlUrl
|
||||||
import com.m3u.data.parser.xtream.XtreamInfo
|
import com.m3u.data.parser.xtream.XtreamInfo
|
||||||
import com.m3u.data.repository.playlist.PlaylistRepository
|
import com.m3u.data.repository.playlist.PlaylistRepository
|
||||||
import com.m3u.business.playlist.configuration.components.AutoSyncProgrammesButton
|
|
||||||
import com.m3u.business.playlist.configuration.components.EpgManifestGallery
|
|
||||||
import com.m3u.business.playlist.configuration.components.SyncProgrammesButton
|
|
||||||
import com.m3u.business.playlist.configuration.components.XtreamPanel
|
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.components.Background
|
import com.m3u.smartphone.ui.material.components.Background
|
||||||
import androidx.compose.material3.Icon
|
import com.m3u.smartphone.ui.material.components.PlaceholderField
|
||||||
import com.m3u.material.components.PlaceholderField
|
import com.m3u.smartphone.ui.material.ktx.checkPermissionOrRationale
|
||||||
import com.m3u.material.ktx.checkPermissionOrRationale
|
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||||
import com.m3u.material.model.LocalHazeState
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.business.configuration.components.AutoSyncProgrammesButton
|
||||||
import com.m3u.ui.helper.LocalHelper
|
import com.m3u.smartphone.ui.business.configuration.components.EpgManifestGallery
|
||||||
import com.m3u.ui.helper.Metadata
|
import com.m3u.smartphone.ui.business.configuration.components.SyncProgrammesButton
|
||||||
|
import com.m3u.smartphone.ui.business.configuration.components.XtreamPanel
|
||||||
|
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||||
|
import com.m3u.smartphone.ui.common.helper.Metadata
|
||||||
import dev.chrisbanes.haze.HazeStyle
|
import dev.chrisbanes.haze.HazeStyle
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.haze
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.playlist.configuration.components
|
package com.m3u.smartphone.ui.business.configuration.components
|
||||||
|
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@ -15,9 +15,9 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.i18n.R
|
import com.m3u.i18n.R
|
||||||
import com.m3u.material.components.SelectionsDefaults
|
import com.m3u.smartphone.ui.material.components.SelectionsDefaults
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun AutoSyncProgrammesButton(
|
internal fun AutoSyncProgrammesButton(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.playlist.configuration.components
|
package com.m3u.smartphone.ui.business.configuration.components
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
@ -21,14 +21,14 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.m3u.business.playlist.configuration.EpgManifest
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.data.database.model.Playlist
|
import com.m3u.data.database.model.Playlist
|
||||||
import com.m3u.data.repository.playlist.PlaylistRepository
|
import com.m3u.data.repository.playlist.PlaylistRepository
|
||||||
import com.m3u.business.playlist.configuration.EpgManifest
|
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.components.SelectionsDefaults
|
import com.m3u.smartphone.ui.material.components.SelectionsDefaults
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||||
|
|
||||||
internal fun LazyListScope.EpgManifestGallery(
|
internal fun LazyListScope.EpgManifestGallery(
|
||||||
playlistUrl: String,
|
playlistUrl: String,
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.playlist.configuration.components
|
package com.m3u.smartphone.ui.business.configuration.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
@ -22,10 +22,10 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.components.CircularProgressIndicator
|
import com.m3u.smartphone.ui.material.components.CircularProgressIndicator
|
||||||
import com.m3u.material.components.SelectionsDefaults
|
import com.m3u.smartphone.ui.material.components.SelectionsDefaults
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
|
||||||
@Composable
|
@Composable
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.playlist.configuration.components
|
package com.m3u.smartphone.ui.business.configuration.components
|
||||||
|
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@ -23,10 +23,10 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.m3u.core.wrapper.Resource
|
import com.m3u.core.wrapper.Resource
|
||||||
import com.m3u.data.parser.xtream.XtreamInfo
|
import com.m3u.data.parser.xtream.XtreamInfo
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.ui.Badge
|
import com.m3u.smartphone.ui.material.components.Badge
|
||||||
import com.m3u.ui.FontFamilies
|
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||||
import com.m3u.ui.TextBadge
|
import com.m3u.smartphone.ui.material.components.TextBadge
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
|
|
||||||
@Composable
|
@Composable
|
@ -1,13 +1,13 @@
|
|||||||
package com.m3u.business.crash
|
package com.m3u.smartphone.ui.business.crash
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import com.m3u.ui.Events.enableDPadReaction
|
import com.m3u.smartphone.ui.common.helper.Helper
|
||||||
import com.m3u.ui.Toolkit
|
import com.m3u.smartphone.ui.common.internal.Events.enableDPadReaction
|
||||||
import com.m3u.ui.helper.Helper
|
import com.m3u.smartphone.ui.common.internal.Toolkit
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.crash
|
package com.m3u.smartphone.ui.business.crash
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -10,9 +10,9 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.m3u.business.crash.navigation.Destination
|
import com.m3u.smartphone.ui.business.crash.navigation.Destination
|
||||||
import com.m3u.business.crash.screen.detail.DetailScreen
|
import com.m3u.smartphone.ui.business.crash.screen.detail.DetailScreen
|
||||||
import com.m3u.business.crash.screen.list.ListScreen
|
import com.m3u.smartphone.ui.business.crash.screen.list.ListScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun CrashApp() {
|
internal fun CrashApp() {
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.crash
|
package com.m3u.smartphone.ui.business.crash
|
||||||
|
|
||||||
import android.app.AlarmManager
|
import android.app.AlarmManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.crash.components
|
package com.m3u.smartphone.ui.business.crash.components
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Adb
|
import androidx.compose.material.icons.rounded.Adb
|
||||||
@ -9,7 +9,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import com.m3u.material.components.Background
|
import com.m3u.smartphone.ui.material.components.Background
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@Composable
|
@Composable
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.crash.navigation
|
package com.m3u.smartphone.ui.business.crash.navigation
|
||||||
|
|
||||||
internal sealed class Destination {
|
internal sealed class Destination {
|
||||||
data object List : Destination()
|
data object List : Destination()
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.crash.screen.detail
|
package com.m3u.smartphone.ui.business.crash.screen.detail
|
||||||
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
@ -18,10 +18,10 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.m3u.material.components.Background
|
import com.m3u.smartphone.ui.material.components.Background
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.ui.MonoText
|
import com.m3u.smartphone.ui.material.components.MonoText
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun DetailScreen(
|
internal fun DetailScreen(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.crash.screen.detail
|
package com.m3u.smartphone.ui.business.crash.screen.detail
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.crash.screen.list
|
package com.m3u.smartphone.ui.business.crash.screen.list
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@ -7,9 +7,9 @@ import androidx.compose.foundation.lazy.items
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.m3u.business.crash.components.FileItem
|
import com.m3u.smartphone.ui.material.components.Background
|
||||||
import com.m3u.business.crash.screen.list.navigation.NavigateToDetail
|
import com.m3u.smartphone.ui.business.crash.components.FileItem
|
||||||
import com.m3u.material.components.Background
|
import com.m3u.smartphone.ui.business.crash.screen.list.navigation.NavigateToDetail
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ListScreen(
|
internal fun ListScreen(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.crash.screen.list
|
package com.m3u.smartphone.ui.business.crash.screen.list
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.m3u.core.architecture.FileProvider
|
import com.m3u.core.architecture.FileProvider
|
@ -0,0 +1,3 @@
|
|||||||
|
package com.m3u.smartphone.ui.business.crash.screen.list.navigation
|
||||||
|
|
||||||
|
typealias NavigateToDetail = (path: String) -> Unit
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.favorite
|
package com.m3u.smartphone.ui.business.favorite
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
@ -24,25 +24,26 @@ import androidx.compose.ui.text.AnnotatedString
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.m3u.business.favorite.FavouriteViewModel
|
||||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.core.wrapper.Resource
|
import com.m3u.core.wrapper.Resource
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.data.database.model.isSeries
|
import com.m3u.data.database.model.isSeries
|
||||||
import com.m3u.data.service.MediaCommand
|
import com.m3u.data.service.MediaCommand
|
||||||
import com.m3u.business.favorite.components.FavouriteGallery
|
import com.m3u.core.wrapper.Sort
|
||||||
import com.m3u.i18n.R
|
import com.m3u.i18n.R
|
||||||
import com.m3u.material.ktx.interceptVolumeEvent
|
import com.m3u.smartphone.ui.material.ktx.interceptVolumeEvent
|
||||||
import com.m3u.material.ktx.thenIf
|
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||||
import com.m3u.material.model.LocalHazeState
|
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||||
import com.m3u.ui.EpisodesBottomSheet
|
import com.m3u.smartphone.ui.business.favorite.components.FavouriteGallery
|
||||||
import com.m3u.ui.MediaSheet
|
import com.m3u.smartphone.ui.material.components.EpisodesBottomSheet
|
||||||
import com.m3u.ui.MediaSheetValue
|
import com.m3u.smartphone.ui.material.components.MediaSheet
|
||||||
import com.m3u.ui.Sort
|
import com.m3u.smartphone.ui.material.components.MediaSheetValue
|
||||||
import com.m3u.ui.SortBottomSheet
|
import com.m3u.smartphone.ui.material.components.SortBottomSheet
|
||||||
import com.m3u.ui.helper.Action
|
import com.m3u.smartphone.ui.common.helper.Action
|
||||||
import com.m3u.ui.helper.LocalHelper
|
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||||
import com.m3u.ui.helper.Metadata
|
import com.m3u.smartphone.ui.common.helper.Metadata
|
||||||
import dev.chrisbanes.haze.HazeDefaults
|
import dev.chrisbanes.haze.HazeDefaults
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.haze
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.favorite.components
|
package com.m3u.smartphone.ui.business.favorite.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -18,9 +18,9 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.m3u.core.wrapper.Resource
|
import com.m3u.core.wrapper.Resource
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.material.components.VerticalDraggableScrollbar
|
import com.m3u.smartphone.ui.material.components.VerticalDraggableScrollbar
|
||||||
import com.m3u.material.ktx.plus
|
import com.m3u.smartphone.ui.material.ktx.plus
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun FavouriteGallery(
|
internal fun FavouriteGallery(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.favorite.components
|
package com.m3u.smartphone.ui.business.favorite.components
|
||||||
|
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
@ -18,8 +18,8 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.foryou
|
package com.m3u.smartphone.ui.business.foryou
|
||||||
|
|
||||||
import android.content.res.Configuration.ORIENTATION_PORTRAIT
|
import android.content.res.Configuration.ORIENTATION_PORTRAIT
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
@ -31,6 +31,8 @@ import androidx.lifecycle.compose.LifecycleResumeEffect
|
|||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import com.m3u.business.foryou.ForyouViewModel
|
||||||
|
import com.m3u.business.foryou.Recommend
|
||||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.core.wrapper.Resource
|
import com.m3u.core.wrapper.Resource
|
||||||
@ -39,20 +41,19 @@ import com.m3u.data.database.model.Playlist
|
|||||||
import com.m3u.data.database.model.PlaylistWithCount
|
import com.m3u.data.database.model.PlaylistWithCount
|
||||||
import com.m3u.data.database.model.isSeries
|
import com.m3u.data.database.model.isSeries
|
||||||
import com.m3u.data.service.MediaCommand
|
import com.m3u.data.service.MediaCommand
|
||||||
import com.m3u.business.foryou.components.HeadlineBackground
|
|
||||||
import com.m3u.business.foryou.components.PlaylistGallery
|
|
||||||
import com.m3u.business.foryou.components.recommend.Recommend
|
|
||||||
import com.m3u.business.foryou.components.recommend.RecommendGallery
|
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.ktx.composableOf
|
import com.m3u.smartphone.ui.material.ktx.composableOf
|
||||||
import com.m3u.material.ktx.interceptVolumeEvent
|
import com.m3u.smartphone.ui.material.ktx.interceptVolumeEvent
|
||||||
import com.m3u.material.ktx.thenIf
|
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||||
import com.m3u.ui.EpisodesBottomSheet
|
import com.m3u.smartphone.ui.business.foryou.components.HeadlineBackground
|
||||||
import com.m3u.ui.MediaSheet
|
import com.m3u.smartphone.ui.business.foryou.components.PlaylistGallery
|
||||||
import com.m3u.ui.MediaSheetValue
|
import com.m3u.smartphone.ui.business.foryou.components.recommend.RecommendGallery
|
||||||
import com.m3u.ui.helper.Action
|
import com.m3u.smartphone.ui.material.components.EpisodesBottomSheet
|
||||||
import com.m3u.ui.helper.LocalHelper
|
import com.m3u.smartphone.ui.material.components.MediaSheet
|
||||||
import com.m3u.ui.helper.Metadata
|
import com.m3u.smartphone.ui.material.components.MediaSheetValue
|
||||||
|
import com.m3u.smartphone.ui.common.helper.Action
|
||||||
|
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||||
|
import com.m3u.smartphone.ui.common.helper.Metadata
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
@ -111,7 +112,7 @@ fun ForyouRoute(
|
|||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val playlist = viewModel.getPlaylist(channel.playlistUrl)
|
val playlist = viewModel.getPlaylist(channel.playlistUrl)
|
||||||
when {
|
when {
|
||||||
playlist?.isSeries ?: false -> {
|
playlist?.isSeries == true -> {
|
||||||
viewModel.series.value = channel
|
viewModel.series.value = channel
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.foryou.components
|
package com.m3u.smartphone.ui.business.foryou.components
|
||||||
|
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
@ -26,10 +26,10 @@ import coil.compose.AsyncImage
|
|||||||
import coil.request.CachePolicy
|
import coil.request.CachePolicy
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||||
import com.m3u.material.transformation.BlurTransformation
|
import com.m3u.smartphone.ui.material.transformation.BlurTransformation
|
||||||
import com.m3u.ui.helper.LocalHelper
|
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||||
import com.m3u.ui.helper.Metadata
|
import com.m3u.smartphone.ui.common.helper.Metadata
|
||||||
import com.m3u.ui.helper.useRailNav
|
import com.m3u.smartphone.ui.common.helper.useRailNav
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@Composable
|
@Composable
|
@ -1,10 +1,10 @@
|
|||||||
package com.m3u.business.foryou.components
|
package com.m3u.smartphone.ui.business.foryou.components
|
||||||
|
|
||||||
import androidx.annotation.FloatRange
|
import androidx.annotation.FloatRange
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.m3u.business.foryou.R
|
import com.m3u.business.foryou.R
|
||||||
import com.m3u.material.components.ProgressLottie
|
import com.m3u.smartphone.ui.material.components.ProgressLottie
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun Loading(
|
internal fun Loading(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.foryou.components
|
package com.m3u.smartphone.ui.business.foryou.components
|
||||||
|
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@ -31,12 +31,12 @@ import com.m3u.data.database.model.epgUrlsOrXtreamXmlUrl
|
|||||||
import com.m3u.data.database.model.fromLocal
|
import com.m3u.data.database.model.fromLocal
|
||||||
import com.m3u.data.database.model.type
|
import com.m3u.data.database.model.type
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.ktx.plus
|
import com.m3u.smartphone.ui.material.ktx.plus
|
||||||
import com.m3u.material.model.LocalHazeState
|
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.ui.helper.LocalHelper
|
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||||
import com.m3u.ui.helper.Metadata
|
import com.m3u.smartphone.ui.common.helper.Metadata
|
||||||
import com.m3u.ui.helper.useRailNav
|
import com.m3u.smartphone.ui.common.helper.useRailNav
|
||||||
import dev.chrisbanes.haze.HazeDefaults
|
import dev.chrisbanes.haze.HazeDefaults
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.haze
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.foryou.components
|
package com.m3u.smartphone.ui.business.foryou.components
|
||||||
|
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@ -26,12 +26,12 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.m3u.material.components.CircularProgressIndicator
|
import com.m3u.smartphone.ui.material.components.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||||
import com.m3u.ui.Badge
|
import com.m3u.smartphone.ui.material.components.Badge
|
||||||
import com.m3u.ui.FontFamilies
|
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun PlaylistItem(
|
internal fun PlaylistItem(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.foryou.components.recommend
|
package com.m3u.smartphone.ui.business.foryou.components.recommend
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@ -13,13 +13,14 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.m3u.business.foryou.Recommend
|
||||||
import com.m3u.core.wrapper.eventOf
|
import com.m3u.core.wrapper.eventOf
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.data.database.model.Playlist
|
import com.m3u.data.database.model.Playlist
|
||||||
import com.m3u.material.components.HorizontalPagerIndicator
|
import com.m3u.smartphone.ui.common.internal.Events
|
||||||
import com.m3u.material.ktx.pageOffset
|
import com.m3u.smartphone.ui.material.components.HorizontalPagerIndicator
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.ktx.pageOffset
|
||||||
import com.m3u.ui.Events
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun RecommendGallery(
|
internal fun RecommendGallery(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.foryou.components.recommend
|
package com.m3u.smartphone.ui.business.foryou.components.recommend
|
||||||
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@ -39,14 +39,15 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.util.lerp
|
import androidx.compose.ui.util.lerp
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
|
import com.m3u.business.foryou.Recommend
|
||||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.brush.RecommendCardContainerBrush
|
import com.m3u.smartphone.ui.material.brush.RecommendCardContainerBrush
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||||
import com.m3u.ui.FontFamilies
|
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||||
import com.m3u.ui.createPremiumBrush
|
import com.m3u.smartphone.ui.material.components.createPremiumBrush
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
@ -1,40 +1,15 @@
|
|||||||
package com.m3u.business.playlist.navigation
|
package com.m3u.smartphone.ui.business.playlist
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavOptions
|
|
||||||
import androidx.navigation.NavType
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
import com.m3u.business.playlist.PlaylistRoute
|
import com.m3u.business.playlist.PlaylistNavigation
|
||||||
|
|
||||||
private const val PLAYLIST_ROUTE_PATH = "playlist_route"
|
|
||||||
|
|
||||||
object PlaylistNavigation {
|
|
||||||
internal const val TYPE_URL = "url"
|
|
||||||
|
|
||||||
const val PLAYLIST_ROUTE =
|
|
||||||
"$PLAYLIST_ROUTE_PATH?$TYPE_URL={$TYPE_URL}"
|
|
||||||
|
|
||||||
internal fun createPlaylistRoute(url: String): String {
|
|
||||||
return "$PLAYLIST_ROUTE_PATH?$TYPE_URL=$url"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun NavController.navigateToPlaylist(
|
|
||||||
playlistUrl: String,
|
|
||||||
navOptions: NavOptions? = null,
|
|
||||||
) {
|
|
||||||
val encodedUrl = Uri.encode(playlistUrl)
|
|
||||||
val route = PlaylistNavigation.createPlaylistRoute(encodedUrl)
|
|
||||||
this.navigate(route, navOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun NavGraphBuilder.playlistScreen(
|
fun NavGraphBuilder.playlistScreen(
|
||||||
navigateToChannel: () -> Unit,
|
navigateToChannel: () -> Unit,
|
@ -1,6 +1,6 @@
|
|||||||
@file:Suppress("UsingMaterialAndMaterial3Libraries")
|
@file:Suppress("UsingMaterialAndMaterial3Libraries")
|
||||||
|
|
||||||
package com.m3u.business.playlist
|
package com.m3u.smartphone.ui.business.playlist
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -81,6 +81,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.google.accompanist.permissions.rememberPermissionState
|
import com.google.accompanist.permissions.rememberPermissionState
|
||||||
|
import com.m3u.business.playlist.PlaylistViewModel
|
||||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.core.wrapper.Event
|
import com.m3u.core.wrapper.Event
|
||||||
@ -92,29 +93,29 @@ import com.m3u.data.database.model.isSeries
|
|||||||
import com.m3u.data.database.model.isVod
|
import com.m3u.data.database.model.isVod
|
||||||
import com.m3u.data.database.model.type
|
import com.m3u.data.database.model.type
|
||||||
import com.m3u.data.service.MediaCommand
|
import com.m3u.data.service.MediaCommand
|
||||||
import com.m3u.business.playlist.components.PlaylistTabRow
|
import com.m3u.core.wrapper.Sort
|
||||||
import com.m3u.business.playlist.components.ChannelGallery
|
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.components.TextField
|
import com.m3u.smartphone.ui.material.components.TextField
|
||||||
import com.m3u.material.ktx.checkPermissionOrRationale
|
import com.m3u.smartphone.ui.material.ktx.checkPermissionOrRationale
|
||||||
import com.m3u.material.ktx.interceptVolumeEvent
|
import com.m3u.smartphone.ui.material.ktx.interceptVolumeEvent
|
||||||
import com.m3u.material.ktx.isAtTop
|
import com.m3u.smartphone.ui.material.ktx.isAtTop
|
||||||
import com.m3u.material.ktx.only
|
import com.m3u.smartphone.ui.material.ktx.only
|
||||||
import com.m3u.material.ktx.split
|
import com.m3u.smartphone.ui.material.ktx.split
|
||||||
import com.m3u.material.ktx.thenIf
|
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||||
import com.m3u.material.model.LocalHazeState
|
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.ui.Destination
|
import com.m3u.smartphone.ui.business.playlist.components.ChannelGallery
|
||||||
import com.m3u.ui.EpisodesBottomSheet
|
import com.m3u.smartphone.ui.business.playlist.components.PlaylistTabRow
|
||||||
import com.m3u.ui.EventHandler
|
import com.m3u.smartphone.ui.material.components.Destination
|
||||||
import com.m3u.ui.MediaSheet
|
import com.m3u.smartphone.ui.material.components.EpisodesBottomSheet
|
||||||
import com.m3u.ui.MediaSheetValue
|
import com.m3u.smartphone.ui.material.components.EventHandler
|
||||||
import com.m3u.ui.Sort
|
import com.m3u.smartphone.ui.material.components.MediaSheet
|
||||||
import com.m3u.ui.SortBottomSheet
|
import com.m3u.smartphone.ui.material.components.MediaSheetValue
|
||||||
import com.m3u.ui.helper.Action
|
import com.m3u.smartphone.ui.material.components.SortBottomSheet
|
||||||
import com.m3u.ui.helper.Fob
|
import com.m3u.smartphone.ui.common.helper.Action
|
||||||
import com.m3u.ui.helper.LocalHelper
|
import com.m3u.smartphone.ui.common.helper.Fob
|
||||||
import com.m3u.ui.helper.Metadata
|
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||||
|
import com.m3u.smartphone.ui.common.helper.Metadata
|
||||||
import dev.chrisbanes.haze.HazeDefaults
|
import dev.chrisbanes.haze.HazeDefaults
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.haze
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.playlist.components
|
package com.m3u.smartphone.ui.business.playlist.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -23,10 +23,10 @@ import com.m3u.core.architecture.preferences.hiltPreferences
|
|||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.data.database.model.Programme
|
import com.m3u.data.database.model.Programme
|
||||||
import com.m3u.business.playlist.PlaylistViewModel
|
import com.m3u.business.playlist.PlaylistViewModel
|
||||||
import com.m3u.material.components.CircularProgressIndicator
|
import com.m3u.smartphone.ui.material.components.CircularProgressIndicator
|
||||||
import com.m3u.material.components.VerticalDraggableScrollbar
|
import com.m3u.smartphone.ui.material.components.VerticalDraggableScrollbar
|
||||||
import com.m3u.material.ktx.plus
|
import com.m3u.smartphone.ui.material.ktx.plus
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ChannelGallery(
|
internal fun ChannelGallery(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.playlist.components
|
package com.m3u.smartphone.ui.business.playlist.components
|
||||||
|
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
@ -44,11 +44,11 @@ import com.m3u.core.architecture.preferences.hiltPreferences
|
|||||||
import com.m3u.data.database.model.Programme
|
import com.m3u.data.database.model.Programme
|
||||||
import com.m3u.data.database.model.Channel
|
import com.m3u.data.database.model.Channel
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.components.CircularProgressIndicator
|
import com.m3u.smartphone.ui.material.components.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||||
import com.m3u.ui.util.TimeUtils.formatEOrSh
|
import com.m3u.smartphone.TimeUtils.formatEOrSh
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
import kotlinx.datetime.TimeZone
|
import kotlinx.datetime.TimeZone
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.playlist.components
|
package com.m3u.smartphone.ui.business.playlist.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@ -52,14 +52,14 @@ import androidx.compose.ui.semantics.Role
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import com.m3u.material.effects.BackStackEntry
|
import com.m3u.smartphone.ui.material.effects.BackStackEntry
|
||||||
import com.m3u.material.effects.BackStackHandler
|
import com.m3u.smartphone.ui.material.effects.BackStackHandler
|
||||||
import com.m3u.material.ktx.Edge
|
import com.m3u.smartphone.ui.material.ktx.Edge
|
||||||
import com.m3u.material.ktx.blurEdge
|
import com.m3u.smartphone.ui.material.ktx.blurEdge
|
||||||
import com.m3u.material.ktx.thenIf
|
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||||
import com.m3u.material.model.LocalHazeState
|
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||||
import dev.chrisbanes.haze.HazeDefaults
|
import dev.chrisbanes.haze.HazeDefaults
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.haze
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.setting
|
package com.m3u.smartphone.ui.business.setting
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
@ -28,6 +28,8 @@ import androidx.compose.ui.text.AnnotatedString
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.m3u.business.setting.BackingUpAndRestoringState
|
||||||
|
import com.m3u.business.setting.SettingViewModel
|
||||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||||
import com.m3u.core.unit.DataUnit
|
import com.m3u.core.unit.DataUnit
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
@ -35,19 +37,19 @@ import com.m3u.data.database.model.Channel
|
|||||||
import com.m3u.data.database.model.ColorScheme
|
import com.m3u.data.database.model.ColorScheme
|
||||||
import com.m3u.data.database.model.DataSource
|
import com.m3u.data.database.model.DataSource
|
||||||
import com.m3u.data.database.model.Playlist
|
import com.m3u.data.database.model.Playlist
|
||||||
import com.m3u.business.setting.components.CanvasBottomSheet
|
|
||||||
import com.m3u.business.setting.fragments.AppearanceFragment
|
|
||||||
import com.m3u.business.setting.fragments.OptionalFragment
|
|
||||||
import com.m3u.business.setting.fragments.SubscriptionsFragment
|
|
||||||
import com.m3u.business.setting.fragments.preferences.PreferencesFragment
|
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.model.LocalHazeState
|
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||||
import com.m3u.ui.Destination
|
import com.m3u.smartphone.ui.business.setting.components.CanvasBottomSheet
|
||||||
import com.m3u.ui.EventHandler
|
import com.m3u.smartphone.ui.business.setting.fragments.AppearanceFragment
|
||||||
import com.m3u.ui.Events
|
import com.m3u.smartphone.ui.business.setting.fragments.OptionalFragment
|
||||||
import com.m3u.ui.SettingDestination
|
import com.m3u.smartphone.ui.business.setting.fragments.SubscriptionsFragment
|
||||||
import com.m3u.ui.helper.Fob
|
import com.m3u.smartphone.ui.business.setting.fragments.preferences.PreferencesFragment
|
||||||
import com.m3u.ui.helper.Metadata
|
import com.m3u.smartphone.ui.material.components.Destination
|
||||||
|
import com.m3u.smartphone.ui.material.components.EventHandler
|
||||||
|
import com.m3u.smartphone.ui.material.components.SettingDestination
|
||||||
|
import com.m3u.smartphone.ui.common.helper.Fob
|
||||||
|
import com.m3u.smartphone.ui.common.helper.Metadata
|
||||||
|
import com.m3u.smartphone.ui.common.internal.Events
|
||||||
import dev.chrisbanes.haze.HazeDefaults
|
import dev.chrisbanes.haze.HazeDefaults
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.haze
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.setting.components
|
package com.m3u.smartphone.ui.business.setting.components
|
||||||
|
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -39,10 +39,10 @@ import androidx.compose.ui.unit.dp
|
|||||||
import com.m3u.data.database.model.ColorScheme
|
import com.m3u.data.database.model.ColorScheme
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.ktx.createScheme
|
import com.m3u.smartphone.ui.material.ktx.createScheme
|
||||||
import com.m3u.material.model.LocalSpacing
|
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||||
import com.m3u.material.model.SugarColors
|
import com.m3u.smartphone.ui.material.model.SugarColors
|
||||||
import com.m3u.ui.FontFamilies
|
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
@Composable
|
@Composable
|
@ -1,12 +1,12 @@
|
|||||||
package com.m3u.business.setting.components
|
package com.m3u.smartphone.ui.business.setting.components
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.m3u.core.util.basic.title
|
import com.m3u.core.util.basic.title
|
||||||
import com.m3u.material.components.CheckBoxPreference
|
import com.m3u.smartphone.ui.material.components.CheckBoxPreference
|
||||||
import com.m3u.material.components.SwitchPreference
|
import com.m3u.smartphone.ui.material.components.SwitchPreference
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CheckBoxSharedPreference(
|
fun CheckBoxSharedPreference(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.setting.components
|
package com.m3u.smartphone.ui.business.setting.components
|
||||||
|
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@ -24,9 +24,9 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.m3u.data.database.model.DataSource
|
import com.m3u.data.database.model.DataSource
|
||||||
import com.m3u.material.components.ClickableSelection
|
import com.m3u.smartphone.ui.material.components.ClickableSelection
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.components.SelectionsDefaults
|
import com.m3u.smartphone.ui.material.components.SelectionsDefaults
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun DataSourceSelection(
|
internal fun DataSourceSelection(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.setting.components
|
package com.m3u.smartphone.ui.business.setting.components
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Delete
|
import androidx.compose.material.icons.rounded.Delete
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.setting.components
|
package com.m3u.smartphone.ui.business.setting.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.setting.components
|
package com.m3u.smartphone.ui.business.setting.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.setting.components
|
package com.m3u.smartphone.ui.business.setting.components
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
@ -17,7 +17,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import com.m3u.core.util.readFileName
|
import com.m3u.core.util.readFileName
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import com.m3u.material.components.ToggleableSelection
|
import com.m3u.smartphone.ui.material.components.ToggleableSelection
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun LocalStorageButton(
|
internal fun LocalStorageButton(
|
@ -1,4 +1,4 @@
|
|||||||
package com.m3u.business.setting.components
|
package com.m3u.smartphone.ui.business.setting.components
|
||||||
|
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.m3u.i18n.R.string
|
import com.m3u.i18n.R.string
|
||||||
import com.m3u.material.components.ToggleableSelection
|
import com.m3u.smartphone.ui.material.components.ToggleableSelection
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun LocalStorageSwitch(
|
internal fun LocalStorageSwitch(
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user