mirror of
https://github.com/oxyroid/M3UAndroid.git
synced 2025-05-18 03:45:56 +08:00
refactor: move ui logic to smartphone module.
This commit is contained in:
@ -22,7 +22,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
implementation(project(":ui"))
|
||||
implementation(project(":data"))
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
|
||||
@ -39,6 +39,5 @@ dependencies {
|
||||
ksp(libs.androidx.hilt.compiler)
|
||||
implementation(libs.androidx.hilt.work)
|
||||
|
||||
implementation(libs.minabox)
|
||||
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.install
|
||||
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.Channel
|
||||
import com.m3u.data.database.model.DataSource
|
||||
@ -74,15 +75,15 @@ class ChannelViewModel @Inject constructor(
|
||||
private val logger = delegate.install(Profiles.VIEWMODEL_CHANNEL)
|
||||
|
||||
// searched screencast devices
|
||||
internal var devices by mutableStateOf(emptyList<Device>())
|
||||
var devices by mutableStateOf(emptyList<Device>())
|
||||
|
||||
private val _volume: MutableStateFlow<Float> by lazy {
|
||||
MutableStateFlow(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) / 100f)
|
||||
}
|
||||
internal val volume = _volume.asStateFlow()
|
||||
val volume = _volume.asStateFlow()
|
||||
|
||||
internal val channel: StateFlow<Channel?> = playerManager.channel
|
||||
internal val playlist: StateFlow<Playlist?> = playerManager.playlist
|
||||
val channel: StateFlow<Channel?> = playerManager.channel
|
||||
val playlist: StateFlow<Playlist?> = playerManager.playlist
|
||||
|
||||
val adjacentChannels: StateFlow<AdjacentChannels?> = flatmapCombined(
|
||||
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
|
||||
if (it.isSeries || it.isVod) return@map false
|
||||
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 ->
|
||||
all
|
||||
.mapValues { (_, formats) -> formats }
|
||||
.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 group = groups.find { it.type == type } ?: return
|
||||
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)
|
||||
}
|
||||
|
||||
// channel playing state
|
||||
internal val playerState: StateFlow<PlayerState> = combine(
|
||||
val playerState: StateFlow<PlayerState> = combine(
|
||||
playerManager.player,
|
||||
playerManager.playbackState,
|
||||
playerManager.size,
|
||||
@ -168,14 +169,14 @@ class ChannelViewModel @Inject constructor(
|
||||
private val _isDevicesVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||
|
||||
// show searching devices dialog or not
|
||||
internal val isDevicesVisible = _isDevicesVisible.asStateFlow()
|
||||
val isDevicesVisible = _isDevicesVisible.asStateFlow()
|
||||
|
||||
private val _searching: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||
|
||||
// searching or not
|
||||
internal val searching = _searching.asStateFlow()
|
||||
val searching = _searching.asStateFlow()
|
||||
|
||||
internal fun openDlnaDevices() {
|
||||
fun openDlnaDevices() {
|
||||
viewModelScope.launch {
|
||||
delay(800.milliseconds)
|
||||
_searching.value = true
|
||||
@ -189,7 +190,7 @@ class ChannelViewModel @Inject constructor(
|
||||
_isDevicesVisible.value = true
|
||||
}
|
||||
|
||||
internal fun closeDlnaDevices() {
|
||||
fun closeDlnaDevices() {
|
||||
runCatching {
|
||||
_searching.value = false
|
||||
_isDevicesVisible.value = false
|
||||
@ -213,7 +214,7 @@ class ChannelViewModel @Inject constructor(
|
||||
|
||||
private var controlPoint: ControlPoint? = null
|
||||
|
||||
internal fun connectDlnaDevice(device: Device) {
|
||||
fun connectDlnaDevice(device: Device) {
|
||||
val url = channel.value?.url ?: return
|
||||
device.findAction(ACTION_SET_AV_TRANSPORT_URI)?.invoke(
|
||||
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 {
|
||||
val id = channel.value?.id ?: return@launch
|
||||
channelRepository.favouriteOrUnfavourite(id)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun onVolume(target: Float) {
|
||||
fun onVolume(target: Float) {
|
||||
_volume.update { target }
|
||||
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
audioManager.setStreamVolume(
|
||||
@ -246,7 +247,7 @@ class ChannelViewModel @Inject constructor(
|
||||
// controlPoint?.setVolume((target * 100).roundToInt(), null)
|
||||
}
|
||||
|
||||
internal fun getPreviousChannel() {
|
||||
fun getPreviousChannel() {
|
||||
viewModelScope.launch {
|
||||
val previousChannelId = adjacentChannels.value?.prevId
|
||||
if (adjacentChannels.value != null && previousChannelId != null) {
|
||||
@ -255,7 +256,7 @@ class ChannelViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getNextChannel() {
|
||||
fun getNextChannel() {
|
||||
viewModelScope.launch {
|
||||
val nextChannelId = adjacentChannels.value?.nextId
|
||||
if (adjacentChannels.value != null && nextChannelId != null) {
|
||||
@ -279,7 +280,7 @@ class ChannelViewModel @Inject constructor(
|
||||
playerManager.pauseOrContinue(isContinued)
|
||||
}
|
||||
|
||||
internal val programmeReminderIds: StateFlow<List<Int>> = workManager.getWorkInfosFlow(
|
||||
val programmeReminderIds: StateFlow<List<Int>> = workManager.getWorkInfosFlow(
|
||||
WorkQuery.fromStates(
|
||||
WorkInfo.State.ENQUEUED
|
||||
)
|
||||
@ -319,14 +320,14 @@ class ChannelViewModel @Inject constructor(
|
||||
|
||||
// the channels which is in the same category with the current channel
|
||||
// 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())
|
||||
Pager(PagingConfig(10)) {
|
||||
channelRepository.pagingAllByPlaylistUrl(
|
||||
playlist.url,
|
||||
channel.value?.category.orEmpty(),
|
||||
"",
|
||||
ChannelRepository.Sort.UNSPECIFIED
|
||||
Sort.UNSPECIFIED
|
||||
)
|
||||
}
|
||||
.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())
|
||||
val relationId = channel.relationId ?: return@flatMapLatest flowOf(PagingData.empty())
|
||||
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)
|
||||
val relationId = channel.relationId ?: return@flatMapLatest flowOf(defaultProgrammeRange)
|
||||
programmeRepository
|
||||
@ -377,7 +378,7 @@ class ChannelViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000L)
|
||||
)
|
||||
|
||||
internal fun onSpeedUpdated(race: Float) {
|
||||
fun onSpeedUpdated(race: Float) {
|
||||
playerManager.updateSpeed(race)
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import androidx.media3.common.PlaybackException
|
||||
import androidx.media3.common.Player
|
||||
|
||||
@Immutable
|
||||
internal data class PlayerState(
|
||||
data class PlayerState(
|
||||
val playState: @Player.State Int = Player.STATE_IDLE,
|
||||
val videoSize: Rect = Rect(),
|
||||
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 {
|
||||
implementation(project(":core"))
|
||||
implementation(project(":ui"))
|
||||
implementation(project(":data"))
|
||||
|
||||
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.preferences.Preferences
|
||||
import com.m3u.core.wrapper.Resource
|
||||
import com.m3u.core.wrapper.Sort
|
||||
import com.m3u.core.wrapper.asResource
|
||||
import com.m3u.core.wrapper.mapResource
|
||||
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.service.MediaCommand
|
||||
import com.m3u.data.service.PlayerManager
|
||||
import com.m3u.ui.Sort
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -161,9 +161,9 @@ class FavouriteViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
internal val series = MutableStateFlow<Channel?>(null)
|
||||
internal val seriesReplay = MutableStateFlow(0)
|
||||
internal val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
||||
val series = MutableStateFlow<Channel?>(null)
|
||||
val seriesReplay = MutableStateFlow(0)
|
||||
val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
||||
.combine(seriesReplay) { series, _ -> series }
|
||||
.flatMapLatest { series ->
|
||||
if (series == null) flow { }
|
||||
@ -177,10 +177,10 @@ class FavouriteViewModel @Inject constructor(
|
||||
started = SharingStarted.Lazily
|
||||
)
|
||||
|
||||
internal suspend fun getPlaylist(playlistUrl: String): Playlist? =
|
||||
suspend fun getPlaylist(playlistUrl: String): Playlist? =
|
||||
playlistRepository.get(playlistUrl)
|
||||
|
||||
internal fun playRandomly() {
|
||||
fun playRandomly() {
|
||||
viewModelScope.launch {
|
||||
val channel = channelRepository.getRandomIgnoreSeriesAndHidden() ?: return@launch
|
||||
playerManager.play(
|
||||
|
@ -21,15 +21,13 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
implementation(project(":ui"))
|
||||
implementation(project(":data"))
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
|
||||
implementation(libs.airbnb.lottie.compose)
|
||||
|
||||
implementation(libs.google.dagger.hilt)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
ksp(libs.google.dagger.hilt.compiler)
|
||||
|
@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkQuery
|
||||
import com.m3u.business.foryou.Recommend
|
||||
import com.m3u.core.architecture.dispatcher.Dispatcher
|
||||
import com.m3u.core.architecture.dispatcher.M3uDispatchers.IO
|
||||
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.programme.ProgrammeRepository
|
||||
import com.m3u.data.worker.SubscriptionWorker
|
||||
import com.m3u.business.foryou.components.recommend.Recommend
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@ -58,7 +58,7 @@ class ForyouViewModel @Inject constructor(
|
||||
) : ViewModel() {
|
||||
private val logger = delegate.install(Profiles.VIEWMODEL_FORYOU)
|
||||
|
||||
internal val playlistCounts: StateFlow<Resource<List<PlaylistWithCount>>> =
|
||||
val playlistCounts: StateFlow<Resource<List<PlaylistWithCount>>> =
|
||||
playlistRepository
|
||||
.observeAllCounts()
|
||||
.asResource()
|
||||
@ -68,7 +68,7 @@ class ForyouViewModel @Inject constructor(
|
||||
initialValue = Resource.Loading
|
||||
)
|
||||
|
||||
internal val subscribingPlaylistUrls: StateFlow<List<String>> =
|
||||
val subscribingPlaylistUrls: StateFlow<List<String>> =
|
||||
workManager.getWorkInfosFlow(
|
||||
WorkQuery.fromStates(
|
||||
WorkInfo.State.RUNNING,
|
||||
@ -86,7 +86,7 @@ class ForyouViewModel @Inject constructor(
|
||||
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 }
|
||||
.map { it.toDuration(DurationUnit.MILLISECONDS) }
|
||||
@ -105,7 +105,7 @@ class ForyouViewModel @Inject constructor(
|
||||
initialValue = null,
|
||||
started = SharingStarted.Lazily
|
||||
)
|
||||
internal val specs: StateFlow<List<Recommend.Spec>> = unseensDuration
|
||||
val specs: StateFlow<List<Recommend.Spec>> = unseensDuration
|
||||
.flatMapLatest { channelRepository.observeAllUnseenFavourites(it) }
|
||||
.let { flow ->
|
||||
combine(flow, newRelease) { channels, nr ->
|
||||
@ -132,15 +132,15 @@ class ForyouViewModel @Inject constructor(
|
||||
initialValue = emptyList()
|
||||
)
|
||||
|
||||
internal fun onUnsubscribePlaylist(url: String) {
|
||||
fun onUnsubscribePlaylist(url: String) {
|
||||
viewModelScope.launch {
|
||||
playlistRepository.unsubscribe(url)
|
||||
}
|
||||
}
|
||||
|
||||
internal val series = MutableStateFlow<Channel?>(null)
|
||||
internal val seriesReplay = MutableStateFlow(0)
|
||||
internal val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
||||
val series = MutableStateFlow<Channel?>(null)
|
||||
val seriesReplay = MutableStateFlow(0)
|
||||
val episodes: StateFlow<Resource<List<XtreamChannelInfo.Episode>>> = series
|
||||
.combine(seriesReplay) { series, _ -> series }
|
||||
.flatMapLatest { series ->
|
||||
if (series == null) flow { }
|
||||
@ -154,6 +154,6 @@ class ForyouViewModel @Inject constructor(
|
||||
started = SharingStarted.Lazily
|
||||
)
|
||||
|
||||
internal suspend fun getPlaylist(playlistUrl: String): Playlist? =
|
||||
suspend fun getPlaylist(playlistUrl: String): Playlist? =
|
||||
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 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.Playlist
|
||||
|
||||
@Immutable
|
||||
class Recommend(
|
||||
@ -35,4 +35,4 @@ class Recommend(
|
||||
val size: ClosedRange<DataUnit>,
|
||||
val url: String,
|
||||
): Spec
|
||||
}
|
||||
}
|
@ -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 {
|
||||
implementation(project(":core"))
|
||||
implementation(project(":ui"))
|
||||
implementation(project(":data"))
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
|
||||
|
@ -16,7 +16,7 @@ import androidx.navigation.navArgument
|
||||
private const val PLAYLIST_CONFIGURATION_ROUTE_PATH = "playlist_configuration_route"
|
||||
|
||||
object PlaylistConfigurationNavigation {
|
||||
internal const val TYPE_PLAYLIST_URL = "playlist_url"
|
||||
const val TYPE_PLAYLIST_URL = "playlist_url"
|
||||
|
||||
const val PLAYLIST_CONFIGURATION_ROUTE =
|
||||
"$PLAYLIST_CONFIGURATION_ROUTE_PATH?$TYPE_PLAYLIST_URL={$TYPE_PLAYLIST_URL}"
|
||||
@ -34,25 +34,3 @@ fun NavController.navigateToPlaylistConfiguration(
|
||||
val route = PlaylistConfigurationNavigation.createPlaylistConfigurationRoute(encodedUrl)
|
||||
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 javax.inject.Inject
|
||||
|
||||
internal typealias EpgManifest = Map<Playlist, Boolean>
|
||||
typealias EpgManifest = Map<Playlist, Boolean>
|
||||
|
||||
@HiltViewModel
|
||||
class PlaylistConfigurationViewModel @Inject constructor(
|
||||
@ -53,7 +53,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
||||
private val logger = delegate.install(Profiles.VIEWMODEL_PLAYLIST_CONFIGURATION)
|
||||
private val playlistUrl: StateFlow<String> = savedStateHandle
|
||||
.getStateFlow(PlaylistConfigurationNavigation.TYPE_PLAYLIST_URL, "")
|
||||
internal val playlist: StateFlow<Playlist?> = playlistUrl.flatMapLatest {
|
||||
val playlist: StateFlow<Playlist?> = playlistUrl.flatMapLatest {
|
||||
playlistRepository.observe(it)
|
||||
}
|
||||
.stateIn(
|
||||
@ -62,7 +62,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000L)
|
||||
)
|
||||
|
||||
internal val xtreamUserInfo: StateFlow<Resource<XtreamInfo.UserInfo>> =
|
||||
val xtreamUserInfo: StateFlow<Resource<XtreamInfo.UserInfo>> =
|
||||
playlist.map { playlist ->
|
||||
playlist ?: return@map null
|
||||
if (playlist.source != DataSource.Xtream) return@map null
|
||||
@ -81,7 +81,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
||||
started = SharingStarted.Lazily
|
||||
)
|
||||
|
||||
internal val manifest: StateFlow<EpgManifest> = combine(
|
||||
val manifest: StateFlow<EpgManifest> = combine(
|
||||
playlistRepository.observeAllEpgs(),
|
||||
playlist
|
||||
) { epgs, playlist ->
|
||||
@ -93,7 +93,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
||||
started = SharingStarted.Lazily,
|
||||
initialValue = emptyMap()
|
||||
)
|
||||
internal val subscribingOrRefreshingWorkInfo: StateFlow<WorkInfo?> = workManager
|
||||
val subscribingOrRefreshingWorkInfo: StateFlow<WorkInfo?> = workManager
|
||||
.getWorkInfosFlow(
|
||||
WorkQuery.fromStates(
|
||||
WorkInfo.State.RUNNING,
|
||||
@ -114,7 +114,7 @@ class PlaylistConfigurationViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000L)
|
||||
)
|
||||
|
||||
internal val expired: StateFlow<LocalDateTime?> = playlistUrl
|
||||
val expired: StateFlow<LocalDateTime?> = playlistUrl
|
||||
.flatMapLatest { 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
|
||||
viewModelScope.launch {
|
||||
playlistRepository.onUpdatePlaylistTitle(playlistUrl, title)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun onUpdatePlaylistUserAgent(userAgent: String?) {
|
||||
fun onUpdatePlaylistUserAgent(userAgent: String?) {
|
||||
val playlistUrl = playlistUrl.value
|
||||
viewModelScope.launch {
|
||||
playlistRepository.onUpdatePlaylistUserAgent(playlistUrl, userAgent)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun onUpdateEpgPlaylist(usecase: PlaylistRepository.EpgPlaylistUseCase) {
|
||||
fun onUpdateEpgPlaylist(usecase: PlaylistRepository.EpgPlaylistUseCase) {
|
||||
viewModelScope.launch {
|
||||
playlistRepository.onUpdateEpgPlaylist(usecase)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun onUpdatePlaylistAutoRefreshProgrammes() {
|
||||
fun onUpdatePlaylistAutoRefreshProgrammes() {
|
||||
val playlistUrl = playlistUrl.value
|
||||
viewModelScope.launch {
|
||||
playlistRepository.onUpdatePlaylistAutoRefreshProgrammes(playlistUrl)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun onSyncProgrammes() {
|
||||
fun onSyncProgrammes() {
|
||||
val playlistUrl = playlistUrl.value
|
||||
SubscriptionWorker.epg(workManager, playlistUrl, true)
|
||||
}
|
||||
|
||||
internal fun onCancelSyncProgrammes() {
|
||||
fun onCancelSyncProgrammes() {
|
||||
val workInfo = subscribingOrRefreshingWorkInfo.value
|
||||
workInfo?.id?.let { workManager.cancelWorkById(it) }
|
||||
}
|
||||
|
@ -23,13 +23,10 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
implementation(project(":ui"))
|
||||
implementation(project(":data"))
|
||||
|
||||
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.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.worker.SubscriptionWorker
|
||||
import com.m3u.business.playlist.PlaylistMessage.ChannelCoverSaved
|
||||
import com.m3u.business.playlist.navigation.PlaylistNavigation
|
||||
import com.m3u.ui.Sort
|
||||
import com.m3u.ui.toCommonSort
|
||||
import com.m3u.core.wrapper.Sort
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
@ -99,10 +97,10 @@ class PlaylistViewModel @Inject constructor(
|
||||
) : ViewModel() {
|
||||
private val logger = delegate.install(Profiles.VIEWMODEL_PLAYLIST)
|
||||
|
||||
internal val playlistUrl: StateFlow<String> = savedStateHandle
|
||||
val playlistUrl: StateFlow<String> = savedStateHandle
|
||||
.getStateFlow(PlaylistNavigation.TYPE_URL, "")
|
||||
|
||||
internal val playlist: StateFlow<Playlist?> = playlistUrl.flatMapLatest {
|
||||
val playlist: StateFlow<Playlist?> = playlistUrl.flatMapLatest {
|
||||
playlistRepository.observe(it)
|
||||
}
|
||||
.stateIn(
|
||||
@ -111,7 +109,7 @@ class PlaylistViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000L)
|
||||
)
|
||||
|
||||
internal val zapping: StateFlow<Channel?> = combine(
|
||||
val zapping: StateFlow<Channel?> = combine(
|
||||
snapshotFlow { preferences.zappingMode },
|
||||
playerManager.channel,
|
||||
playlistUrl.flatMapLatest { channelRepository.observeAllByPlaylistUrl(it) }
|
||||
@ -125,7 +123,7 @@ class PlaylistViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000)
|
||||
)
|
||||
|
||||
internal val subscribingOrRefreshing: StateFlow<Boolean> = workManager
|
||||
val subscribingOrRefreshing: StateFlow<Boolean> = workManager
|
||||
.getWorkInfosFlow(
|
||||
WorkQuery.fromStates(
|
||||
WorkInfo.State.RUNNING,
|
||||
@ -146,20 +144,20 @@ class PlaylistViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5000)
|
||||
)
|
||||
|
||||
internal fun refresh() {
|
||||
fun refresh() {
|
||||
val url = playlistUrl.value
|
||||
viewModelScope.launch {
|
||||
playlistRepository.refresh(url)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun favourite(id: Int) {
|
||||
fun favourite(id: Int) {
|
||||
viewModelScope.launch {
|
||||
channelRepository.favouriteOrUnfavourite(id)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun savePicture(id: Int) {
|
||||
fun savePicture(id: Int) {
|
||||
viewModelScope.launch {
|
||||
val channel = channelRepository.get(id)
|
||||
if (channel == null) {
|
||||
@ -188,7 +186,7 @@ class PlaylistViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun hide(id: Int) {
|
||||
fun hide(id: Int) {
|
||||
viewModelScope.launch {
|
||||
val channel = channelRepository.get(id)
|
||||
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"
|
||||
viewModelScope.launch {
|
||||
val channel = channelRepository.get(id) ?: return@launch
|
||||
@ -227,7 +225,7 @@ class PlaylistViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
internal fun createTvRecommend(activityContext: Context, id: Int) {
|
||||
fun createTvRecommend(activityContext: Context, id: Int) {
|
||||
val channelInternalProviderId = "M3U"
|
||||
val programInternalProviderId = "Program_$id"
|
||||
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)
|
||||
}
|
||||
|
||||
private val sortIndex: MutableStateFlow<Int> = MutableStateFlow(0)
|
||||
|
||||
internal val sort: StateFlow<Sort> = sortIndex
|
||||
val sort: StateFlow<Sort> = sortIndex
|
||||
.map { Sort.entries[it] }
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
@ -315,12 +313,12 @@ class PlaylistViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000L)
|
||||
)
|
||||
|
||||
internal fun sort(sort: Sort) {
|
||||
fun sort(sort: Sort) {
|
||||
sortIndex.value = Sort.entries.indexOf(sort).coerceAtLeast(0)
|
||||
}
|
||||
|
||||
internal val query = MutableStateFlow("")
|
||||
internal val scrollUp: MutableStateFlow<Event<Unit>> = MutableStateFlow(handledEvent())
|
||||
val query = MutableStateFlow("")
|
||||
val scrollUp: MutableStateFlow<Event<Unit>> = MutableStateFlow(handledEvent())
|
||||
|
||||
@Immutable
|
||||
data class ChannelParameters(
|
||||
@ -354,7 +352,7 @@ class PlaylistViewModel @Inject constructor(
|
||||
started = SharingStarted.Lazily
|
||||
)
|
||||
|
||||
internal val channels: StateFlow<List<CategoryWithChannels>> = combine(
|
||||
val channels: StateFlow<List<CategoryWithChannels>> = combine(
|
||||
playlistUrl,
|
||||
categories,
|
||||
query, sort
|
||||
@ -376,7 +374,7 @@ class PlaylistViewModel @Inject constructor(
|
||||
playlistUrl,
|
||||
"",
|
||||
query,
|
||||
sort.toCommonSort()
|
||||
sort
|
||||
)
|
||||
}
|
||||
.flow
|
||||
@ -392,7 +390,7 @@ class PlaylistViewModel @Inject constructor(
|
||||
playlistUrl,
|
||||
category,
|
||||
query,
|
||||
sort.toCommonSort()
|
||||
sort
|
||||
)
|
||||
}
|
||||
.flow
|
||||
@ -407,7 +405,7 @@ class PlaylistViewModel @Inject constructor(
|
||||
started = SharingStarted.Lazily
|
||||
)
|
||||
|
||||
internal val pinnedCategories: StateFlow<List<String>> = playlist
|
||||
val pinnedCategories: StateFlow<List<String>> = playlist
|
||||
.map { it?.pinnedCategories ?: emptyList() }
|
||||
|
||||
.flowOn(ioDispatcher)
|
||||
@ -417,21 +415,21 @@ class PlaylistViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000L)
|
||||
)
|
||||
|
||||
internal fun pinOrUnpinCategory(category: String) {
|
||||
fun pinOrUnpinCategory(category: String) {
|
||||
val currentPlaylistUrl = playlistUrl.value
|
||||
viewModelScope.launch {
|
||||
playlistRepository.pinOrUnpinCategory(currentPlaylistUrl, category)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun hideCategory(category: String) {
|
||||
fun hideCategory(category: String) {
|
||||
val currentPlaylistUrl = playlistUrl.value
|
||||
viewModelScope.launch {
|
||||
playlistRepository.hideOrUnhideCategory(currentPlaylistUrl, category)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun setup(
|
||||
fun setup(
|
||||
channelId: Int,
|
||||
onPlayMediaCommand: (MediaCommand) -> Unit
|
||||
) {
|
||||
@ -448,10 +446,10 @@ class PlaylistViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
internal val series = MutableStateFlow<Channel?>(null)
|
||||
internal val seriesReplay = MutableStateFlow(0)
|
||||
val series = MutableStateFlow<Channel?>(null)
|
||||
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 }
|
||||
.flatMapLatest { series ->
|
||||
if (series == null) flow {}
|
||||
|
@ -21,7 +21,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
implementation(project(":ui"))
|
||||
implementation(project(":data"))
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
|
||||
@ -35,8 +35,4 @@ dependencies {
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
ksp(libs.androidx.hilt.compiler)
|
||||
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
|
||||
|
||||
internal enum class BackingUpAndRestoringState {
|
||||
enum class BackingUpAndRestoringState {
|
||||
NONE, BACKING_UP, RESTORING, BOTH;
|
||||
|
||||
companion object {
|
||||
|
@ -68,7 +68,7 @@ class SettingViewModel @Inject constructor(
|
||||
) : ViewModel() {
|
||||
private val logger = delegate.install(Profiles.VIEWMODEL_SETTING)
|
||||
|
||||
internal val epgs: StateFlow<List<Playlist>> = playlistRepository
|
||||
val epgs: StateFlow<List<Playlist>> = playlistRepository
|
||||
.observeAllEpgs()
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
@ -76,7 +76,7 @@ class SettingViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000L)
|
||||
)
|
||||
|
||||
internal val hiddenChannels: StateFlow<List<Channel>> = channelRepository
|
||||
val hiddenChannels: StateFlow<List<Channel>> = channelRepository
|
||||
.observeAllHidden()
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
@ -84,7 +84,7 @@ class SettingViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000L)
|
||||
)
|
||||
|
||||
internal val hiddenCategoriesWithPlaylists: StateFlow<List<Pair<Playlist, String>>> =
|
||||
val hiddenCategoriesWithPlaylists: StateFlow<List<Pair<Playlist, String>>> =
|
||||
playlistRepository
|
||||
.observeAll()
|
||||
.map { playlists ->
|
||||
@ -99,13 +99,13 @@ class SettingViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000L)
|
||||
)
|
||||
|
||||
internal fun onUnhidePlaylistCategory(playlistUrl: String, group: String) {
|
||||
fun onUnhidePlaylistCategory(playlistUrl: String, group: String) {
|
||||
viewModelScope.launch {
|
||||
playlistRepository.hideOrUnhideCategory(playlistUrl, group)
|
||||
}
|
||||
}
|
||||
|
||||
internal val colorSchemes: StateFlow<List<ColorScheme>> = combine(
|
||||
val colorSchemes: StateFlow<List<ColorScheme>> = combine(
|
||||
colorSchemeDao.observeAll().catch { emit(emptyList()) },
|
||||
snapshotFlow { preferences.followSystemTheme }
|
||||
) { all, followSystemTheme -> if (followSystemTheme) all.filter { !it.isDark } else all }
|
||||
@ -116,7 +116,7 @@ class SettingViewModel @Inject constructor(
|
||||
initialValue = emptyList()
|
||||
)
|
||||
|
||||
internal fun onClipboard(url: String) {
|
||||
fun onClipboard(url: String) {
|
||||
val title = run {
|
||||
val filePath = url.split("/")
|
||||
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 }
|
||||
if (hidden != null) {
|
||||
viewModelScope.launch {
|
||||
@ -146,7 +146,7 @@ class SettingViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun subscribe() {
|
||||
fun subscribe() {
|
||||
val title = titleState.value
|
||||
val url = urlState.value
|
||||
val uri = uriState.value
|
||||
@ -237,7 +237,7 @@ class SettingViewModel @Inject constructor(
|
||||
resetAllInputs()
|
||||
}
|
||||
|
||||
internal val backingUpOrRestoring: StateFlow<BackingUpAndRestoringState> = workManager
|
||||
val backingUpOrRestoring: StateFlow<BackingUpAndRestoringState> = workManager
|
||||
.getWorkInfosFlow(
|
||||
WorkQuery.fromStates(
|
||||
WorkInfo.State.RUNNING,
|
||||
@ -266,7 +266,7 @@ class SettingViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5000)
|
||||
)
|
||||
|
||||
internal fun backup(uri: Uri) {
|
||||
fun backup(uri: Uri) {
|
||||
workManager.cancelAllWorkByTag(BackupWorker.TAG)
|
||||
val request = OneTimeWorkRequestBuilder<BackupWorker>()
|
||||
.setInputData(
|
||||
@ -281,7 +281,7 @@ class SettingViewModel @Inject constructor(
|
||||
messager.emit(SettingMessage.BackingUp)
|
||||
}
|
||||
|
||||
internal fun restore(uri: Uri) {
|
||||
fun restore(uri: Uri) {
|
||||
workManager.cancelAllWorkByTag(RestoreWorker.TAG)
|
||||
val request = OneTimeWorkRequestBuilder<RestoreWorker>()
|
||||
.setInputData(
|
||||
@ -296,7 +296,7 @@ class SettingViewModel @Inject constructor(
|
||||
messager.emit(SettingMessage.Restoring)
|
||||
}
|
||||
|
||||
internal val cacheSpace: StateFlow<DataUnit> = playerManager
|
||||
val cacheSpace: StateFlow<DataUnit> = playerManager
|
||||
.cacheSpace
|
||||
.map { DataUnit.of(it) }
|
||||
.stateIn(
|
||||
@ -315,18 +315,18 @@ class SettingViewModel @Inject constructor(
|
||||
epgState.value = ""
|
||||
}
|
||||
|
||||
internal fun clearCache() {
|
||||
fun clearCache() {
|
||||
playerManager.clearCache()
|
||||
}
|
||||
|
||||
internal fun deleteEpgPlaylist(epgUrl: String) {
|
||||
fun deleteEpgPlaylist(epgUrl: String) {
|
||||
viewModelScope.launch {
|
||||
playlistRepository.deleteEpgPlaylistAndProgrammes(epgUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
internal fun applyColor(
|
||||
fun applyColor(
|
||||
prev: ColorScheme?,
|
||||
argb: Int,
|
||||
isDark: Boolean
|
||||
@ -347,24 +347,24 @@ class SettingViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreSchemes() {
|
||||
fun restoreSchemes() {
|
||||
val schemes = ColorSchemeExample.schemes
|
||||
viewModelScope.launch {
|
||||
colorSchemeDao.insertAll(*schemes.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
internal val versionName: String = publisher.versionName
|
||||
internal val versionCode: Int = publisher.versionCode
|
||||
val versionName: String = publisher.versionName
|
||||
val versionCode: Int = publisher.versionCode
|
||||
|
||||
internal val titleState = mutableStateOf("")
|
||||
internal val urlState = mutableStateOf("")
|
||||
internal val uriState = mutableStateOf(Uri.EMPTY)
|
||||
internal val localStorageState = mutableStateOf(false)
|
||||
internal val forTvState = mutableStateOf(false)
|
||||
internal val basicUrlState = mutableStateOf("")
|
||||
internal val usernameState = mutableStateOf("")
|
||||
internal val passwordState = mutableStateOf("")
|
||||
internal val epgState = mutableStateOf("")
|
||||
internal val selectedState: MutableState<DataSource> = mutableStateOf(DataSource.M3U)
|
||||
val titleState = mutableStateOf("")
|
||||
val urlState = mutableStateOf("")
|
||||
val uriState = mutableStateOf(Uri.EMPTY)
|
||||
val localStorageState = mutableStateOf(false)
|
||||
val forTvState = mutableStateOf(false)
|
||||
val basicUrlState = mutableStateOf("")
|
||||
val usernameState = mutableStateOf("")
|
||||
val passwordState = mutableStateOf("")
|
||||
val epgState = mutableStateOf("")
|
||||
val selectedState: MutableState<DataSource> = mutableStateOf(DataSource.M3U)
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ android {
|
||||
dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.google.material)
|
||||
|
||||
implementation(libs.androidx.media3.exoplayer)
|
||||
}
|
@ -13,7 +13,6 @@ android {
|
||||
dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.google.material)
|
||||
|
||||
implementation(libs.androidx.media3.exoplayer)
|
||||
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
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import com.m3u.core.wrapper.Sort
|
||||
import com.m3u.data.database.model.AdjacentChannels
|
||||
import com.m3u.data.database.model.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@ -35,12 +36,4 @@ interface ChannelRepository {
|
||||
fun observeAllUnseenFavourites(limit: Duration): Flow<List<Channel>>
|
||||
fun observeAllFavourite(): 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.Channel
|
||||
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.catch
|
||||
import kotlinx.datetime.Clock
|
||||
|
@ -55,7 +55,6 @@ ktor-server = "3.0.0-beta-1"
|
||||
mm2d-mmupnp = "3.1.6"
|
||||
symbolProcessingApi = "2.0.0-1.0.22"
|
||||
profileinstaller = "1.4.1"
|
||||
tvFoundation = "1.0.0-alpha12"
|
||||
|
||||
[libraries]
|
||||
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-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-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" }
|
||||
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" }
|
||||
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]
|
||||
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(":core")
|
||||
include(":data")
|
||||
include(":material")
|
||||
include(":ui")
|
||||
include(
|
||||
":business:foryou",
|
||||
":business:favorite",
|
||||
":business:setting",
|
||||
":business:playlist",
|
||||
":business:playlist-configuration",
|
||||
":business:channel",
|
||||
":business:crash"
|
||||
":business:channel"
|
||||
)
|
||||
include(":baselineprofile")
|
||||
include(":i18n")
|
||||
|
@ -10,6 +10,7 @@ plugins {
|
||||
alias(libs.plugins.androidx.baselineprofile)
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.m3u.smartphone"
|
||||
compileSdk = 35
|
||||
@ -120,38 +121,68 @@ baselineProfile {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
implementation(project(":ui"))
|
||||
implementation(project(":data"))
|
||||
// business
|
||||
implementation(project(":business:foryou"))
|
||||
implementation(project(":business:favorite"))
|
||||
implementation(project(":business:setting"))
|
||||
implementation(project(":business:playlist"))
|
||||
implementation(project(":business:channel"))
|
||||
implementation(project(":business:playlist-configuration"))
|
||||
implementation(project(":business:crash"))
|
||||
// baselineprofile
|
||||
implementation(libs.androidx.profileinstaller)
|
||||
"baselineProfile"(project(":baselineprofile"))
|
||||
|
||||
// base
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
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.compose)
|
||||
implementation(libs.androidx.lifecycle.process)
|
||||
|
||||
implementation(libs.androidx.core.splashscreen)
|
||||
|
||||
// work
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
// dagger
|
||||
implementation(libs.google.dagger.hilt)
|
||||
ksp(libs.google.dagger.hilt.compiler)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
ksp(libs.androidx.hilt.compiler)
|
||||
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.material3)
|
||||
|
||||
// accompanist
|
||||
implementation(libs.google.accompanist.permissions)
|
||||
// performance
|
||||
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 com.m3u.core.architecture.logger.Logger
|
||||
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 javax.inject.Inject
|
||||
|
||||
|
@ -9,9 +9,9 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import com.m3u.smartphone.ui.App
|
||||
import com.m3u.smartphone.ui.AppViewModel
|
||||
import com.m3u.ui.Events.enableDPadReaction
|
||||
import com.m3u.ui.Toolkit
|
||||
import com.m3u.ui.helper.Helper
|
||||
import com.m3u.smartphone.ui.common.helper.Helper
|
||||
import com.m3u.smartphone.ui.common.internal.Events.enableDPadReaction
|
||||
import com.m3u.smartphone.ui.common.internal.Toolkit
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.m3u.ui.util
|
||||
package com.m3u.smartphone
|
||||
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
|
@ -41,7 +41,7 @@ import com.m3u.smartphone.R
|
||||
import com.m3u.core.Contracts
|
||||
import com.m3u.data.database.model.Channel
|
||||
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 kotlinx.datetime.Instant
|
||||
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.data.tv.model.RemoteDirection
|
||||
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.Scaffold
|
||||
import com.m3u.ui.Destination
|
||||
import com.m3u.ui.FontFamilies
|
||||
import com.m3u.ui.SnackHost
|
||||
import com.m3u.smartphone.ui.material.components.Destination
|
||||
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||
import com.m3u.smartphone.ui.material.components.SnackHost
|
||||
|
||||
@Composable
|
||||
fun App(
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.m3u.business.channel
|
||||
package com.m3u.smartphone.ui.business.channel
|
||||
|
||||
import android.content.pm.ActivityInfo
|
||||
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.dp
|
||||
import androidx.media3.common.Player
|
||||
import com.m3u.business.channel.PlayerState
|
||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||
import com.m3u.core.util.basic.isNotEmpty
|
||||
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.material.components.mask.MaskButton
|
||||
import com.m3u.material.components.mask.MaskCircleButton
|
||||
import com.m3u.material.components.mask.MaskPanel
|
||||
import com.m3u.material.components.mask.MaskState
|
||||
import com.m3u.material.effects.currentBackStackEntry
|
||||
import com.m3u.material.ktx.thenIf
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.FontFamilies
|
||||
import com.m3u.ui.Image
|
||||
import com.m3u.ui.helper.LocalHelper
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.business.channel.components.MaskTextButton
|
||||
import com.m3u.smartphone.ui.business.channel.components.PlayerMask
|
||||
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||
import com.m3u.smartphone.ui.material.components.Image
|
||||
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||
import com.m3u.smartphone.ui.material.components.mask.MaskButton
|
||||
import com.m3u.smartphone.ui.material.components.mask.MaskCircleButton
|
||||
import com.m3u.smartphone.ui.material.components.mask.MaskPanel
|
||||
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.launch
|
||||
import kotlin.math.absoluteValue
|
||||
@ -164,10 +162,11 @@ internal fun ChannelMask(
|
||||
playerState.playState
|
||||
) {
|
||||
derivedStateOf {
|
||||
val currentPlayer = playerState.player
|
||||
when {
|
||||
playerState.player == null -> false
|
||||
!playerState.player.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM) -> false
|
||||
else -> with(playerState.player) {
|
||||
currentPlayer == null -> false
|
||||
!currentPlayer.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM) -> false
|
||||
else -> with(currentPlayer) {
|
||||
!isCurrentMediaItemDynamic && isCurrentMediaItemSeekable
|
||||
}
|
||||
}
|
||||
@ -179,8 +178,7 @@ internal fun ChannelMask(
|
||||
) {
|
||||
derivedStateOf {
|
||||
playerState.player
|
||||
?.isCommandAvailable(Player.COMMAND_SET_SPEED_AND_PITCH)
|
||||
?: false
|
||||
?.isCommandAvailable(Player.COMMAND_SET_SPEED_AND_PITCH) == true
|
||||
}
|
||||
}
|
||||
|
||||
@ -555,14 +553,14 @@ private fun MaskCenterButton(
|
||||
scaleY = scale
|
||||
},
|
||||
icon = when (centerRole) {
|
||||
Play -> Icons.Rounded.PlayArrow
|
||||
Pause -> Icons.Rounded.Pause
|
||||
MaskCenterRole.Play -> Icons.Rounded.PlayArrow
|
||||
MaskCenterRole.Pause -> Icons.Rounded.Pause
|
||||
else -> Icons.Rounded.Refresh
|
||||
},
|
||||
onClick = when (centerRole) {
|
||||
Replay -> onRetry
|
||||
Play -> onPlay
|
||||
Pause -> onPause
|
||||
MaskCenterRole.Replay -> onRetry
|
||||
MaskCenterRole.Play -> onPlay
|
||||
MaskCenterRole.Pause -> onPause
|
||||
else -> {
|
||||
{}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.m3u.business.channel
|
||||
package com.m3u.smartphone.ui.business.channel
|
||||
|
||||
import android.database.ContentObserver
|
||||
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.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.Channel
|
||||
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.material.components.Background
|
||||
import com.m3u.material.components.PullPanelLayout
|
||||
import com.m3u.material.components.PullPanelLayoutValue
|
||||
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 com.m3u.smartphone.ui.material.components.Player
|
||||
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||
import com.m3u.smartphone.ui.material.components.rememberPlayerState
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
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
|
||||
fun ChannelRoute(
|
@ -1,4 +1,4 @@
|
||||
package com.m3u.business.channel
|
||||
package com.m3u.smartphone.ui.business.channel
|
||||
|
||||
internal enum class MaskGesture {
|
||||
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.res.Configuration
|
||||
@ -9,14 +9,15 @@ import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.m3u.business.channel.ChannelViewModel
|
||||
import com.m3u.core.Contracts
|
||||
import com.m3u.data.database.model.isSeries
|
||||
import com.m3u.data.repository.playlist.PlaylistRepository
|
||||
import com.m3u.data.repository.channel.ChannelRepository
|
||||
import com.m3u.data.service.MediaCommand
|
||||
import com.m3u.ui.Events.enableDPadReaction
|
||||
import com.m3u.ui.Toolkit
|
||||
import com.m3u.ui.helper.Helper
|
||||
import com.m3u.smartphone.ui.common.helper.Helper
|
||||
import com.m3u.smartphone.ui.common.internal.Events.enableDPadReaction
|
||||
import com.m3u.smartphone.ui.common.internal.Toolkit
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
@ -72,7 +73,7 @@ class PlayerActivity : ComponentActivity() {
|
||||
val playlist = playlistRepository.get(channel.playlistUrl)
|
||||
when {
|
||||
// series can not be played from shortcuts
|
||||
playlist?.isSeries ?: false -> {}
|
||||
playlist?.isSeries == true -> {}
|
||||
else -> {
|
||||
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.core.tween
|
||||
@ -13,9 +13,9 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m3u.material.model.LocalDuration
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.Image
|
||||
import com.m3u.smartphone.ui.material.model.LocalDuration
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.Image
|
||||
|
||||
@Composable
|
||||
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.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.foundation.clickable
|
||||
@ -27,12 +27,12 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m3u.core.util.basic.title
|
||||
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 com.m3u.material.components.mask.MaskState
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.UnstableBadge
|
||||
import com.m3u.ui.UnstableValue
|
||||
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.UnstableBadge
|
||||
import com.m3u.smartphone.ui.material.components.UnstableValue
|
||||
import net.mm2d.upnp.Device
|
||||
|
||||
@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.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.fillMaxWidth
|
||||
@ -36,8 +36,8 @@ import androidx.media3.common.C
|
||||
import androidx.media3.common.Format
|
||||
import com.m3u.i18n.R.string
|
||||
import androidx.compose.material3.Icon
|
||||
import com.m3u.material.components.mask.MaskState
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@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.Row
|
||||
@ -17,8 +17,8 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.MonoText
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.MonoText
|
||||
|
||||
@Composable
|
||||
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.Row
|
||||
@ -16,8 +16,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.material3.IconButton
|
||||
import com.m3u.material.components.mask.MaskState
|
||||
import com.m3u.ui.FontFamilies
|
||||
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||
|
||||
@Composable
|
||||
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.fadeIn
|
||||
@ -20,9 +20,9 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.coerceAtLeast
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m3u.material.components.mask.Mask
|
||||
import com.m3u.material.components.mask.MaskState
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.mask.Mask
|
||||
import com.m3u.smartphone.ui.material.components.mask.MaskState
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
|
||||
@Composable
|
||||
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.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.ProgrammeRange
|
||||
import com.m3u.data.service.MediaCommand
|
||||
import com.m3u.material.components.Background
|
||||
import com.m3u.material.components.CircularProgressIndicator
|
||||
import com.m3u.smartphone.ui.material.components.Background
|
||||
import com.m3u.smartphone.ui.material.components.CircularProgressIndicator
|
||||
import androidx.compose.material3.IconButton
|
||||
import com.m3u.material.effects.BackStackEntry
|
||||
import com.m3u.material.effects.BackStackHandler
|
||||
import com.m3u.material.ktx.Edge
|
||||
import com.m3u.material.ktx.blurEdges
|
||||
import com.m3u.material.ktx.thenIf
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.ui.FontFamilies
|
||||
import com.m3u.ui.helper.LocalHelper
|
||||
import com.m3u.ui.util.TimeUtils.formatEOrSh
|
||||
import com.m3u.ui.util.TimeUtils.toEOrSh
|
||||
import com.m3u.smartphone.ui.material.effects.BackStackEntry
|
||||
import com.m3u.smartphone.ui.material.effects.BackStackHandler
|
||||
import com.m3u.smartphone.ui.material.ktx.Edge
|
||||
import com.m3u.smartphone.ui.material.ktx.blurEdges
|
||||
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||
import com.m3u.smartphone.TimeUtils.formatEOrSh
|
||||
import com.m3u.smartphone.TimeUtils.toEOrSh
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.Clock
|
||||
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.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.Companion.HOUR_LENGTH
|
||||
import androidx.compose.material3.Icon
|
||||
import com.m3u.material.ktx.Edge
|
||||
import com.m3u.material.ktx.blurEdges
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.FontFamilies
|
||||
import com.m3u.ui.util.TimeUtils.formatEOrSh
|
||||
import com.m3u.ui.util.TimeUtils.toEOrSh
|
||||
import com.m3u.smartphone.ui.material.ktx.Edge
|
||||
import com.m3u.smartphone.ui.material.ktx.blurEdges
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||
import com.m3u.smartphone.TimeUtils.formatEOrSh
|
||||
import com.m3u.smartphone.TimeUtils.toEOrSh
|
||||
import eu.wewox.minabox.MinaBox
|
||||
import eu.wewox.minabox.MinaBoxItem
|
||||
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.interaction.MutableInteractionSource
|
||||
@ -10,8 +10,8 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.m3u.business.channel.ChannelMaskUtils.detectVerticalGesture
|
||||
import com.m3u.material.ktx.thenIf
|
||||
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||
import com.m3u.smartphone.ui.business.channel.ChannelMaskUtils.detectVerticalGesture
|
||||
|
||||
@Composable
|
||||
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.content.Intent
|
||||
@ -26,6 +26,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@ -43,6 +44,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.wrapper.Resource
|
||||
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.parser.xtream.XtreamInfo
|
||||
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.material.components.Background
|
||||
import androidx.compose.material3.Icon
|
||||
import com.m3u.material.components.PlaceholderField
|
||||
import com.m3u.material.ktx.checkPermissionOrRationale
|
||||
import com.m3u.material.model.LocalHazeState
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.helper.LocalHelper
|
||||
import com.m3u.ui.helper.Metadata
|
||||
import com.m3u.smartphone.ui.material.components.Background
|
||||
import com.m3u.smartphone.ui.material.components.PlaceholderField
|
||||
import com.m3u.smartphone.ui.material.ktx.checkPermissionOrRationale
|
||||
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.business.configuration.components.AutoSyncProgrammesButton
|
||||
import com.m3u.smartphone.ui.business.configuration.components.EpgManifestGallery
|
||||
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.haze
|
||||
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.clickable
|
||||
@ -15,9 +15,9 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m3u.core.util.basic.title
|
||||
import com.m3u.i18n.R
|
||||
import com.m3u.material.components.SelectionsDefaults
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.smartphone.ui.material.components.SelectionsDefaults
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||
|
||||
@Composable
|
||||
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.border
|
||||
@ -21,14 +21,14 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m3u.business.playlist.configuration.EpgManifest
|
||||
import com.m3u.core.util.basic.title
|
||||
import com.m3u.data.database.model.Playlist
|
||||
import com.m3u.data.repository.playlist.PlaylistRepository
|
||||
import com.m3u.business.playlist.configuration.EpgManifest
|
||||
import com.m3u.i18n.R.string
|
||||
import com.m3u.material.components.SelectionsDefaults
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.smartphone.ui.material.components.SelectionsDefaults
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||
|
||||
internal fun LazyListScope.EpgManifestGallery(
|
||||
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.fadeIn
|
||||
@ -22,10 +22,10 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m3u.core.util.basic.title
|
||||
import com.m3u.i18n.R.string
|
||||
import com.m3u.material.components.CircularProgressIndicator
|
||||
import com.m3u.material.components.SelectionsDefaults
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.smartphone.ui.material.components.CircularProgressIndicator
|
||||
import com.m3u.smartphone.ui.material.components.SelectionsDefaults
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
|
||||
@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.foundation.layout.Arrangement
|
||||
@ -23,10 +23,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m3u.core.wrapper.Resource
|
||||
import com.m3u.data.parser.xtream.XtreamInfo
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.Badge
|
||||
import com.m3u.ui.FontFamilies
|
||||
import com.m3u.ui.TextBadge
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.Badge
|
||||
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||
import com.m3u.smartphone.ui.material.components.TextBadge
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
@Composable
|
@ -1,13 +1,13 @@
|
||||
package com.m3u.business.crash
|
||||
package com.m3u.smartphone.ui.business.crash
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import com.m3u.ui.Events.enableDPadReaction
|
||||
import com.m3u.ui.Toolkit
|
||||
import com.m3u.ui.helper.Helper
|
||||
import com.m3u.smartphone.ui.common.helper.Helper
|
||||
import com.m3u.smartphone.ui.common.internal.Events.enableDPadReaction
|
||||
import com.m3u.smartphone.ui.common.internal.Toolkit
|
||||
import dagger.hilt.android.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.compose.foundation.layout.Box
|
||||
@ -10,9 +10,9 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.m3u.business.crash.navigation.Destination
|
||||
import com.m3u.business.crash.screen.detail.DetailScreen
|
||||
import com.m3u.business.crash.screen.list.ListScreen
|
||||
import com.m3u.smartphone.ui.business.crash.navigation.Destination
|
||||
import com.m3u.smartphone.ui.business.crash.screen.detail.DetailScreen
|
||||
import com.m3u.smartphone.ui.business.crash.screen.list.ListScreen
|
||||
|
||||
@Composable
|
||||
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.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.rounded.Adb
|
||||
@ -9,7 +9,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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
|
||||
|
||||
@Composable
|
@ -1,4 +1,4 @@
|
||||
package com.m3u.business.crash.navigation
|
||||
package com.m3u.smartphone.ui.business.crash.navigation
|
||||
|
||||
internal sealed class 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.result.contract.ActivityResultContracts
|
||||
@ -18,10 +18,10 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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 com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.MonoText
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.MonoText
|
||||
|
||||
@Composable
|
||||
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 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.layout.fillMaxSize
|
||||
@ -7,9 +7,9 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.m3u.business.crash.components.FileItem
|
||||
import com.m3u.business.crash.screen.list.navigation.NavigateToDetail
|
||||
import com.m3u.material.components.Background
|
||||
import com.m3u.smartphone.ui.material.components.Background
|
||||
import com.m3u.smartphone.ui.business.crash.components.FileItem
|
||||
import com.m3u.smartphone.ui.business.crash.screen.list.navigation.NavigateToDetail
|
||||
|
||||
@Composable
|
||||
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 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.view.KeyEvent
|
||||
@ -24,25 +24,26 @@ import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.m3u.business.favorite.FavouriteViewModel
|
||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||
import com.m3u.core.util.basic.title
|
||||
import com.m3u.core.wrapper.Resource
|
||||
import com.m3u.data.database.model.Channel
|
||||
import com.m3u.data.database.model.isSeries
|
||||
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.material.ktx.interceptVolumeEvent
|
||||
import com.m3u.material.ktx.thenIf
|
||||
import com.m3u.material.model.LocalHazeState
|
||||
import com.m3u.ui.EpisodesBottomSheet
|
||||
import com.m3u.ui.MediaSheet
|
||||
import com.m3u.ui.MediaSheetValue
|
||||
import com.m3u.ui.Sort
|
||||
import com.m3u.ui.SortBottomSheet
|
||||
import com.m3u.ui.helper.Action
|
||||
import com.m3u.ui.helper.LocalHelper
|
||||
import com.m3u.ui.helper.Metadata
|
||||
import com.m3u.smartphone.ui.material.ktx.interceptVolumeEvent
|
||||
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||
import com.m3u.smartphone.ui.business.favorite.components.FavouriteGallery
|
||||
import com.m3u.smartphone.ui.material.components.EpisodesBottomSheet
|
||||
import com.m3u.smartphone.ui.material.components.MediaSheet
|
||||
import com.m3u.smartphone.ui.material.components.MediaSheetValue
|
||||
import com.m3u.smartphone.ui.material.components.SortBottomSheet
|
||||
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 dev.chrisbanes.haze.HazeDefaults
|
||||
import dev.chrisbanes.haze.haze
|
||||
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.Box
|
||||
@ -18,9 +18,9 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m3u.core.wrapper.Resource
|
||||
import com.m3u.data.database.model.Channel
|
||||
import com.m3u.material.components.VerticalDraggableScrollbar
|
||||
import com.m3u.material.ktx.plus
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.VerticalDraggableScrollbar
|
||||
import com.m3u.smartphone.ui.material.ktx.plus
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
|
||||
@Composable
|
||||
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.material3.CardDefaults
|
||||
@ -18,8 +18,8 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.m3u.data.database.model.Channel
|
||||
import com.m3u.i18n.R.string
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
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.view.KeyEvent
|
||||
@ -31,6 +31,8 @@ import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.util.basic.title
|
||||
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.isSeries
|
||||
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.material.ktx.composableOf
|
||||
import com.m3u.material.ktx.interceptVolumeEvent
|
||||
import com.m3u.material.ktx.thenIf
|
||||
import com.m3u.ui.EpisodesBottomSheet
|
||||
import com.m3u.ui.MediaSheet
|
||||
import com.m3u.ui.MediaSheetValue
|
||||
import com.m3u.ui.helper.Action
|
||||
import com.m3u.ui.helper.LocalHelper
|
||||
import com.m3u.ui.helper.Metadata
|
||||
import com.m3u.smartphone.ui.material.ktx.composableOf
|
||||
import com.m3u.smartphone.ui.material.ktx.interceptVolumeEvent
|
||||
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||
import com.m3u.smartphone.ui.business.foryou.components.HeadlineBackground
|
||||
import com.m3u.smartphone.ui.business.foryou.components.PlaylistGallery
|
||||
import com.m3u.smartphone.ui.business.foryou.components.recommend.RecommendGallery
|
||||
import com.m3u.smartphone.ui.material.components.EpisodesBottomSheet
|
||||
import com.m3u.smartphone.ui.material.components.MediaSheet
|
||||
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.launch
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
@ -111,7 +112,7 @@ fun ForyouRoute(
|
||||
coroutineScope.launch {
|
||||
val playlist = viewModel.getPlaylist(channel.playlistUrl)
|
||||
when {
|
||||
playlist?.isSeries ?: false -> {
|
||||
playlist?.isSeries == true -> {
|
||||
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.core.tween
|
||||
@ -26,10 +26,10 @@ import coil.compose.AsyncImage
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||
import com.m3u.material.transformation.BlurTransformation
|
||||
import com.m3u.ui.helper.LocalHelper
|
||||
import com.m3u.ui.helper.Metadata
|
||||
import com.m3u.ui.helper.useRailNav
|
||||
import com.m3u.smartphone.ui.material.transformation.BlurTransformation
|
||||
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||
import com.m3u.smartphone.ui.common.helper.Metadata
|
||||
import com.m3u.smartphone.ui.common.helper.useRailNav
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@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.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.m3u.business.foryou.R
|
||||
import com.m3u.material.components.ProgressLottie
|
||||
import com.m3u.smartphone.ui.material.components.ProgressLottie
|
||||
|
||||
@Composable
|
||||
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.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.type
|
||||
import com.m3u.i18n.R.string
|
||||
import com.m3u.material.ktx.plus
|
||||
import com.m3u.material.model.LocalHazeState
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.helper.LocalHelper
|
||||
import com.m3u.ui.helper.Metadata
|
||||
import com.m3u.ui.helper.useRailNav
|
||||
import com.m3u.smartphone.ui.material.ktx.plus
|
||||
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.common.helper.LocalHelper
|
||||
import com.m3u.smartphone.ui.common.helper.Metadata
|
||||
import com.m3u.smartphone.ui.common.helper.useRailNav
|
||||
import dev.chrisbanes.haze.HazeDefaults
|
||||
import dev.chrisbanes.haze.haze
|
||||
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.layout.Arrangement
|
||||
@ -26,12 +26,12 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
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 com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.ui.Badge
|
||||
import com.m3u.ui.FontFamilies
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.smartphone.ui.material.components.Badge
|
||||
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||
|
||||
@Composable
|
||||
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.Column
|
||||
@ -13,13 +13,14 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.m3u.business.foryou.Recommend
|
||||
import com.m3u.core.wrapper.eventOf
|
||||
import com.m3u.data.database.model.Channel
|
||||
import com.m3u.data.database.model.Playlist
|
||||
import com.m3u.material.components.HorizontalPagerIndicator
|
||||
import com.m3u.material.ktx.pageOffset
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.Events
|
||||
import com.m3u.smartphone.ui.common.internal.Events
|
||||
import com.m3u.smartphone.ui.material.components.HorizontalPagerIndicator
|
||||
import com.m3u.smartphone.ui.material.ktx.pageOffset
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
|
||||
@Composable
|
||||
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.background
|
||||
@ -39,14 +39,15 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.lerp
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.m3u.business.foryou.Recommend
|
||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||
import com.m3u.core.util.basic.title
|
||||
import com.m3u.i18n.R.string
|
||||
import com.m3u.material.brush.RecommendCardContainerBrush
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.ui.FontFamilies
|
||||
import com.m3u.ui.createPremiumBrush
|
||||
import com.m3u.smartphone.ui.material.brush.RecommendCardContainerBrush
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||
import com.m3u.smartphone.ui.material.components.createPremiumBrush
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
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.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navArgument
|
||||
import com.m3u.business.playlist.PlaylistRoute
|
||||
|
||||
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)
|
||||
}
|
||||
import com.m3u.business.playlist.PlaylistNavigation
|
||||
|
||||
fun NavGraphBuilder.playlistScreen(
|
||||
navigateToChannel: () -> Unit,
|
@ -1,6 +1,6 @@
|
||||
@file:Suppress("UsingMaterialAndMaterial3Libraries")
|
||||
|
||||
package com.m3u.business.playlist
|
||||
package com.m3u.smartphone.ui.business.playlist
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
@ -81,6 +81,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.m3u.business.playlist.PlaylistViewModel
|
||||
import com.m3u.core.architecture.preferences.hiltPreferences
|
||||
import com.m3u.core.util.basic.title
|
||||
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.type
|
||||
import com.m3u.data.service.MediaCommand
|
||||
import com.m3u.business.playlist.components.PlaylistTabRow
|
||||
import com.m3u.business.playlist.components.ChannelGallery
|
||||
import com.m3u.core.wrapper.Sort
|
||||
import com.m3u.i18n.R.string
|
||||
import com.m3u.material.components.TextField
|
||||
import com.m3u.material.ktx.checkPermissionOrRationale
|
||||
import com.m3u.material.ktx.interceptVolumeEvent
|
||||
import com.m3u.material.ktx.isAtTop
|
||||
import com.m3u.material.ktx.only
|
||||
import com.m3u.material.ktx.split
|
||||
import com.m3u.material.ktx.thenIf
|
||||
import com.m3u.material.model.LocalHazeState
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.ui.Destination
|
||||
import com.m3u.ui.EpisodesBottomSheet
|
||||
import com.m3u.ui.EventHandler
|
||||
import com.m3u.ui.MediaSheet
|
||||
import com.m3u.ui.MediaSheetValue
|
||||
import com.m3u.ui.Sort
|
||||
import com.m3u.ui.SortBottomSheet
|
||||
import com.m3u.ui.helper.Action
|
||||
import com.m3u.ui.helper.Fob
|
||||
import com.m3u.ui.helper.LocalHelper
|
||||
import com.m3u.ui.helper.Metadata
|
||||
import com.m3u.smartphone.ui.material.components.TextField
|
||||
import com.m3u.smartphone.ui.material.ktx.checkPermissionOrRationale
|
||||
import com.m3u.smartphone.ui.material.ktx.interceptVolumeEvent
|
||||
import com.m3u.smartphone.ui.material.ktx.isAtTop
|
||||
import com.m3u.smartphone.ui.material.ktx.only
|
||||
import com.m3u.smartphone.ui.material.ktx.split
|
||||
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.business.playlist.components.ChannelGallery
|
||||
import com.m3u.smartphone.ui.business.playlist.components.PlaylistTabRow
|
||||
import com.m3u.smartphone.ui.material.components.Destination
|
||||
import com.m3u.smartphone.ui.material.components.EpisodesBottomSheet
|
||||
import com.m3u.smartphone.ui.material.components.EventHandler
|
||||
import com.m3u.smartphone.ui.material.components.MediaSheet
|
||||
import com.m3u.smartphone.ui.material.components.MediaSheetValue
|
||||
import com.m3u.smartphone.ui.material.components.SortBottomSheet
|
||||
import com.m3u.smartphone.ui.common.helper.Action
|
||||
import com.m3u.smartphone.ui.common.helper.Fob
|
||||
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.haze
|
||||
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.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.Programme
|
||||
import com.m3u.business.playlist.PlaylistViewModel
|
||||
import com.m3u.material.components.CircularProgressIndicator
|
||||
import com.m3u.material.components.VerticalDraggableScrollbar
|
||||
import com.m3u.material.ktx.plus
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.components.CircularProgressIndicator
|
||||
import com.m3u.smartphone.ui.material.components.VerticalDraggableScrollbar
|
||||
import com.m3u.smartphone.ui.material.ktx.plus
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
|
||||
@Composable
|
||||
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.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.Channel
|
||||
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 com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.ui.util.TimeUtils.formatEOrSh
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.smartphone.TimeUtils.formatEOrSh
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
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.foundation.background
|
||||
@ -52,14 +52,14 @@ import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.material3.IconButton
|
||||
import com.m3u.material.effects.BackStackEntry
|
||||
import com.m3u.material.effects.BackStackHandler
|
||||
import com.m3u.material.ktx.Edge
|
||||
import com.m3u.material.ktx.blurEdge
|
||||
import com.m3u.material.ktx.thenIf
|
||||
import com.m3u.material.model.LocalHazeState
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.shape.AbsoluteSmoothCornerShape
|
||||
import com.m3u.smartphone.ui.material.effects.BackStackEntry
|
||||
import com.m3u.smartphone.ui.material.effects.BackStackHandler
|
||||
import com.m3u.smartphone.ui.material.ktx.Edge
|
||||
import com.m3u.smartphone.ui.material.ktx.blurEdge
|
||||
import com.m3u.smartphone.ui.material.ktx.thenIf
|
||||
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.shape.AbsoluteSmoothCornerShape
|
||||
import dev.chrisbanes.haze.HazeDefaults
|
||||
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 androidx.activity.compose.BackHandler
|
||||
@ -28,6 +28,8 @@ import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||
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.unit.DataUnit
|
||||
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.DataSource
|
||||
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.material.model.LocalHazeState
|
||||
import com.m3u.ui.Destination
|
||||
import com.m3u.ui.EventHandler
|
||||
import com.m3u.ui.Events
|
||||
import com.m3u.ui.SettingDestination
|
||||
import com.m3u.ui.helper.Fob
|
||||
import com.m3u.ui.helper.Metadata
|
||||
import com.m3u.smartphone.ui.material.model.LocalHazeState
|
||||
import com.m3u.smartphone.ui.business.setting.components.CanvasBottomSheet
|
||||
import com.m3u.smartphone.ui.business.setting.fragments.AppearanceFragment
|
||||
import com.m3u.smartphone.ui.business.setting.fragments.OptionalFragment
|
||||
import com.m3u.smartphone.ui.business.setting.fragments.SubscriptionsFragment
|
||||
import com.m3u.smartphone.ui.business.setting.fragments.preferences.PreferencesFragment
|
||||
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.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.foundation.layout.Box
|
||||
@ -39,10 +39,10 @@ import androidx.compose.ui.unit.dp
|
||||
import com.m3u.data.database.model.ColorScheme
|
||||
import com.m3u.i18n.R.string
|
||||
import androidx.compose.material3.Icon
|
||||
import com.m3u.material.ktx.createScheme
|
||||
import com.m3u.material.model.LocalSpacing
|
||||
import com.m3u.material.model.SugarColors
|
||||
import com.m3u.ui.FontFamilies
|
||||
import com.m3u.smartphone.ui.material.ktx.createScheme
|
||||
import com.m3u.smartphone.ui.material.model.LocalSpacing
|
||||
import com.m3u.smartphone.ui.material.model.SugarColors
|
||||
import com.m3u.smartphone.ui.material.components.FontFamilies
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@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.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.m3u.core.util.basic.title
|
||||
import com.m3u.material.components.CheckBoxPreference
|
||||
import com.m3u.material.components.SwitchPreference
|
||||
import com.m3u.smartphone.ui.material.components.CheckBoxPreference
|
||||
import com.m3u.smartphone.ui.material.components.SwitchPreference
|
||||
|
||||
@Composable
|
||||
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.layout.Arrangement
|
||||
@ -24,9 +24,9 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
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 com.m3u.material.components.SelectionsDefaults
|
||||
import com.m3u.smartphone.ui.material.components.SelectionsDefaults
|
||||
|
||||
@Composable
|
||||
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.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.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.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 androidx.activity.compose.rememberLauncherForActivityResult
|
||||
@ -17,7 +17,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import com.m3u.core.util.readFileName
|
||||
import com.m3u.i18n.R.string
|
||||
import androidx.compose.material3.Icon
|
||||
import com.m3u.material.components.ToggleableSelection
|
||||
import com.m3u.smartphone.ui.material.components.ToggleableSelection
|
||||
|
||||
@Composable
|
||||
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.Text
|
||||
@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.m3u.i18n.R.string
|
||||
import com.m3u.material.components.ToggleableSelection
|
||||
import com.m3u.smartphone.ui.material.components.ToggleableSelection
|
||||
|
||||
@Composable
|
||||
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