From 68b0b93b4938bcf911177a9c105af59d907c2d61 Mon Sep 17 00:00:00 2001 From: oxy Date: Tue, 17 Oct 2023 19:36:18 +0800 Subject: [PATCH] build: material module for jitpack. --- .../java/com/m3u/androidApp/MainActivity.kt | 10 +-- .../androidApp/components/AppBottomSheet.kt | 10 +-- .../m3u/androidApp/components/AppSnackHost.kt | 4 +- .../m3u/androidApp/navigation/M3UNavHost.kt | 6 +- .../m3u/androidApp/navigation/RootGraph.kt | 25 ++++-- .../main/java/com/m3u/androidApp/ui/App.kt | 6 +- .../java/com/m3u/androidApp/ui/AppDefaults.kt | 8 +- .../java/com/m3u/androidApp/ui/AppScaffold.kt | 17 ++-- .../com/m3u/androidApp/ui/AppViewModel.kt | 4 +- .../java/com/m3u/core/unspecified/UBoolean.kt | 21 ++++- .../java/com/m3u/core/util/Collections.kt | 4 +- core/src/main/java/com/m3u/core/util/Files.kt | 22 +++-- ...bservableStateOf.kt => ObservableState.kt} | 17 ++-- .../core/util/context/SharedPreferences.kt | 21 +---- .../main/java/com/m3u/core/wrapper/Event.kt | 5 ++ .../java/com/m3u/core/wrapper/Resource.kt | 6 ++ .../main/java/com/m3u/core/wrapper/Stored.kt | 6 ++ data/build.gradle.kts | 1 + .../main/java/com/m3u/data/api/DropboxApi.kt | 7 +- .../main/java/com/m3u/data/api/GithubApi.kt | 8 +- .../m3u/data/api/dto/{ => github}/Asset.kt | 2 +- .../com/m3u/data/api/dto/{ => github}/File.kt | 2 +- .../com/m3u/data/api/dto/{ => github}/Leaf.kt | 2 +- .../m3u/data/api/dto/{ => github}/Links.kt | 2 +- .../m3u/data/api/dto/{ => github}/Release.kt | 2 +- .../com/m3u/data/api/dto/{ => github}/Tree.kt | 2 +- .../com/m3u/data/api/dto/{ => github}/User.kt | 2 +- .../main/java/com/m3u/data/di/ApiModule.kt | 2 +- .../m3u/data/repository/CloudRepository.kt | 6 ++ .../com/m3u/data/repository/FeedRepository.kt | 3 +- .../com/m3u/data/repository/LiveRepository.kt | 2 +- .../{Repository.kt => ReadOnlyRepository.kt} | 12 ++- .../repository/impl/DropboxCloudRepository.kt | 76 ++++++++++++++++ .../repository/impl/FeedRepositoryImpl.kt | 4 +- .../m3u/data/service/NotificationService.kt | 1 + .../worker/SubscriptionInBackgroundWorker.kt | 4 +- .../com/m3u/features/about/AboutScreen.kt | 14 +-- .../about/components/ContributorItem.kt | 4 +- .../m3u/features/about/model/Contributor.kt | 2 +- .../com/m3u/features/console/ConsoleScreen.kt | 16 ++-- .../com/m3u/features/console/MonoStyle.kt | 2 +- .../m3u/features/crash/components/FileItem.kt | 4 +- .../crash/screen/detail/DetailScreen.kt | 6 +- .../features/crash/screen/list/ListScreen.kt | 2 +- .../m3u/features/favorite/FavoriteScreen.kt | 21 +++-- .../favorite/components/FavoriteLiveItem.kt | 16 ++-- .../java/com/m3u/features/feed/FeedScreen.kt | 39 +++++---- .../com/m3u/features/feed/FeedViewModel.kt | 6 +- .../features/feed/components/FeedDialog.kt | 18 ++-- .../m3u/features/feed/components/LiveItem.kt | 14 +-- .../java/com/m3u/features/live/LiveScreen.kt | 18 ++-- .../live/components/CoverPlaceholder.kt | 4 +- .../live/components/DlnaDeviceItem.kt | 2 +- .../live/components/DlnaDevicesBottomSheet.kt | 12 +-- .../m3u/features/live/components/LiveMask.kt | 8 +- .../features/live/fragments/LiveFragment.kt | 48 +++++----- .../java/com/m3u/features/main/MainScreen.kt | 26 +++--- .../com/m3u/features/main/MainViewModel.kt | 4 +- .../m3u/features/main/components/FeedItem.kt | 12 +-- .../m3u/features/main/components/Loading.kt | 2 +- .../features/main/components/MainDialog.kt | 20 ++--- .../com/m3u/features/setting/SettingEvent.kt | 4 +- .../com/m3u/features/setting/SettingScreen.kt | 33 ++++--- .../m3u/features/setting/SettingViewModel.kt | 18 ++-- .../setting/components/MutedLiveItem.kt | 2 +- .../setting/fragments/PreferencesFragment.kt | 87 ++++++++++--------- .../setting/fragments/ScriptsFragment.kt | 8 +- .../fragments/SubscriptionsFragment.kt | 43 +++++---- gradle/libs.versions.toml | 2 + jitpack.yml | 6 ++ material/.gitignore | 3 + material/build.gradle.kts | 71 +++++++++++++++ material/consumer-rules.pro | 0 material/proguard-rules.pro | 21 +++++ material/src/main/AndroidManifest.xml | 4 + .../com/m3u/material/components/AppBars.kt | 15 ++-- .../m3u/material/components/Backgrounds.kt | 8 +- .../com/m3u/material/components/Badges.kt | 8 +- .../com/m3u/material/components/Brushes.kt | 6 +- .../com/m3u/material/components/Buttons.kt | 4 +- .../com/m3u/material/components/Dialogs.kt | 8 +- .../com/m3u/material/components/Images.kt | 8 +- .../com/m3u/material/components/Indicators.kt | 2 +- .../com/m3u/material/components/Layouts.kt | 4 +- .../com/m3u/material/components/Lotties.kt | 2 +- .../java/com/m3u/material}/components/Mask.kt | 6 +- .../material}/components/NavigationSheet.kt | 4 +- .../m3u/material}/components/Preferences.kt | 34 ++++---- .../com/m3u/material}/components/Selection.kt | 6 +- .../com/m3u/material/components/TextFields.kt | 16 ++-- .../material}/components/ThemeSelection.kt | 28 +++--- .../java/com/m3u/material}/ktx/Animations.kt | 4 +- .../main/java/com/m3u/material}/ktx/Blurs.kt | 2 +- .../java/com/m3u/material}/ktx/Effects.kt | 7 +- .../java/com/m3u/material}/ktx/Interaction.kt | 2 +- .../com/m3u/material}/ktx/InterceptEvent.kt | 2 +- .../com/m3u/material}/ktx/LifecycleEffect.kt | 2 +- .../com/m3u/material}/ktx/ScrollableState.kt | 9 +- .../java/com/m3u/material/ktx}/Specified.kt | 2 +- .../java/com/m3u/material}/model/Duration.kt | 2 +- .../com/m3u/material}/model/GradientColors.kt | 2 +- .../java/com/m3u/material}/model/Scalable.kt | 2 +- .../java/com/m3u/material}/model/Spacing.kt | 2 +- .../java/com/m3u/material}/model/StepColor.kt | 2 +- .../com/m3u/material}/model/SugarColors.kt | 2 +- .../java/com/m3u/material}/model/Theme.kt | 2 +- settings.gradle.kts | 3 +- ui/.gitignore | 4 +- ui/build.gradle.kts | 29 ++----- ui/proguard-rules.pro | 2 +- .../java/com/m3u/ui/{model => }/AppFont.kt | 3 +- ui/src/main/java/com/m3u/ui/Destination.kt | 14 +-- .../java/com/m3u/ui/{ktx => }/EventHandler.kt | 2 +- .../{LocalProvider.kt => M3ULocalProvider.kt} | 14 ++- .../com/m3u/ui/{components => }/MonoText.kt | 5 +- .../{model/Helper.kt => OnUserLeaveHint.kt} | 5 +- .../com/m3u/ui/{components => }/Player.kt | 4 +- ui/src/main/java/com/m3u/ui/ResumeEvent.kt | 5 ++ .../main/java/com/m3u/ui/ScrollableState.kt | 9 ++ 119 files changed, 745 insertions(+), 511 deletions(-) rename core/src/main/java/com/m3u/core/util/compose/{observableStateOf.kt => ObservableState.kt} (66%) create mode 100644 core/src/main/java/com/m3u/core/wrapper/Stored.kt rename data/src/main/java/com/m3u/data/api/dto/{ => github}/Asset.kt (95%) rename data/src/main/java/com/m3u/data/api/dto/{ => github}/File.kt (95%) rename data/src/main/java/com/m3u/data/api/dto/{ => github}/Leaf.kt (84%) rename data/src/main/java/com/m3u/data/api/dto/{ => github}/Links.kt (87%) rename data/src/main/java/com/m3u/data/api/dto/{ => github}/Release.kt (96%) rename data/src/main/java/com/m3u/data/api/dto/{ => github}/Tree.kt (86%) rename data/src/main/java/com/m3u/data/api/dto/{ => github}/User.kt (97%) create mode 100644 data/src/main/java/com/m3u/data/repository/CloudRepository.kt rename data/src/main/java/com/m3u/data/repository/{Repository.kt => ReadOnlyRepository.kt} (56%) create mode 100644 data/src/main/java/com/m3u/data/repository/impl/DropboxCloudRepository.kt create mode 100644 jitpack.yml create mode 100644 material/.gitignore create mode 100644 material/build.gradle.kts create mode 100644 material/consumer-rules.pro create mode 100644 material/proguard-rules.pro create mode 100644 material/src/main/AndroidManifest.xml rename ui/src/main/java/com/m3u/ui/components/AppBar.kt => material/src/main/java/com/m3u/material/components/AppBars.kt (96%) rename ui/src/main/java/com/m3u/ui/components/Background.kt => material/src/main/java/com/m3u/material/components/Backgrounds.kt (87%) rename ui/src/main/java/com/m3u/ui/components/Badge.kt => material/src/main/java/com/m3u/material/components/Badges.kt (90%) rename ui/src/main/java/com/m3u/ui/components/Brush.kt => material/src/main/java/com/m3u/material/components/Brushes.kt (97%) rename ui/src/main/java/com/m3u/ui/components/Button.kt => material/src/main/java/com/m3u/material/components/Buttons.kt (98%) rename ui/src/main/java/com/m3u/ui/components/Dialog.kt => material/src/main/java/com/m3u/material/components/Dialogs.kt (97%) rename ui/src/main/java/com/m3u/ui/components/Image.kt => material/src/main/java/com/m3u/material/components/Images.kt (92%) rename ui/src/main/java/com/m3u/ui/components/CircularProgressIndicator.kt => material/src/main/java/com/m3u/material/components/Indicators.kt (98%) rename ui/src/main/java/com/m3u/ui/components/Layout.kt => material/src/main/java/com/m3u/material/components/Layouts.kt (96%) rename ui/src/main/java/com/m3u/ui/components/Lottie.kt => material/src/main/java/com/m3u/material/components/Lotties.kt (97%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/components/Mask.kt (98%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/components/NavigationSheet.kt (96%) rename {features/setting/src/main/java/com/m3u/features/setting => material/src/main/java/com/m3u/material}/components/Preferences.kt (93%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/components/Selection.kt (94%) rename ui/src/main/java/com/m3u/ui/components/TextField.kt => material/src/main/java/com/m3u/material/components/TextFields.kt (98%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/components/ThemeSelection.kt (92%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/ktx/Animations.kt (94%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/ktx/Blurs.kt (99%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/ktx/Effects.kt (90%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/ktx/Interaction.kt (96%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/ktx/InterceptEvent.kt (98%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/ktx/LifecycleEffect.kt (98%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/ktx/ScrollableState.kt (74%) rename {core/src/main/java/com/m3u/core/util/compose => material/src/main/java/com/m3u/material/ktx}/Specified.kt (91%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/model/Duration.kt (89%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/model/GradientColors.kt (92%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/model/Scalable.kt (95%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/model/Spacing.kt (93%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/model/StepColor.kt (83%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/model/SugarColors.kt (94%) rename {ui/src/main/java/com/m3u/ui => material/src/main/java/com/m3u/material}/model/Theme.kt (99%) rename ui/src/main/java/com/m3u/ui/{model => }/AppFont.kt (97%) rename ui/src/main/java/com/m3u/ui/{ktx => }/EventHandler.kt (96%) rename ui/src/main/java/com/m3u/ui/{LocalProvider.kt => M3ULocalProvider.kt} (82%) rename ui/src/main/java/com/m3u/ui/{components => }/MonoText.kt (95%) rename ui/src/main/java/com/m3u/ui/{model/Helper.kt => OnUserLeaveHint.kt} (98%) rename ui/src/main/java/com/m3u/ui/{components => }/Player.kt (98%) create mode 100644 ui/src/main/java/com/m3u/ui/ResumeEvent.kt create mode 100644 ui/src/main/java/com/m3u/ui/ScrollableState.kt diff --git a/androidApp/src/main/java/com/m3u/androidApp/MainActivity.kt b/androidApp/src/main/java/com/m3u/androidApp/MainActivity.kt index 5aacd8d71..fbf5922ad 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/MainActivity.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/MainActivity.kt @@ -24,11 +24,11 @@ import com.m3u.core.unspecified.UBoolean import com.m3u.core.util.basic.rational import com.m3u.core.util.context.isDarkMode import com.m3u.core.util.context.isPortraitMode -import com.m3u.ui.model.Action -import com.m3u.ui.model.Fob -import com.m3u.ui.model.Helper -import com.m3u.ui.model.OnPipModeChanged -import com.m3u.ui.model.OnUserLeaveHint +import com.m3u.ui.Action +import com.m3u.ui.Fob +import com.m3u.ui.Helper +import com.m3u.ui.OnPipModeChanged +import com.m3u.ui.OnUserLeaveHint import dagger.hilt.android.AndroidEntryPoint import kotlin.reflect.KMutableProperty0 diff --git a/androidApp/src/main/java/com/m3u/androidApp/components/AppBottomSheet.kt b/androidApp/src/main/java/com/m3u/androidApp/components/AppBottomSheet.kt index a3aec30ec..8c38eba90 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/components/AppBottomSheet.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/components/AppBottomSheet.kt @@ -20,13 +20,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import com.m3u.core.util.basic.title +import com.m3u.material.components.NavigationSheet +import com.m3u.material.ktx.animateColor +import com.m3u.material.ktx.animated +import com.m3u.material.model.LocalTheme import com.m3u.ui.Destination +import com.m3u.ui.Fob import com.m3u.ui.Navigate -import com.m3u.ui.components.NavigationSheet -import com.m3u.ui.ktx.animateColor -import com.m3u.ui.ktx.animated -import com.m3u.ui.model.Fob -import com.m3u.ui.model.LocalTheme @Composable fun AppBottomSheet( diff --git a/androidApp/src/main/java/com/m3u/androidApp/components/AppSnackHost.kt b/androidApp/src/main/java/com/m3u/androidApp/components/AppSnackHost.kt index fcb32fe35..19da0548b 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/components/AppSnackHost.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/components/AppSnackHost.kt @@ -13,8 +13,8 @@ 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.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme @Composable fun AppSnackHost( diff --git a/androidApp/src/main/java/com/m3u/androidApp/navigation/M3UNavHost.kt b/androidApp/src/main/java/com/m3u/androidApp/navigation/M3UNavHost.kt index 09c2ecb26..f89edf799 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/navigation/M3UNavHost.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/navigation/M3UNavHost.kt @@ -14,10 +14,10 @@ import com.m3u.features.console.navigation.consoleScreen import com.m3u.features.feed.navigation.feedScreen import com.m3u.features.live.navigation.livePlaylistScreen import com.m3u.features.live.navigation.liveScreen +import com.m3u.i18n.R.string import com.m3u.ui.Destination +import com.m3u.ui.LocalHelper import com.m3u.ui.Navigate -import com.m3u.ui.model.LocalHelper -import com.m3u.i18n.R as I18R @Composable fun M3UNavHost( @@ -42,7 +42,7 @@ fun M3UNavHost( onCurrentPage = onCurrentPage, navigateToFeed = { feed -> helper.title = feed.title.ifEmpty { - if (feed.local) context.getString(I18R.string.feat_main_imported_feed_title) + if (feed.local) context.getString(string.feat_main_imported_feed_title) else "" } navigate(Destination.Feed(feed.url)) diff --git a/androidApp/src/main/java/com/m3u/androidApp/navigation/RootGraph.kt b/androidApp/src/main/java/com/m3u/androidApp/navigation/RootGraph.kt index c6e17152c..d2b7e619e 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/navigation/RootGraph.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/navigation/RootGraph.kt @@ -8,12 +8,15 @@ import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import com.m3u.core.wrapper.Event +import com.m3u.core.wrapper.eventOf import com.m3u.features.favorite.FavouriteRoute import com.m3u.features.favorite.NavigateToLive import com.m3u.features.main.MainRoute @@ -21,10 +24,11 @@ import com.m3u.features.main.NavigateToFeed import com.m3u.features.setting.NavigateToAbout import com.m3u.features.setting.NavigateToConsole import com.m3u.features.setting.SettingRoute +import com.m3u.material.ktx.Edge +import com.m3u.material.ktx.blurEdges +import com.m3u.material.model.LocalTheme import com.m3u.ui.Destination -import com.m3u.ui.ktx.Edge -import com.m3u.ui.ktx.blurEdges -import com.m3u.ui.model.LocalTheme +import com.m3u.ui.ResumeEvent import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -112,7 +116,7 @@ private fun RootGraph( Destination.Root.Main -> { MainRoute( navigateToFeed = navigateToFeed, - isCurrentPage = currentPage == pagerIndex, + resume = rememberResumeEvent(currentPage, pagerIndex), modifier = Modifier.fillMaxSize() ) } @@ -120,7 +124,7 @@ private fun RootGraph( Destination.Root.Favourite -> { FavouriteRoute( navigateToLive = navigateToLive, - isCurrentPage = currentPage == pagerIndex, + resume = rememberResumeEvent(currentPage, pagerIndex), modifier = Modifier.fillMaxSize() ) } @@ -129,7 +133,7 @@ private fun RootGraph( SettingRoute( navigateToConsole = navigateToConsole, navigateToAbout = navigateToAbout, - isCurrentPage = currentPage == pagerIndex, + resume = rememberResumeEvent(currentPage, pagerIndex), modifier = Modifier.fillMaxSize() ) } @@ -142,4 +146,11 @@ private data class PagerStateSnapshot( val target: Int, val settled: Int, val scrolling: Boolean -) \ No newline at end of file +) + +@Composable +private fun rememberResumeEvent(currentPage: Int, targetPage: Int): ResumeEvent = + remember(currentPage, targetPage) { + if (currentPage == targetPage) eventOf(Unit) + else Event.Handled() + } diff --git a/androidApp/src/main/java/com/m3u/androidApp/ui/App.kt b/androidApp/src/main/java/com/m3u/androidApp/ui/App.kt index cd3dcfeb1..550368941 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/ui/App.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/ui/App.kt @@ -7,9 +7,9 @@ import androidx.compose.ui.Modifier import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.m3u.androidApp.navigation.M3UNavHost -import com.m3u.ui.ktx.EventHandler -import com.m3u.ui.model.EmptyHelper -import com.m3u.ui.model.Helper +import com.m3u.ui.EmptyHelper +import com.m3u.ui.EventHandler +import com.m3u.ui.Helper @Composable fun App( diff --git a/androidApp/src/main/java/com/m3u/androidApp/ui/AppDefaults.kt b/androidApp/src/main/java/com/m3u/androidApp/ui/AppDefaults.kt index c1e0392b5..8b0b27166 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/ui/AppDefaults.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/ui/AppDefaults.kt @@ -9,11 +9,11 @@ import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.navigation.NavDestination import com.m3u.core.util.basic.title +import com.m3u.material.model.ABlackTheme +import com.m3u.material.model.DayTheme +import com.m3u.material.model.NightTheme +import com.m3u.material.model.Theme import com.m3u.ui.Destination -import com.m3u.ui.model.ABlackTheme -import com.m3u.ui.model.DayTheme -import com.m3u.ui.model.NightTheme -import com.m3u.ui.model.Theme object AppDefaults { @Composable diff --git a/androidApp/src/main/java/com/m3u/androidApp/ui/AppScaffold.kt b/androidApp/src/main/java/com/m3u/androidApp/ui/AppScaffold.kt index 5a8a31f1d..7b79513cf 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/ui/AppScaffold.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/ui/AppScaffold.kt @@ -16,19 +16,21 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import com.m3u.androidApp.components.AppBottomSheet import com.m3u.androidApp.components.AppSnackHost import com.m3u.core.util.withEach +import com.m3u.i18n.R.string +import com.m3u.material.components.AppTopBar +import com.m3u.material.components.IconButton +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.Theme +import com.m3u.ui.Action import com.m3u.ui.Destination +import com.m3u.ui.Fob +import com.m3u.ui.Helper import com.m3u.ui.M3ULocalProvider import com.m3u.ui.Navigate -import com.m3u.ui.components.AppTopBar -import com.m3u.ui.components.IconButton -import com.m3u.ui.model.Action -import com.m3u.ui.model.Fob -import com.m3u.ui.model.Helper -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.Theme import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.milliseconds @@ -89,6 +91,7 @@ internal fun AppScaffold( } }, onBackPressed = onBackPressed, + onBackPressedContentDescription = stringResource(string.ui_cd_top_bar_on_back_pressed), modifier = modifier ) { padding -> Column( diff --git a/androidApp/src/main/java/com/m3u/androidApp/ui/AppViewModel.kt b/androidApp/src/main/java/com/m3u/androidApp/ui/AppViewModel.kt index f3ccc6ae6..3f3fae19d 100644 --- a/androidApp/src/main/java/com/m3u/androidApp/ui/AppViewModel.kt +++ b/androidApp/src/main/java/com/m3u/androidApp/ui/AppViewModel.kt @@ -10,9 +10,9 @@ import com.m3u.core.wrapper.Event import com.m3u.core.wrapper.eventOf import com.m3u.core.wrapper.handledEvent import com.m3u.data.service.UiService +import com.m3u.ui.Action import com.m3u.ui.Destination -import com.m3u.ui.model.Action -import com.m3u.ui.model.Fob +import com.m3u.ui.Fob import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update diff --git a/core/src/main/java/com/m3u/core/unspecified/UBoolean.kt b/core/src/main/java/com/m3u/core/unspecified/UBoolean.kt index 8a53485c5..864d17334 100644 --- a/core/src/main/java/com/m3u/core/unspecified/UBoolean.kt +++ b/core/src/main/java/com/m3u/core/unspecified/UBoolean.kt @@ -4,9 +4,26 @@ enum class UBoolean { True, False, Unspecified } -val Boolean.u: UBoolean get() = if (this) UBoolean.True else UBoolean.False +@Deprecated( + "Replace with Boolean.unspecifiable", + replaceWith = ReplaceWith( + "this.unspecifiable", + "com.m3u.core.unspecified.unspecifiable" + ) +) +val Boolean.u: UBoolean get() = unspecifiable +val Boolean.unspecifiable: UBoolean get() = if (this) UBoolean.True else UBoolean.False -val UBoolean.actual: Boolean? +@Deprecated( + "Replace with UBoolean.specified", + replaceWith = ReplaceWith( + "this.specified", + "com.m3u.core.unspecified.specified" + ) +) +val UBoolean.actual: Boolean? get() = specified + +val UBoolean.specified: Boolean? get() = when (this) { UBoolean.True -> true UBoolean.False -> false diff --git a/core/src/main/java/com/m3u/core/util/Collections.kt b/core/src/main/java/com/m3u/core/util/Collections.kt index ae991f6e0..d4a57a911 100644 --- a/core/src/main/java/com/m3u/core/util/Collections.kt +++ b/core/src/main/java/com/m3u/core/util/Collections.kt @@ -118,4 +118,6 @@ inline fun List.indexOf(start: Int = 0, predicate: (E) -> Boolean): Int { index++ } return -1 -} \ No newline at end of file +} + +inline fun Iterable.contains(predicate: (E) -> Boolean): Boolean = find(predicate) != null \ No newline at end of file diff --git a/core/src/main/java/com/m3u/core/util/Files.kt b/core/src/main/java/com/m3u/core/util/Files.kt index 01e638fb5..fff383fd7 100644 --- a/core/src/main/java/com/m3u/core/util/Files.kt +++ b/core/src/main/java/com/m3u/core/util/Files.kt @@ -7,7 +7,8 @@ import androidx.core.net.toFile fun Uri.readContentFilename( contentResolver: ContentResolver -): String? = when (scheme) { +): String? = if (this == Uri.EMPTY) null +else when (scheme) { ContentResolver.SCHEME_FILE -> toFile().name ContentResolver.SCHEME_CONTENT -> { contentResolver.query( @@ -25,20 +26,17 @@ fun Uri.readContentFilename( } else null } } - else -> null } fun Uri.readContentText( contentResolver: ContentResolver -): String? { - return when (scheme) { - ContentResolver.SCHEME_FILE -> toFile().readText() - ContentResolver.SCHEME_CONTENT -> - contentResolver.openInputStream(this)?.use { - it.bufferedReader().readText() - } - - else -> null - } +): String? = if (this == Uri.EMPTY) null +else when (scheme) { + ContentResolver.SCHEME_FILE -> toFile().readText() + ContentResolver.SCHEME_CONTENT -> + contentResolver.openInputStream(this)?.use { + it.bufferedReader().readText() + } + else -> null } \ No newline at end of file diff --git a/core/src/main/java/com/m3u/core/util/compose/observableStateOf.kt b/core/src/main/java/com/m3u/core/util/compose/ObservableState.kt similarity index 66% rename from core/src/main/java/com/m3u/core/util/compose/observableStateOf.kt rename to core/src/main/java/com/m3u/core/util/compose/ObservableState.kt index b2add4da5..0fac3d8bd 100644 --- a/core/src/main/java/com/m3u/core/util/compose/observableStateOf.kt +++ b/core/src/main/java/com/m3u/core/util/compose/ObservableState.kt @@ -4,25 +4,32 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.StateFactoryMarker +// TODO: Move to material/ui module. +@StateFactoryMarker +fun observableStateOf( + delegate: MutableState, + onChanged: (T) -> Unit +): MutableState { + return ObservableState(delegate, onChanged) +} + @StateFactoryMarker fun observableStateOf( value: T, onChanged: (T) -> Unit ): MutableState { - val delegate = mutableStateOf(value) - return ObservableState(value, delegate, onChanged) + return ObservableState(mutableStateOf(value), onChanged) } private class ObservableState( - defaultValue: T, private val delegate: MutableState, private val onChanged: (T) -> Unit ) : MutableState { - override var value: T = defaultValue + override var value: T get() = delegate.value set(value) { - field = value onChanged(value) + delegate.value = value } override fun component1(): T = delegate.component1() diff --git a/core/src/main/java/com/m3u/core/util/context/SharedPreferences.kt b/core/src/main/java/com/m3u/core/util/context/SharedPreferences.kt index 7479c859c..901c1f55f 100644 --- a/core/src/main/java/com/m3u/core/util/context/SharedPreferences.kt +++ b/core/src/main/java/com/m3u/core/util/context/SharedPreferences.kt @@ -5,6 +5,7 @@ package com.m3u.core.util.context import android.content.SharedPreferences import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf +import com.m3u.core.util.compose.observableStateOf import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -76,23 +77,7 @@ private fun SharedPreferences.delegateAsState( defaultValue: T, getter: SharedPreferences.(String, T) -> T, setter: SharedPreferences.Editor.(String, T) -> SharedPreferences.Editor -): MutableState = StateDelegator( +): MutableState = observableStateOf( delegate = mutableStateOf(getter(key, defaultValue)), - onUpdate = { edit().setter(key, it).apply() }, + onChanged = { edit().setter(key, it).apply() } ) - -private class StateDelegator( - private val delegate: MutableState, - private val onUpdate: (T) -> Unit -) : MutableState { - override var value: T - get() = delegate.value - set(value) { - onUpdate(value) - delegate.value = value - } - - override fun component1(): T = delegate.component1() - - override fun component2(): (T) -> Unit = delegate.component2() -} \ No newline at end of file diff --git a/core/src/main/java/com/m3u/core/wrapper/Event.kt b/core/src/main/java/com/m3u/core/wrapper/Event.kt index 39bdbca9b..3a2651686 100644 --- a/core/src/main/java/com/m3u/core/wrapper/Event.kt +++ b/core/src/main/java/com/m3u/core/wrapper/Event.kt @@ -1,6 +1,9 @@ package com.m3u.core.wrapper +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +@Stable sealed class Event private constructor( private val data: T? = null ) { @@ -25,6 +28,7 @@ sealed class Event private constructor( /** * Event which cannot be consumed */ + @Immutable class Handled : Event() { override var isHandled: Boolean = true } @@ -34,6 +38,7 @@ sealed class Event private constructor( * @see eventOf * @hide */ + @Stable class Regular(data: T) : Event(data) } diff --git a/core/src/main/java/com/m3u/core/wrapper/Resource.kt b/core/src/main/java/com/m3u/core/wrapper/Resource.kt index 06bb0b5f1..ea1dca1c1 100644 --- a/core/src/main/java/com/m3u/core/wrapper/Resource.kt +++ b/core/src/main/java/com/m3u/core/wrapper/Resource.kt @@ -2,6 +2,8 @@ package com.m3u.core.wrapper +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.channelFlow @@ -9,11 +11,15 @@ import kotlinx.coroutines.flow.flow import kotlin.experimental.ExperimentalTypeInference sealed class Resource { + @Immutable data object Loading : Resource() + + @Stable data class Success( val data: T ) : Resource() + @Stable data class Failure( val message: String? ) : Resource() diff --git a/core/src/main/java/com/m3u/core/wrapper/Stored.kt b/core/src/main/java/com/m3u/core/wrapper/Stored.kt new file mode 100644 index 000000000..00e48aec1 --- /dev/null +++ b/core/src/main/java/com/m3u/core/wrapper/Stored.kt @@ -0,0 +1,6 @@ +package com.m3u.core.wrapper + +interface Stored { + fun R.restore(): T + fun store(): R +} diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 74090f5b6..d327d9779 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { ksp(libs.androidx.room.room.compiler) implementation(libs.org.jetbrains.kotlinx.kotlinx.serialization.json) + implementation(libs.org.jetbrains.kotlinx.kotlinx.datetime) implementation(libs.io.coil.kt.coil) diff --git a/data/src/main/java/com/m3u/data/api/DropboxApi.kt b/data/src/main/java/com/m3u/data/api/DropboxApi.kt index dc1838883..f4b3a9e10 100644 --- a/data/src/main/java/com/m3u/data/api/DropboxApi.kt +++ b/data/src/main/java/com/m3u/data/api/DropboxApi.kt @@ -1,4 +1,9 @@ package com.m3u.data.api -interface DropboxApi { +import retrofit2.http.POST + +internal interface DropboxApi { + // limitation: 150MB + @POST("files/upload") + suspend fun upload() } \ No newline at end of file diff --git a/data/src/main/java/com/m3u/data/api/GithubApi.kt b/data/src/main/java/com/m3u/data/api/GithubApi.kt index 2f05dee52..c870a936e 100644 --- a/data/src/main/java/com/m3u/data/api/GithubApi.kt +++ b/data/src/main/java/com/m3u/data/api/GithubApi.kt @@ -2,10 +2,10 @@ package com.m3u.data.api -import com.m3u.data.api.dto.File -import com.m3u.data.api.dto.Release -import com.m3u.data.api.dto.Tree -import com.m3u.data.api.dto.User +import com.m3u.data.api.dto.github.File +import com.m3u.data.api.dto.github.Release +import com.m3u.data.api.dto.github.Tree +import com.m3u.data.api.dto.github.User import retrofit2.http.GET import retrofit2.http.Path diff --git a/data/src/main/java/com/m3u/data/api/dto/Asset.kt b/data/src/main/java/com/m3u/data/api/dto/github/Asset.kt similarity index 95% rename from data/src/main/java/com/m3u/data/api/dto/Asset.kt rename to data/src/main/java/com/m3u/data/api/dto/github/Asset.kt index 3dad57906..56f69c9ef 100644 --- a/data/src/main/java/com/m3u/data/api/dto/Asset.kt +++ b/data/src/main/java/com/m3u/data/api/dto/github/Asset.kt @@ -1,4 +1,4 @@ -package com.m3u.data.api.dto +package com.m3u.data.api.dto.github import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/data/src/main/java/com/m3u/data/api/dto/File.kt b/data/src/main/java/com/m3u/data/api/dto/github/File.kt similarity index 95% rename from data/src/main/java/com/m3u/data/api/dto/File.kt rename to data/src/main/java/com/m3u/data/api/dto/github/File.kt index 6ecec0afd..352c3a553 100644 --- a/data/src/main/java/com/m3u/data/api/dto/File.kt +++ b/data/src/main/java/com/m3u/data/api/dto/github/File.kt @@ -1,6 +1,6 @@ @file:Suppress("unused") -package com.m3u.data.api.dto +package com.m3u.data.api.dto.github import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/data/src/main/java/com/m3u/data/api/dto/Leaf.kt b/data/src/main/java/com/m3u/data/api/dto/github/Leaf.kt similarity index 84% rename from data/src/main/java/com/m3u/data/api/dto/Leaf.kt rename to data/src/main/java/com/m3u/data/api/dto/github/Leaf.kt index 3f9721023..dd7ebf1dc 100644 --- a/data/src/main/java/com/m3u/data/api/dto/Leaf.kt +++ b/data/src/main/java/com/m3u/data/api/dto/github/Leaf.kt @@ -1,4 +1,4 @@ -package com.m3u.data.api.dto +package com.m3u.data.api.dto.github import kotlinx.serialization.Serializable diff --git a/data/src/main/java/com/m3u/data/api/dto/Links.kt b/data/src/main/java/com/m3u/data/api/dto/github/Links.kt similarity index 87% rename from data/src/main/java/com/m3u/data/api/dto/Links.kt rename to data/src/main/java/com/m3u/data/api/dto/github/Links.kt index bd680b782..bc398e39f 100644 --- a/data/src/main/java/com/m3u/data/api/dto/Links.kt +++ b/data/src/main/java/com/m3u/data/api/dto/github/Links.kt @@ -1,4 +1,4 @@ -package com.m3u.data.api.dto +package com.m3u.data.api.dto.github import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/data/src/main/java/com/m3u/data/api/dto/Release.kt b/data/src/main/java/com/m3u/data/api/dto/github/Release.kt similarity index 96% rename from data/src/main/java/com/m3u/data/api/dto/Release.kt rename to data/src/main/java/com/m3u/data/api/dto/github/Release.kt index caac2df3f..82d64102d 100644 --- a/data/src/main/java/com/m3u/data/api/dto/Release.kt +++ b/data/src/main/java/com/m3u/data/api/dto/github/Release.kt @@ -1,4 +1,4 @@ -package com.m3u.data.api.dto +package com.m3u.data.api.dto.github import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/data/src/main/java/com/m3u/data/api/dto/Tree.kt b/data/src/main/java/com/m3u/data/api/dto/github/Tree.kt similarity index 86% rename from data/src/main/java/com/m3u/data/api/dto/Tree.kt rename to data/src/main/java/com/m3u/data/api/dto/github/Tree.kt index 551867745..78b67d751 100644 --- a/data/src/main/java/com/m3u/data/api/dto/Tree.kt +++ b/data/src/main/java/com/m3u/data/api/dto/github/Tree.kt @@ -1,4 +1,4 @@ -package com.m3u.data.api.dto +package com.m3u.data.api.dto.github import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/data/src/main/java/com/m3u/data/api/dto/User.kt b/data/src/main/java/com/m3u/data/api/dto/github/User.kt similarity index 97% rename from data/src/main/java/com/m3u/data/api/dto/User.kt rename to data/src/main/java/com/m3u/data/api/dto/github/User.kt index d16513be0..fe2ccf6a8 100644 --- a/data/src/main/java/com/m3u/data/api/dto/User.kt +++ b/data/src/main/java/com/m3u/data/api/dto/github/User.kt @@ -1,4 +1,4 @@ -package com.m3u.data.api.dto +package com.m3u.data.api.dto.github import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/data/src/main/java/com/m3u/data/di/ApiModule.kt b/data/src/main/java/com/m3u/data/di/ApiModule.kt index fff1f13ef..a4987488f 100644 --- a/data/src/main/java/com/m3u/data/di/ApiModule.kt +++ b/data/src/main/java/com/m3u/data/di/ApiModule.kt @@ -22,7 +22,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object ApiModule { +internal object ApiModule { @Provides @Singleton fun provideOkhttpClient( diff --git a/data/src/main/java/com/m3u/data/repository/CloudRepository.kt b/data/src/main/java/com/m3u/data/repository/CloudRepository.kt new file mode 100644 index 000000000..9e46d7b13 --- /dev/null +++ b/data/src/main/java/com/m3u/data/repository/CloudRepository.kt @@ -0,0 +1,6 @@ +package com.m3u.data.repository + +import com.m3u.core.wrapper.Stored + +interface CloudRepository, R> : ReadWriteRepository + diff --git a/data/src/main/java/com/m3u/data/repository/FeedRepository.kt b/data/src/main/java/com/m3u/data/repository/FeedRepository.kt index cf1e2ce2e..8b95bde5a 100644 --- a/data/src/main/java/com/m3u/data/repository/FeedRepository.kt +++ b/data/src/main/java/com/m3u/data/repository/FeedRepository.kt @@ -9,9 +9,10 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") -interface FeedRepository : Repository { +interface FeedRepository : ReadOnlyRepository { override fun observe(url: String): Flow override suspend fun get(url: String): Feed? + fun subscribe( title: String, url: String, diff --git a/data/src/main/java/com/m3u/data/repository/LiveRepository.kt b/data/src/main/java/com/m3u/data/repository/LiveRepository.kt index 211114845..1dfa9d483 100644 --- a/data/src/main/java/com/m3u/data/repository/LiveRepository.kt +++ b/data/src/main/java/com/m3u/data/repository/LiveRepository.kt @@ -2,7 +2,7 @@ package com.m3u.data.repository import com.m3u.data.database.entity.Live -interface LiveRepository : Repository { +interface LiveRepository : ReadOnlyRepository { suspend fun getByUrl(url: String): Live? suspend fun getByFeedUrl(feedUrl: String): List suspend fun setFavourite(id: Int, target: Boolean) diff --git a/data/src/main/java/com/m3u/data/repository/Repository.kt b/data/src/main/java/com/m3u/data/repository/ReadOnlyRepository.kt similarity index 56% rename from data/src/main/java/com/m3u/data/repository/Repository.kt rename to data/src/main/java/com/m3u/data/repository/ReadOnlyRepository.kt index f81516bd9..391c9008a 100644 --- a/data/src/main/java/com/m3u/data/repository/Repository.kt +++ b/data/src/main/java/com/m3u/data/repository/ReadOnlyRepository.kt @@ -4,14 +4,20 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map -interface Repository { +interface ReadOnlyRepository { fun observe(id: ID): Flow fun observeAll(): Flow> suspend fun get(id: ID): T? } -inline fun Repository.observeAll( +inline fun ReadOnlyRepository.observeAll( crossinline predicate: (T) -> Boolean ): Flow> = observeAll() .map { it.filter(predicate) } - .distinctUntilChanged() \ No newline at end of file + .distinctUntilChanged() + +interface ReadWriteRepository : ReadOnlyRepository { + suspend fun save(e: T) + suspend fun delete(e: T) + suspend fun deleteAll() +} diff --git a/data/src/main/java/com/m3u/data/repository/impl/DropboxCloudRepository.kt b/data/src/main/java/com/m3u/data/repository/impl/DropboxCloudRepository.kt new file mode 100644 index 000000000..55ee87bbd --- /dev/null +++ b/data/src/main/java/com/m3u/data/repository/impl/DropboxCloudRepository.kt @@ -0,0 +1,76 @@ +package com.m3u.data.repository.impl + +import com.m3u.core.architecture.Logger +import com.m3u.core.wrapper.Stored +import com.m3u.data.api.DropboxApi +import com.m3u.data.repository.CloudRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn +import java.io.File +import java.io.InputStream +import javax.inject.Inject + +internal data class DropboxCloud( + val input: InputStream, + val id: Int +) : Stored { + override fun File.restore(): DropboxCloud = DropboxCloud( + input = inputStream(), + id = id + ) + + override fun store(): File { + TODO() + } +} + + +internal class DropboxCloudRepository @Inject constructor( + private val api: DropboxApi, + private val logger: Logger, + private val socket: Any, + coroutineScope: CoroutineScope +) : CloudRepository { + private val source = MutableSharedFlow>() + + private val state = source.stateIn( + scope = coroutineScope, + started = SharingStarted.Lazily, + initialValue = emptyList() + ) + + override fun observe(id: Int): Flow = channelFlow { + source + .mapNotNull { all -> all.find { it.id == id } } + .onEach(::send) + .collect() + } + .flowOn(Dispatchers.IO) + + override fun observeAll(): Flow> = source.asSharedFlow() + + override suspend fun get(id: Int): DropboxCloud? = state.value.find { it.id == id } + + override suspend fun save(e: DropboxCloud) { + + source.emit(state.value + e) + } + + override suspend fun delete(e: DropboxCloud) { + TODO("Not yet implemented") + } + + override suspend fun deleteAll() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/data/src/main/java/com/m3u/data/repository/impl/FeedRepositoryImpl.kt b/data/src/main/java/com/m3u/data/repository/impl/FeedRepositoryImpl.kt index f1a8efd76..3ab7a21d4 100644 --- a/data/src/main/java/com/m3u/data/repository/impl/FeedRepositoryImpl.kt +++ b/data/src/main/java/com/m3u/data/repository/impl/FeedRepositoryImpl.kt @@ -34,7 +34,7 @@ import java.io.File import java.io.FileNotFoundException import java.io.InputStream import javax.inject.Inject -import com.m3u.i18n.R as I18R +import com.m3u.i18n.R.string class FeedRepositoryImpl @Inject constructor( private val feedDao: FeedDao, @@ -124,7 +124,7 @@ class FeedRepositoryImpl @Inject constructor( liveDao.insert(live) emitResource(Unit) } catch (e: FileNotFoundException) { - error(context.getString(I18R.string.data_error_file_not_found)) + error(context.getString(string.data_error_file_not_found)) } catch (e: Exception) { logger.log(e) emitException(e) diff --git a/data/src/main/java/com/m3u/data/service/NotificationService.kt b/data/src/main/java/com/m3u/data/service/NotificationService.kt index da0ffa457..72af68335 100644 --- a/data/src/main/java/com/m3u/data/service/NotificationService.kt +++ b/data/src/main/java/com/m3u/data/service/NotificationService.kt @@ -5,6 +5,7 @@ import androidx.annotation.DrawableRes import androidx.annotation.IntDef import androidx.core.app.NotificationCompat +// TODO: combine to ui-service interface NotificationService { fun post( notificationId: Int, diff --git a/data/src/main/java/com/m3u/data/worker/SubscriptionInBackgroundWorker.kt b/data/src/main/java/com/m3u/data/worker/SubscriptionInBackgroundWorker.kt index bf09c633f..1cecdad86 100644 --- a/data/src/main/java/com/m3u/data/worker/SubscriptionInBackgroundWorker.kt +++ b/data/src/main/java/com/m3u/data/worker/SubscriptionInBackgroundWorker.kt @@ -12,12 +12,12 @@ import com.m3u.core.annotation.FeedStrategy import com.m3u.core.wrapper.ProgressResource import com.m3u.data.R import com.m3u.data.repository.FeedRepository +import com.m3u.i18n.R.string import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.cancel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.supervisorScope -import com.m3u.i18n.R as I18R @HiltWorker class SubscriptionInBackgroundWorker @AssistedInject constructor( @@ -34,7 +34,7 @@ class SubscriptionInBackgroundWorker @AssistedInject constructor( url ?: return@coroutineScope Result.failure() createChannel() if (title.isEmpty()) { - val message = context.getString(I18R.string.data_error_empty_title) + val message = context.getString(string.data_error_empty_title) val data = workDataOf("message" to message) failure(message) Result.failure(data) diff --git a/features/about/src/main/java/com/m3u/features/about/AboutScreen.kt b/features/about/src/main/java/com/m3u/features/about/AboutScreen.kt index 1b8222ed3..50bcf3945 100644 --- a/features/about/src/main/java/com/m3u/features/about/AboutScreen.kt +++ b/features/about/src/main/java/com/m3u/features/about/AboutScreen.kt @@ -14,12 +14,12 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.m3u.features.about.components.ContributorItem import com.m3u.features.about.model.Contributor -import com.m3u.ui.components.Background -import com.m3u.ui.components.MonoText -import com.m3u.ui.model.LocalHelper -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.repeatOnLifecycle -import com.m3u.i18n.R as I18R +import com.m3u.material.components.Background +import com.m3u.material.model.LocalSpacing +import com.m3u.i18n.R.string +import com.m3u.ui.LocalHelper +import com.m3u.ui.MonoText +import com.m3u.ui.repeatOnLifecycle @Composable internal fun AboutRoute( @@ -27,7 +27,7 @@ internal fun AboutRoute( viewModel: AboutViewModel = hiltViewModel() ) { val helper = LocalHelper.current - val title = stringResource(I18R.string.feat_about_title) + val title = stringResource(string.feat_about_title) helper.repeatOnLifecycle { this.title = title } diff --git a/features/about/src/main/java/com/m3u/features/about/components/ContributorItem.kt b/features/about/src/main/java/com/m3u/features/about/components/ContributorItem.kt index c895c2a75..fa0b3e177 100644 --- a/features/about/src/main/java/com/m3u/features/about/components/ContributorItem.kt +++ b/features/about/src/main/java/com/m3u/features/about/components/ContributorItem.kt @@ -18,8 +18,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import com.m3u.features.about.model.Contributor -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme @Composable @OptIn(ExperimentalMaterialApi::class) diff --git a/features/about/src/main/java/com/m3u/features/about/model/Contributor.kt b/features/about/src/main/java/com/m3u/features/about/model/Contributor.kt index 8588c6e99..7322bde0f 100644 --- a/features/about/src/main/java/com/m3u/features/about/model/Contributor.kt +++ b/features/about/src/main/java/com/m3u/features/about/model/Contributor.kt @@ -1,7 +1,7 @@ package com.m3u.features.about.model import androidx.compose.runtime.Immutable -import com.m3u.data.api.dto.User +import com.m3u.data.api.dto.github.User @Immutable internal data class Contributor( diff --git a/features/console/src/main/java/com/m3u/features/console/ConsoleScreen.kt b/features/console/src/main/java/com/m3u/features/console/ConsoleScreen.kt index 4ebddde06..862d40b37 100644 --- a/features/console/src/main/java/com/m3u/features/console/ConsoleScreen.kt +++ b/features/console/src/main/java/com/m3u/features/console/ConsoleScreen.kt @@ -24,13 +24,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.m3u.ui.components.Background -import com.m3u.ui.components.MonoText -import com.m3u.ui.components.TextField -import com.m3u.ui.model.LocalHelper -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.repeatOnLifecycle -import com.m3u.i18n.R as I18R +import com.m3u.material.components.Background +import com.m3u.material.components.TextField +import com.m3u.material.model.LocalSpacing +import com.m3u.i18n.R.string +import com.m3u.ui.LocalHelper +import com.m3u.ui.MonoText +import com.m3u.ui.repeatOnLifecycle @Composable internal fun ConsoleRoute( @@ -38,7 +38,7 @@ internal fun ConsoleRoute( viewModel: ConsoleViewModel = hiltViewModel() ) { val helper = LocalHelper.current - val title = stringResource(I18R.string.feat_console_title) + val title = stringResource(string.feat_console_title) helper.repeatOnLifecycle { this.title = title } diff --git a/features/console/src/main/java/com/m3u/features/console/MonoStyle.kt b/features/console/src/main/java/com/m3u/features/console/MonoStyle.kt index 9ff675e0a..602920272 100644 --- a/features/console/src/main/java/com/m3u/features/console/MonoStyle.kt +++ b/features/console/src/main/java/com/m3u/features/console/MonoStyle.kt @@ -3,7 +3,7 @@ package com.m3u.features.console import androidx.compose.material.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalTheme sealed class MonoStyle( val prefix: String diff --git a/features/crash/src/main/java/com/m3u/features/crash/components/FileItem.kt b/features/crash/src/main/java/com/m3u/features/crash/components/FileItem.kt index d056154ac..08ab3bfdc 100644 --- a/features/crash/src/main/java/com/m3u/features/crash/components/FileItem.kt +++ b/features/crash/src/main/java/com/m3u/features/crash/components/FileItem.kt @@ -10,8 +10,8 @@ import androidx.compose.material.icons.rounded.Adb import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow -import com.m3u.ui.components.Background -import com.m3u.ui.model.LocalTheme +import com.m3u.material.components.Background +import com.m3u.material.model.LocalTheme import java.io.File @OptIn(ExperimentalMaterialApi::class) diff --git a/features/crash/src/main/java/com/m3u/features/crash/screen/detail/DetailScreen.kt b/features/crash/src/main/java/com/m3u/features/crash/screen/detail/DetailScreen.kt index b9960207f..7965796fd 100644 --- a/features/crash/src/main/java/com/m3u/features/crash/screen/detail/DetailScreen.kt +++ b/features/crash/src/main/java/com/m3u/features/crash/screen/detail/DetailScreen.kt @@ -9,9 +9,9 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.m3u.ui.components.Background -import com.m3u.ui.components.MonoText -import com.m3u.ui.model.LocalSpacing +import com.m3u.material.components.Background +import com.m3u.material.model.LocalSpacing +import com.m3u.ui.MonoText @Composable internal fun DetailScreen( diff --git a/features/crash/src/main/java/com/m3u/features/crash/screen/list/ListScreen.kt b/features/crash/src/main/java/com/m3u/features/crash/screen/list/ListScreen.kt index 98ac0b7bc..7f14c3b86 100644 --- a/features/crash/src/main/java/com/m3u/features/crash/screen/list/ListScreen.kt +++ b/features/crash/src/main/java/com/m3u/features/crash/screen/list/ListScreen.kt @@ -11,7 +11,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.m3u.features.crash.components.FileItem import com.m3u.features.crash.screen.list.navigation.NavigateToDetail -import com.m3u.ui.components.Background +import com.m3u.material.components.Background @Composable internal fun ListScreen( diff --git a/features/favorite/src/main/java/com/m3u/features/favorite/FavoriteScreen.kt b/features/favorite/src/main/java/com/m3u/features/favorite/FavoriteScreen.kt index aafd8252e..6d1288e70 100644 --- a/features/favorite/src/main/java/com/m3u/features/favorite/FavoriteScreen.kt +++ b/features/favorite/src/main/java/com/m3u/features/favorite/FavoriteScreen.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells import androidx.compose.foundation.lazy.staggeredgrid.items import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -19,18 +18,20 @@ import androidx.compose.ui.platform.LocalConfiguration import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.m3u.features.favorite.components.FavoriteItem -import com.m3u.ui.ktx.interceptVolumeEvent -import com.m3u.ui.model.LocalHelper -import com.m3u.ui.model.LocalScalable -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.Scalable +import com.m3u.material.ktx.interceptVolumeEvent +import com.m3u.material.model.LocalScalable +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.Scalable +import com.m3u.ui.EventHandler +import com.m3u.ui.LocalHelper +import com.m3u.ui.ResumeEvent typealias NavigateToLive = (Int) -> Unit @Composable fun FavouriteRoute( navigateToLive: NavigateToLive, - isCurrentPage: Boolean, + resume: ResumeEvent, modifier: Modifier = Modifier, viewModel: FavouriteViewModel = hiltViewModel() ) { @@ -41,10 +42,8 @@ fun FavouriteRoute( fun onRowCount(target: Int) { state.rowCount = target } - LaunchedEffect(isCurrentPage) { - if (isCurrentPage) { - helper.actions = emptyList() - } + EventHandler(resume) { + helper.actions = emptyList() } val interceptVolumeEventModifier = remember(state.godMode) { diff --git a/features/favorite/src/main/java/com/m3u/features/favorite/components/FavoriteLiveItem.kt b/features/favorite/src/main/java/com/m3u/features/favorite/components/FavoriteLiveItem.kt index b2bcae2be..d51381397 100644 --- a/features/favorite/src/main/java/com/m3u/features/favorite/components/FavoriteLiveItem.kt +++ b/features/favorite/src/main/java/com/m3u/features/favorite/components/FavoriteLiveItem.kt @@ -25,14 +25,14 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import com.m3u.data.database.entity.Live -import com.m3u.ui.components.Image -import com.m3u.ui.components.TextBadge -import com.m3u.ui.model.LocalScalable -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme -import com.m3u.ui.ktx.animated +import com.m3u.material.components.Image +import com.m3u.material.components.TextBadge +import com.m3u.material.model.LocalScalable +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme +import com.m3u.material.ktx.animated import java.net.URI -import com.m3u.i18n.R as I18R +import com.m3u.i18n.R.string @OptIn(ExperimentalFoundationApi::class) @Composable @@ -52,7 +52,7 @@ internal fun FavoriteItem( val actualBackgroundColor by theme.surface.animated("FavoriteItemBackground") val actualContentColor by theme.onSurface.animated("FavoriteItemContent") val scheme = remember(live) { - URI(live.url).scheme ?: context.getString(I18R.string.feat_feed_scheme_unknown).uppercase() + URI(live.url).scheme ?: context.getString(string.feat_feed_scheme_unknown).uppercase() } Surface( shape = RoundedCornerShape(spacing.medium), diff --git a/features/feed/src/main/java/com/m3u/features/feed/FeedScreen.kt b/features/feed/src/main/java/com/m3u/features/feed/FeedScreen.kt index 5768f3b80..183271e4e 100644 --- a/features/feed/src/main/java/com/m3u/features/feed/FeedScreen.kt +++ b/features/feed/src/main/java/com/m3u/features/feed/FeedScreen.kt @@ -75,26 +75,27 @@ import com.m3u.data.database.entity.Live import com.m3u.features.feed.components.DialogStatus import com.m3u.features.feed.components.FeedDialog import com.m3u.features.feed.components.LiveItem -import com.m3u.ui.Destination -import com.m3u.ui.components.TextField -import com.m3u.ui.ktx.Edge -import com.m3u.ui.ktx.EventHandler -import com.m3u.ui.ktx.animateDp -import com.m3u.ui.ktx.blurEdges -import com.m3u.ui.ktx.interceptVolumeEvent -import com.m3u.ui.ktx.isAtTop -import com.m3u.ui.model.Action -import com.m3u.ui.model.Fob -import com.m3u.ui.model.LocalDuration -import com.m3u.ui.model.LocalHelper -import com.m3u.ui.model.LocalScalable -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme -import com.m3u.ui.model.Scalable -import com.m3u.ui.model.repeatOnLifecycle +import com.m3u.material.components.TextField +import com.m3u.material.ktx.Edge +import com.m3u.material.ktx.animateDp +import com.m3u.material.ktx.blurEdges +import com.m3u.material.ktx.interceptVolumeEvent +import com.m3u.material.ktx.isAtTop +import com.m3u.material.model.LocalDuration +import com.m3u.material.model.LocalScalable +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme +import com.m3u.material.model.Scalable import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import com.m3u.i18n.R as I18R +import com.m3u.i18n.R.string +import com.m3u.ui.Action +import com.m3u.ui.Destination +import com.m3u.ui.EventHandler +import com.m3u.ui.Fob +import com.m3u.ui.LocalHelper +import com.m3u.ui.isAtTop +import com.m3u.ui.repeatOnLifecycle internal typealias NavigateToLive = (liveId: Int) -> Unit internal typealias NavigateToPlaylist = (playlist: List, initial: Int) -> Unit @@ -260,7 +261,7 @@ private fun FeedScreen( onValueChange = onQuery, fontWeight = FontWeight.Bold, height = 32.dp, - placeholder = stringResource(I18R.string.feat_feed_query_placeholder).capitalize(Locale.current), + placeholder = stringResource(string.feat_feed_query_placeholder).capitalize(Locale.current), modifier = Modifier .padding(spacing.medium) .fillMaxWidth() diff --git a/features/feed/src/main/java/com/m3u/features/feed/FeedViewModel.kt b/features/feed/src/main/java/com/m3u/features/feed/FeedViewModel.kt index b0c3d45a1..027e6b6a5 100644 --- a/features/feed/src/main/java/com/m3u/features/feed/FeedViewModel.kt +++ b/features/feed/src/main/java/com/m3u/features/feed/FeedViewModel.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject -import com.m3u.i18n.R as I18R +import com.m3u.i18n.R.string @HiltViewModel class FeedViewModel @Inject constructor( @@ -55,7 +55,7 @@ class FeedViewModel @Inject constructor( private fun observe(feedUrl: String) { observeJob?.cancel() if (feedUrl.isEmpty()) { - val message = string(I18R.string.feat_feed_error_observe_feed, "") + val message = string(string.feat_feed_error_observe_feed, "") onMessage(message) return } @@ -75,7 +75,7 @@ class FeedViewModel @Inject constructor( ) } } else { - val message = string(I18R.string.feat_feed_error_observe_feed, feedUrl) + val message = string(string.feat_feed_error_observe_feed, feedUrl) onMessage(message) } } diff --git a/features/feed/src/main/java/com/m3u/features/feed/components/FeedDialog.kt b/features/feed/src/main/java/com/m3u/features/feed/components/FeedDialog.kt index 6d522858e..6325f2ba8 100644 --- a/features/feed/src/main/java/com/m3u/features/feed/components/FeedDialog.kt +++ b/features/feed/src/main/java/com/m3u/features/feed/components/FeedDialog.kt @@ -14,11 +14,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import com.m3u.data.database.entity.Live -import com.m3u.ui.components.DialogTextField -import com.m3u.ui.components.DialogItem -import com.m3u.ui.components.AppDialog -import com.m3u.ui.model.LocalSpacing -import com.m3u.i18n.R as I18R +import com.m3u.material.components.DialogTextField +import com.m3u.material.components.DialogItem +import com.m3u.material.components.AppDialog +import com.m3u.material.model.LocalSpacing +import com.m3u.i18n.R.string internal typealias OnUpdateDialogStatus = (DialogStatus) -> Unit internal typealias OnFavoriteLive = (liveId: Int, target: Boolean) -> Unit @@ -48,18 +48,18 @@ internal fun FeedDialog( ) val favourite = status.live.favourite DialogItem( - if (favourite) I18R.string.feat_feed_dialog_favourite_cancel_title - else I18R.string.feat_feed_dialog_favourite_title + if (favourite) string.feat_feed_dialog_favourite_cancel_title + else string.feat_feed_dialog_favourite_title ) { onUpdate(DialogStatus.Idle) onFavorite(status.live.id, !favourite) } - DialogItem(I18R.string.feat_feed_dialog_mute_title) { + DialogItem(string.feat_feed_dialog_mute_title) { onUpdate(DialogStatus.Idle) onBanned(status.live.id, true) } if (!status.live.cover.isNullOrEmpty()) { - DialogItem(I18R.string.feat_feed_dialog_save_picture_title) { + DialogItem(string.feat_feed_dialog_save_picture_title) { onUpdate(DialogStatus.Idle) onSavePicture(status.live.id) } diff --git a/features/feed/src/main/java/com/m3u/features/feed/components/LiveItem.kt b/features/feed/src/main/java/com/m3u/features/feed/components/LiveItem.kt index ec4c0668b..2127fb128 100644 --- a/features/feed/src/main/java/com/m3u/features/feed/components/LiveItem.kt +++ b/features/feed/src/main/java/com/m3u/features/feed/components/LiveItem.kt @@ -28,13 +28,13 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import com.m3u.data.database.entity.Live -import com.m3u.ui.components.Image -import com.m3u.ui.components.TextBadge -import com.m3u.ui.model.LocalScalable -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.components.Image +import com.m3u.material.components.TextBadge +import com.m3u.material.model.LocalScalable +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme import java.net.URI -import com.m3u.i18n.R as I18R +import com.m3u.i18n.R.string @OptIn(ExperimentalFoundationApi::class) @Composable @@ -57,7 +57,7 @@ internal fun LiveItem( URI(live.url).scheme } catch (ignored: Exception) { null - } ?: context.getString(I18R.string.feat_feed_scheme_unknown).uppercase() + } ?: context.getString(string.feat_feed_scheme_unknown).uppercase() } Surface( shape = RoundedCornerShape(spacing.medium), diff --git a/features/live/src/main/java/com/m3u/features/live/LiveScreen.kt b/features/live/src/main/java/com/m3u/features/live/LiveScreen.kt index 21fd66cf6..78997372c 100644 --- a/features/live/src/main/java/com/m3u/features/live/LiveScreen.kt +++ b/features/live/src/main/java/com/m3u/features/live/LiveScreen.kt @@ -24,17 +24,17 @@ import androidx.media3.common.PlaybackException import androidx.media3.common.Player import com.m3u.core.annotation.ClipMode import com.m3u.core.unspecified.UBoolean -import com.m3u.core.unspecified.u +import com.m3u.core.unspecified.unspecifiable import com.m3u.core.util.basic.isNotEmpty import com.m3u.features.live.components.DlnaDevicesBottomSheet import com.m3u.features.live.fragments.LiveFragment -import com.m3u.ui.components.MaskState -import com.m3u.ui.components.rememberMaskState -import com.m3u.ui.ktx.LifecycleEffect -import com.m3u.ui.model.LocalHelper -import com.m3u.ui.model.LocalTheme -import com.m3u.ui.model.OnPipModeChanged -import com.m3u.ui.model.repeatOnLifecycle +import com.m3u.material.components.MaskState +import com.m3u.material.components.rememberMaskState +import com.m3u.material.ktx.LifecycleEffect +import com.m3u.material.model.LocalTheme +import com.m3u.ui.LocalHelper +import com.m3u.ui.OnPipModeChanged +import com.m3u.ui.repeatOnLifecycle import kotlin.math.absoluteValue @Composable @@ -52,7 +52,7 @@ internal fun LiveRoute( val searching by viewModel.searching.collectAsStateWithLifecycle() val maskState = rememberMaskState { visible -> - helper.statusBarsVisibility = visible.u + helper.statusBarsVisibility = visible.unspecifiable helper.navigationBarsVisibility = UBoolean.False } var isPipMode by remember { mutableStateOf(false) } diff --git a/features/live/src/main/java/com/m3u/features/live/components/CoverPlaceholder.kt b/features/live/src/main/java/com/m3u/features/live/components/CoverPlaceholder.kt index a36f77ed1..0a8fa4719 100644 --- a/features/live/src/main/java/com/m3u/features/live/components/CoverPlaceholder.kt +++ b/features/live/src/main/java/com/m3u/features/live/components/CoverPlaceholder.kt @@ -12,8 +12,8 @@ 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.ui.components.Image -import com.m3u.ui.model.LocalDuration +import com.m3u.material.components.Image +import com.m3u.material.model.LocalDuration @Composable internal fun CoverPlaceholder( diff --git a/features/live/src/main/java/com/m3u/features/live/components/DlnaDeviceItem.kt b/features/live/src/main/java/com/m3u/features/live/components/DlnaDeviceItem.kt index 69352e0ff..cc42408ee 100644 --- a/features/live/src/main/java/com/m3u/features/live/components/DlnaDeviceItem.kt +++ b/features/live/src/main/java/com/m3u/features/live/components/DlnaDeviceItem.kt @@ -14,7 +14,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalTheme import net.mm2d.upnp.Device @OptIn(ExperimentalMaterialApi::class) diff --git a/features/live/src/main/java/com/m3u/features/live/components/DlnaDevicesBottomSheet.kt b/features/live/src/main/java/com/m3u/features/live/components/DlnaDevicesBottomSheet.kt index f79d81bc4..42886e231 100644 --- a/features/live/src/main/java/com/m3u/features/live/components/DlnaDevicesBottomSheet.kt +++ b/features/live/src/main/java/com/m3u/features/live/components/DlnaDevicesBottomSheet.kt @@ -24,12 +24,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.m3u.ui.components.CircularProgressIndicator -import com.m3u.ui.components.MaskState -import com.m3u.ui.components.OnDismiss -import com.m3u.ui.model.LocalSpacing +import com.m3u.material.components.CircularProgressIndicator +import com.m3u.material.components.MaskState +import com.m3u.material.components.OnDismiss +import com.m3u.material.model.LocalSpacing import net.mm2d.upnp.Device -import com.m3u.i18n.R as I18R +import com.m3u.i18n.R.string @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @Composable @@ -67,7 +67,7 @@ fun DlnaDevicesBottomSheet( ) ) { Text( - text = stringResource(I18R.string.feat_live_dlna_devices), + text = stringResource(string.feat_live_dlna_devices), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.h6, modifier = Modifier.weight(1f) diff --git a/features/live/src/main/java/com/m3u/features/live/components/LiveMask.kt b/features/live/src/main/java/com/m3u/features/live/components/LiveMask.kt index a70759e5b..cfd33a255 100644 --- a/features/live/src/main/java/com/m3u/features/live/components/LiveMask.kt +++ b/features/live/src/main/java/com/m3u/features/live/components/LiveMask.kt @@ -12,10 +12,10 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import com.m3u.ui.components.Mask -import com.m3u.ui.components.MaskPanel -import com.m3u.ui.components.MaskState -import com.m3u.ui.model.LocalSpacing +import com.m3u.material.components.Mask +import com.m3u.material.components.MaskPanel +import com.m3u.material.components.MaskState +import com.m3u.material.model.LocalSpacing @Composable internal fun LiveMask( diff --git a/features/live/src/main/java/com/m3u/features/live/fragments/LiveFragment.kt b/features/live/src/main/java/com/m3u/features/live/fragments/LiveFragment.kt index c8ce913ad..40732b052 100644 --- a/features/live/src/main/java/com/m3u/features/live/fragments/LiveFragment.kt +++ b/features/live/src/main/java/com/m3u/features/live/fragments/LiveFragment.kt @@ -41,17 +41,17 @@ import com.m3u.core.annotation.ClipMode import com.m3u.core.util.basic.isNotEmpty import com.m3u.features.live.components.CoverPlaceholder import com.m3u.features.live.components.LiveMask -import com.m3u.ui.components.Background -import com.m3u.ui.components.ExoPlayer -import com.m3u.ui.components.Image -import com.m3u.ui.components.MaskButton -import com.m3u.ui.components.MaskCircleButton -import com.m3u.ui.components.MaskState -import com.m3u.ui.components.rememberPlayerState -import com.m3u.ui.model.LocalHelper -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme -import com.m3u.i18n.R as I18R +import com.m3u.material.components.Background +import com.m3u.material.components.Image +import com.m3u.material.components.MaskButton +import com.m3u.material.components.MaskCircleButton +import com.m3u.material.components.MaskState +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme +import com.m3u.i18n.R.string +import com.m3u.ui.LocalHelper +import com.m3u.ui.Player +import com.m3u.ui.rememberPlayerState @OptIn(ExperimentalFoundationApi::class) @Composable @@ -94,7 +94,7 @@ internal fun LiveFragment( onUninstallMedia = onUninstallMedia ) - ExoPlayer( + Player( state = state, modifier = Modifier.fillMaxSize() ) @@ -114,7 +114,7 @@ internal fun LiveFragment( state = maskState, icon = Icons.AutoMirrored.Rounded.ArrowBack, onClick = onBackPressed, - contentDescription = stringResource(I18R.string.feat_live_tooltip_on_back_pressed) + contentDescription = stringResource(string.feat_live_tooltip_on_back_pressed) ) Spacer(modifier = Modifier.weight(1f)) MaskButton( @@ -122,16 +122,16 @@ internal fun LiveFragment( icon = if (muted) Icons.AutoMirrored.Rounded.VolumeMute else Icons.AutoMirrored.Rounded.VolumeUp, onClick = onMuted, - contentDescription = if (muted) stringResource(I18R.string.feat_live_tooltip_unmute) - else stringResource(I18R.string.feat_live_tooltip_mute) + contentDescription = if (muted) stringResource(string.feat_live_tooltip_unmute) + else stringResource(string.feat_live_tooltip_mute) ) MaskButton( state = maskState, icon = Icons.Rounded.Star, tint = if (stared) Color.Yellow else Color.Unspecified, onClick = onFavourite, - contentDescription = if (stared) stringResource(I18R.string.feat_live_tooltip_unfavourite) - else stringResource(I18R.string.feat_live_tooltip_favourite) + contentDescription = if (stared) stringResource(string.feat_live_tooltip_unfavourite) + else stringResource(string.feat_live_tooltip_favourite) ) if (experimentalMode) { MaskButton( @@ -142,8 +142,8 @@ internal fun LiveFragment( tint = if (recording) LocalTheme.current.error else Color.Unspecified, onClick = onRecord, - contentDescription = if (recording) stringResource(I18R.string.feat_live_tooltip_unrecord) - else stringResource(I18R.string.feat_live_tooltip_record) + contentDescription = if (recording) stringResource(string.feat_live_tooltip_unrecord) + else stringResource(string.feat_live_tooltip_record) ) if (playback != Player.STATE_IDLE) { MaskButton( @@ -151,7 +151,7 @@ internal fun LiveFragment( enabled = false, icon = Icons.Rounded.Cast, onClick = searchDlnaDevices, - contentDescription = stringResource(I18R.string.feat_live_tooltip_cast) + contentDescription = stringResource(string.feat_live_tooltip_cast) ) } } @@ -163,7 +163,7 @@ internal fun LiveFragment( helper.enterPipMode(videoSize) maskState.sleep() }, - contentDescription = stringResource(I18R.string.feat_live_tooltip_enter_pip_mode) + contentDescription = stringResource(string.feat_live_tooltip_enter_pip_mode) ) } }, @@ -255,10 +255,10 @@ private val PlaybackException?.displayText: String private val @Player.State Int.displayText: String @Composable get() = when (this) { - Player.STATE_IDLE -> I18R.string.feat_live_playback_state_idle - Player.STATE_BUFFERING -> I18R.string.feat_live_playback_state_buffering + Player.STATE_IDLE -> string.feat_live_playback_state_idle + Player.STATE_BUFFERING -> string.feat_live_playback_state_buffering Player.STATE_READY -> null - Player.STATE_ENDED -> I18R.string.feat_live_playback_state_ended + Player.STATE_ENDED -> string.feat_live_playback_state_ended else -> null } ?.let { stringResource(it) } diff --git a/features/main/src/main/java/com/m3u/features/main/MainScreen.kt b/features/main/src/main/java/com/m3u/features/main/MainScreen.kt index 04690cba3..6fe1ea880 100644 --- a/features/main/src/main/java/com/m3u/features/main/MainScreen.kt +++ b/features/main/src/main/java/com/m3u/features/main/MainScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -31,12 +30,14 @@ import com.m3u.features.main.components.MainDialog import com.m3u.features.main.components.OnRename import com.m3u.features.main.components.OnUnsubscribe import com.m3u.features.main.model.FeedDetail -import com.m3u.ui.ktx.interceptVolumeEvent -import com.m3u.ui.model.LocalHelper -import com.m3u.ui.model.LocalScalable -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.Scalable -import com.m3u.i18n.R as I18R +import com.m3u.material.ktx.interceptVolumeEvent +import com.m3u.material.model.LocalScalable +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.Scalable +import com.m3u.i18n.R.string +import com.m3u.ui.EventHandler +import com.m3u.ui.LocalHelper +import com.m3u.ui.ResumeEvent private typealias showDialog = (Feed) -> Unit typealias NavigateToFeed = (feed: Feed) -> Unit @@ -44,7 +45,7 @@ typealias NavigateToFeed = (feed: Feed) -> Unit @Composable fun MainRoute( navigateToFeed: NavigateToFeed, - isCurrentPage: Boolean, + resume: ResumeEvent, modifier: Modifier = Modifier, viewModel: MainViewModel = hiltViewModel() ) { @@ -55,10 +56,9 @@ fun MainRoute( fun onRowCount(target: Int) { state.rowCount = target } - LaunchedEffect(isCurrentPage) { - if (isCurrentPage) { - helper.actions = emptyList() - } + + EventHandler(resume) { + helper.actions = emptyList() } val interceptVolumeEventModifier = remember(state.godMode) { @@ -214,7 +214,7 @@ private fun LandscapeOrientationContent( @Composable private fun Feed.calculateUiTitle(): AnnotatedString { val actual = title.ifEmpty { - if (local) stringResource(I18R.string.feat_main_imported_feed_title) + if (local) stringResource(string.feat_main_imported_feed_title) else "" } return AnnotatedString( diff --git a/features/main/src/main/java/com/m3u/features/main/MainViewModel.kt b/features/main/src/main/java/com/m3u/features/main/MainViewModel.kt index b021d48cb..817e07c4a 100644 --- a/features/main/src/main/java/com/m3u/features/main/MainViewModel.kt +++ b/features/main/src/main/java/com/m3u/features/main/MainViewModel.kt @@ -18,7 +18,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject -import com.m3u.i18n.R as I18R +import com.m3u.i18n.R.string @HiltViewModel class MainViewModel @Inject constructor( @@ -71,7 +71,7 @@ class MainViewModel @Inject constructor( viewModelScope.launch { val feed = feedRepository.unsubscribe(url) if (feed == null) { - val message = string(I18R.string.feat_main_error_unsubscribe_feed) + val message = string(string.feat_main_error_unsubscribe_feed) logger.log(message) } } diff --git a/features/main/src/main/java/com/m3u/features/main/components/FeedItem.kt b/features/main/src/main/java/com/m3u/features/main/components/FeedItem.kt index 256e3d66d..46d5d6214 100644 --- a/features/main/src/main/java/com/m3u/features/main/components/FeedItem.kt +++ b/features/main/src/main/java/com/m3u/features/main/components/FeedItem.kt @@ -26,12 +26,12 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.m3u.ui.components.OuterRow -import com.m3u.ui.ktx.animateColor -import com.m3u.ui.ktx.animateDp -import com.m3u.ui.ktx.animated -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.components.OuterRow +import com.m3u.material.ktx.animateColor +import com.m3u.material.ktx.animateDp +import com.m3u.material.ktx.animated +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/features/main/src/main/java/com/m3u/features/main/components/Loading.kt b/features/main/src/main/java/com/m3u/features/main/components/Loading.kt index a31b619c0..388053ab9 100644 --- a/features/main/src/main/java/com/m3u/features/main/components/Loading.kt +++ b/features/main/src/main/java/com/m3u/features/main/components/Loading.kt @@ -4,7 +4,7 @@ import androidx.annotation.FloatRange import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.m3u.features.main.R -import com.m3u.ui.components.ProgressLottie +import com.m3u.material.components.ProgressLottie @Composable internal fun Loading( diff --git a/features/main/src/main/java/com/m3u/features/main/components/MainDialog.kt b/features/main/src/main/java/com/m3u/features/main/components/MainDialog.kt index 10738d753..6138516be 100644 --- a/features/main/src/main/java/com/m3u/features/main/components/MainDialog.kt +++ b/features/main/src/main/java/com/m3u/features/main/components/MainDialog.kt @@ -15,13 +15,13 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp import com.m3u.data.database.entity.Feed -import com.m3u.ui.components.AppDialog -import com.m3u.ui.components.DialogItem -import com.m3u.ui.components.DialogTextField -import com.m3u.ui.ktx.animateDp -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme -import com.m3u.i18n.R as I18R +import com.m3u.material.components.AppDialog +import com.m3u.material.components.DialogItem +import com.m3u.material.components.DialogTextField +import com.m3u.material.ktx.animateDp +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme +import com.m3u.i18n.R.string internal typealias OnUpdateStatus = (MainDialog) -> Unit internal typealias OnUnsubscribe = (feedUrl: String) -> Unit @@ -68,7 +68,7 @@ internal fun MainDialog( var renamedText by remember(currentStatus) { mutableStateOf( with(currentStatus.feed) { - if (editable) title else context.getString(I18R.string.feat_main_imported_feed_title) + if (editable) title else context.getString(string.feat_main_imported_feed_title) } ) } @@ -87,13 +87,13 @@ internal fun MainDialog( } ) if (!editMode) { - DialogItem(I18R.string.feat_main_unsubscribe_feed) { + DialogItem(string.feat_main_unsubscribe_feed) { unsubscribe(currentStatus.feed.url) update(MainDialog.Idle) } if (!currentStatus.feed.local) { val clipboardManager = LocalClipboardManager.current - DialogItem(I18R.string.feat_main_copy_feed_url) { + DialogItem(string.feat_main_copy_feed_url) { val annotatedString = AnnotatedString(currentStatus.feed.url) clipboardManager.setText(annotatedString) update(MainDialog.Idle) diff --git a/features/setting/src/main/java/com/m3u/features/setting/SettingEvent.kt b/features/setting/src/main/java/com/m3u/features/setting/SettingEvent.kt index fafe9ea72..236b27aba 100644 --- a/features/setting/src/main/java/com/m3u/features/setting/SettingEvent.kt +++ b/features/setting/src/main/java/com/m3u/features/setting/SettingEvent.kt @@ -12,8 +12,8 @@ sealed interface SettingEvent { data object OnConnectTimeout : SettingEvent data object OnClipMode : SettingEvent data class OnBannedLive(val id: Int) : SettingEvent - data class ImportJavaScript(val uri: Uri?) : SettingEvent - data class OpenDocument(val uri: Uri?) : SettingEvent + data class ImportJavaScript(val uri: Uri = Uri.EMPTY) : SettingEvent + data class OpenDocument(val uri: Uri = Uri.EMPTY) : SettingEvent data object ScrollDefaultDestination : SettingEvent data object OnLocalStorage : SettingEvent } \ No newline at end of file diff --git a/features/setting/src/main/java/com/m3u/features/setting/SettingScreen.kt b/features/setting/src/main/java/com/m3u/features/setting/SettingScreen.kt index 958373af6..c92378ad0 100644 --- a/features/setting/src/main/java/com/m3u/features/setting/SettingScreen.kt +++ b/features/setting/src/main/java/com/m3u/features/setting/SettingScreen.kt @@ -21,7 +21,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Settings import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -42,11 +41,13 @@ import com.m3u.data.database.entity.Live import com.m3u.features.setting.fragments.PreferencesFragment import com.m3u.features.setting.fragments.ScriptsFragment import com.m3u.features.setting.fragments.SubscriptionsFragment +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme import com.m3u.ui.Destination -import com.m3u.ui.model.Fob -import com.m3u.ui.model.LocalHelper -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.ui.EventHandler +import com.m3u.ui.Fob +import com.m3u.ui.LocalHelper +import com.m3u.ui.ResumeEvent typealias NavigateToConsole = () -> Unit typealias NavigateToAbout = () -> Unit @@ -54,7 +55,7 @@ typealias NavigateToAbout = () -> Unit @Composable fun SettingRoute( modifier: Modifier = Modifier, - isCurrentPage: Boolean, + resume: ResumeEvent, viewModel: SettingViewModel = hiltViewModel(), navigateToConsole: NavigateToConsole, navigateToAbout: NavigateToAbout @@ -62,10 +63,8 @@ fun SettingRoute( val state by viewModel.state.collectAsStateWithLifecycle() val helper = LocalHelper.current - LaunchedEffect(isCurrentPage) { - if (isCurrentPage) { - helper.actions = emptyList() - } + EventHandler(resume) { + helper.actions = emptyList() } val configuration = LocalConfiguration.current val type = configuration.uiMode and Configuration.UI_MODE_TYPE_MASK @@ -78,7 +77,7 @@ fun SettingRoute( version = state.version, title = state.title, url = state.url, - uri = state.uri.takeUnless { it == Uri.EMPTY }, + uri = state.uri, feedStrategy = state.feedStrategy, godMode = state.godMode, clipMode = state.clipMode, @@ -129,7 +128,7 @@ private fun SettingScreen( version: String, title: String, url: String, - uri: Uri?, + uri: Uri, @FeedStrategy feedStrategy: Int, godMode: Boolean, @ClipMode clipMode: Int, @@ -167,7 +166,7 @@ private fun SettingScreen( navigateToAbout: NavigateToAbout, localStorage: Boolean, onLocalStorage: () -> Unit, - openDocument: (Uri?) -> Unit, + openDocument: (Uri) -> Unit, modifier: Modifier = Modifier ) { val helper = LocalHelper.current @@ -312,7 +311,7 @@ private fun PortraitOrientationContent( fragment: SettingFragments, title: String, url: String, - uri: Uri?, + uri: Uri, @FeedStrategy feedStrategy: Int, godMode: Boolean, @ClipMode clipMode: Int, @@ -350,7 +349,7 @@ private fun PortraitOrientationContent( navigateToAbout: NavigateToAbout, localStorage: Boolean, onLocalStorage: () -> Unit, - openDocument: (Uri?) -> Unit, + openDocument: (Uri) -> Unit, modifier: Modifier = Modifier ) { // TODO: replace with material3-modal-side-sheet. @@ -433,7 +432,7 @@ private fun LandscapeOrientationContent( fragment: SettingFragments, title: String, url: String, - uri: Uri?, + uri: Uri, godMode: Boolean, @ClipMode clipMode: Int, replaceFragment: (SettingFragments) -> Unit, @@ -472,7 +471,7 @@ private fun LandscapeOrientationContent( navigateToAbout: NavigateToAbout, localStorage: Boolean, onLocalStorage: () -> Unit, - openDocument: (Uri?) -> Unit, + openDocument: (Uri) -> Unit, modifier: Modifier = Modifier ) { val spacing = LocalSpacing.current diff --git a/features/setting/src/main/java/com/m3u/features/setting/SettingViewModel.kt b/features/setting/src/main/java/com/m3u/features/setting/SettingViewModel.kt index 8b5f42db1..cd71d3291 100644 --- a/features/setting/src/main/java/com/m3u/features/setting/SettingViewModel.kt +++ b/features/setting/src/main/java/com/m3u/features/setting/SettingViewModel.kt @@ -16,14 +16,14 @@ import com.m3u.core.architecture.viewmodel.BaseViewModel import com.m3u.data.repository.LiveRepository import com.m3u.data.repository.observeAll import com.m3u.data.worker.SubscriptionInBackgroundWorker -import com.m3u.ui.Destination import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject -import com.m3u.i18n.R as I18R +import com.m3u.i18n.R.string +import com.m3u.ui.Destination @HiltViewModel class SettingViewModel @Inject constructor( @@ -71,15 +71,15 @@ class SettingViewModel @Inject constructor( } } - private fun openDocument(uri: Uri?) { + private fun openDocument(uri: Uri) { writable.update { it.copy( - uri = uri ?: Uri.EMPTY + uri = uri ) } } - private fun importJavaScript(uri: Uri?) { + private fun importJavaScript(uri: Uri) { } private fun scrollDefaultDestination() { @@ -160,7 +160,7 @@ class SettingViewModel @Inject constructor( private fun subscribe() { val title = writable.value.title if (title.isEmpty()) { - val message = string(I18R.string.feat_setting_error_empty_title) + val message = string(string.feat_setting_error_empty_title) logger.log(message) return } @@ -168,8 +168,8 @@ class SettingViewModel @Inject constructor( val url = readable.actualUrl if (url == null) { val message = when { - readable.localStorage -> string(I18R.string.feat_setting_error_unselected_file) - else -> string(I18R.string.feat_setting_error_blank_url) + readable.localStorage -> string(string.feat_setting_error_unselected_file) + else -> string(string.feat_setting_error_blank_url) } logger.log(message) return @@ -187,7 +187,7 @@ class SettingViewModel @Inject constructor( .addTag(url) .build() workManager.enqueue(request) - val message = string(I18R.string.feat_setting_enqueue_subscribe) + val message = string(string.feat_setting_enqueue_subscribe) logger.log(message) writable.update { it.copy( diff --git a/features/setting/src/main/java/com/m3u/features/setting/components/MutedLiveItem.kt b/features/setting/src/main/java/com/m3u/features/setting/components/MutedLiveItem.kt index feac72881..820529d28 100644 --- a/features/setting/src/main/java/com/m3u/features/setting/components/MutedLiveItem.kt +++ b/features/setting/src/main/java/com/m3u/features/setting/components/MutedLiveItem.kt @@ -10,7 +10,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import com.m3u.data.database.entity.Live -import com.m3u.ui.components.IconButton +import com.m3u.material.components.IconButton @OptIn(ExperimentalMaterialApi::class) @Composable diff --git a/features/setting/src/main/java/com/m3u/features/setting/fragments/PreferencesFragment.kt b/features/setting/src/main/java/com/m3u/features/setting/fragments/PreferencesFragment.kt index bb3d9e4e9..c06689bfc 100644 --- a/features/setting/src/main/java/com/m3u/features/setting/fragments/PreferencesFragment.kt +++ b/features/setting/src/main/java/com/m3u/features/setting/fragments/PreferencesFragment.kt @@ -25,15 +25,16 @@ import com.m3u.core.annotation.ConnectTimeout import com.m3u.core.annotation.FeedStrategy import com.m3u.core.annotation.OnClipMode import com.m3u.core.annotation.OnFeedStrategy +import com.m3u.core.util.basic.title import com.m3u.features.setting.NavigateToAbout import com.m3u.features.setting.NavigateToConsole -import com.m3u.features.setting.components.CheckBoxPreference -import com.m3u.features.setting.components.IconPreference -import com.m3u.features.setting.components.Preference -import com.m3u.features.setting.components.TextPreference +import com.m3u.material.model.LocalSpacing +import com.m3u.i18n.R.string +import com.m3u.material.components.CheckBoxPreference +import com.m3u.material.components.IconPreference +import com.m3u.material.components.Preference +import com.m3u.material.components.TextPreference import com.m3u.ui.Destination -import com.m3u.ui.model.LocalSpacing -import com.m3u.i18n.R as I18R @Composable internal fun PreferencesFragment( @@ -84,43 +85,43 @@ internal fun PreferencesFragment( verticalArrangement = Arrangement.spacedBy(1.dp) ) { Preference( - title = stringResource(I18R.string.feat_setting_feed_management), + title = stringResource(string.feat_setting_feed_management).title(), enabled = true, onClick = onFeedManagement ) TextPreference( - title = stringResource(I18R.string.feat_setting_sync_mode), + title = stringResource(string.feat_setting_sync_mode).title(), content = when (feedStrategy) { - FeedStrategy.ALL -> stringResource(I18R.string.feat_setting_sync_mode_all) - FeedStrategy.SKIP_FAVORITE -> stringResource(I18R.string.feat_setting_sync_mode_skip_favourite) + FeedStrategy.ALL -> stringResource(string.feat_setting_sync_mode_all) + FeedStrategy.SKIP_FAVORITE -> stringResource(string.feat_setting_sync_mode_skip_favourite) else -> "" - }, + }.title(), onClick = onFeedStrategy ) TextPreference( - title = stringResource(I18R.string.feat_setting_clip_mode), + title = stringResource(string.feat_setting_clip_mode).title(), content = when (clipMode) { - ClipMode.ADAPTIVE -> stringResource(I18R.string.feat_setting_clip_mode_adaptive) - ClipMode.CLIP -> stringResource(I18R.string.feat_setting_clip_mode_clip) - ClipMode.STRETCHED -> stringResource(I18R.string.feat_setting_clip_mode_stretched) + ClipMode.ADAPTIVE -> stringResource(string.feat_setting_clip_mode_adaptive) + ClipMode.CLIP -> stringResource(string.feat_setting_clip_mode_clip) + ClipMode.STRETCHED -> stringResource(string.feat_setting_clip_mode_stretched) else -> "" - }, + }.title(), onClick = onClipMode ) TextPreference( - title = stringResource(I18R.string.feat_setting_connect_timeout), + title = stringResource(string.feat_setting_connect_timeout).title(), content = "${connectTimeout / 1000}s", onClick = onConnectTimeout ) TextPreference( - title = stringResource(I18R.string.feat_setting_initial_tab), - content = stringResource(Destination.Root.entries[initialRootDestination].iconTextId), + title = stringResource(string.feat_setting_initial_tab).title(), + content = stringResource(Destination.Root.entries[initialRootDestination].iconTextId).title(), onClick = onInitialTabIndex ) CheckBoxPreference( - title = stringResource(I18R.string.feat_setting_auto_refresh), - subtitle = stringResource(I18R.string.feat_setting_auto_refresh_description), + title = stringResource(string.feat_setting_auto_refresh).title(), + subtitle = stringResource(string.feat_setting_auto_refresh_description).title(), checked = autoRefresh, onCheckedChange = { newValue -> if (newValue != autoRefresh) { @@ -129,8 +130,8 @@ internal fun PreferencesFragment( } ) CheckBoxPreference( - title = stringResource(I18R.string.feat_setting_no_picture_mode), - subtitle = stringResource(I18R.string.feat_setting_no_picture_mode_description), + title = stringResource(string.feat_setting_no_picture_mode).title(), + subtitle = stringResource(string.feat_setting_no_picture_mode_description).title(), checked = noPictureMode, onCheckedChange = { newValue -> if (newValue != noPictureMode) { @@ -139,8 +140,8 @@ internal fun PreferencesFragment( } ) CheckBoxPreference( - title = stringResource(I18R.string.feat_setting_full_info_player), - subtitle = stringResource(I18R.string.feat_setting_full_info_player_description), + title = stringResource(string.feat_setting_full_info_player).title(), + subtitle = stringResource(string.feat_setting_full_info_player_description).title(), checked = fullInfoPlayer, onCheckedChange = { newValue -> if (newValue != fullInfoPlayer) { @@ -149,8 +150,8 @@ internal fun PreferencesFragment( } ) CheckBoxPreference( - title = stringResource(I18R.string.feat_setting_god_mode), - subtitle = stringResource(I18R.string.feat_setting_god_mode_description), + title = stringResource(string.feat_setting_god_mode).title(), + subtitle = stringResource(string.feat_setting_god_mode_description).title(), checked = godMode, onCheckedChange = { newValue -> if (newValue != godMode) { @@ -159,9 +160,9 @@ internal fun PreferencesFragment( } ) CheckBoxPreference( - title = stringResource(I18R.string.feat_setting_common_ui_mode), - subtitle = if (useCommonUIModeEnable) stringResource(I18R.string.feat_setting_common_ui_mode_description) - else stringResource(I18R.string.feat_setting_common_ui_mode_disabled_description), + title = stringResource(string.feat_setting_common_ui_mode).title(), + subtitle = if (useCommonUIModeEnable) stringResource(string.feat_setting_common_ui_mode_description).title() + else stringResource(string.feat_setting_common_ui_mode_disabled_description).title(), enabled = useCommonUIModeEnable, checked = useCommonUIMode, onCheckedChange = { newValue -> @@ -181,8 +182,8 @@ internal fun PreferencesFragment( verticalArrangement = Arrangement.spacedBy(1.dp) ) { CheckBoxPreference( - title = stringResource(I18R.string.feat_setting_experimental_mode), - subtitle = stringResource(I18R.string.feat_setting_experimental_mode_description), + title = stringResource(string.feat_setting_experimental_mode).title(), + subtitle = stringResource(string.feat_setting_experimental_mode_description).title(), checked = experimentalMode, onCheckedChange = { newValue -> if (newValue != experimentalMode) { @@ -203,8 +204,8 @@ internal fun PreferencesFragment( verticalArrangement = Arrangement.spacedBy(1.dp) ) { CheckBoxPreference( - title = stringResource(I18R.string.feat_setting_cinema_mode), - subtitle = stringResource(I18R.string.feat_setting_cinema_mode_description), + title = stringResource(string.feat_setting_cinema_mode).title(), + subtitle = stringResource(string.feat_setting_cinema_mode_description).title(), checked = cinemaMode, onCheckedChange = { newValue -> if (newValue != cinemaMode) { @@ -213,16 +214,16 @@ internal fun PreferencesFragment( } ) Preference( - title = stringResource(I18R.string.feat_setting_script_management), + title = stringResource(string.feat_setting_script_management).title(), enabled = true, onClick = onScriptManagement ) Preference( - title = stringResource(I18R.string.feat_setting_console_editor), + title = stringResource(string.feat_setting_console_editor).title(), onClick = navigateToConsole ) CheckBoxPreference( - title = stringResource(I18R.string.feat_setting_scroll_mode), + title = stringResource(string.feat_setting_scroll_mode).title(), checked = scrollMode, onCheckedChange = { newValue -> if (newValue != scrollMode) { @@ -231,8 +232,8 @@ internal fun PreferencesFragment( } ) CheckBoxPreference( - title = stringResource(I18R.string.feat_setting_ssl_verification_enabled), - subtitle = stringResource(I18R.string.feat_setting_ssl_verification_enabled_description), + title = stringResource(string.feat_setting_ssl_verification_enabled).title(), + subtitle = stringResource(string.feat_setting_ssl_verification_enabled_description).title(), checked = isSSLVerificationEnabled, onCheckedChange = { newValue -> if (newValue != isSSLVerificationEnabled) { @@ -253,7 +254,7 @@ internal fun PreferencesFragment( ) { val context = LocalContext.current IconPreference( - title = stringResource(I18R.string.feat_setting_system_setting), + title = stringResource(string.feat_setting_system_setting).title(), imageVector = Icons.AutoMirrored.Rounded.OpenInNew, onClick = { val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { @@ -264,17 +265,17 @@ internal fun PreferencesFragment( ) // TODO: https://www.dropbox.com/developers/documentation/http/documentation#file_requests-list Preference( - title = stringResource(I18R.string.feat_setting_dropbox).uppercase(), + title = stringResource(string.feat_setting_dropbox).uppercase(), onClick = navigateToAbout, enabled = false ) Preference( - title = stringResource(I18R.string.feat_setting_project_about), + title = stringResource(string.feat_setting_project_about).title(), onClick = navigateToAbout, // enabled = false ) Preference( - title = stringResource(I18R.string.feat_setting_app_version), + title = stringResource(string.feat_setting_app_version).title(), subtitle = version ) } diff --git a/features/setting/src/main/java/com/m3u/features/setting/fragments/ScriptsFragment.kt b/features/setting/src/main/java/com/m3u/features/setting/fragments/ScriptsFragment.kt index 084f96f7e..c4ccf25c1 100644 --- a/features/setting/src/main/java/com/m3u/features/setting/fragments/ScriptsFragment.kt +++ b/features/setting/src/main/java/com/m3u/features/setting/fragments/ScriptsFragment.kt @@ -7,9 +7,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.m3u.ui.components.Button -import com.m3u.ui.components.OuterColumn -import com.m3u.i18n.R as I18R +import com.m3u.material.components.Button +import com.m3u.material.components.OuterColumn +import com.m3u.i18n.R.string @Composable internal fun ScriptsFragment( @@ -33,7 +33,7 @@ internal fun ScriptsFragment( } Button( - textRes = I18R.string.feat_setting_script_management_import_js, + textRes = string.feat_setting_script_management_import_js, modifier = Modifier.fillMaxWidth(), onClick = { launcher.launch(arrayOf("text/javascript")) diff --git a/features/setting/src/main/java/com/m3u/features/setting/fragments/SubscriptionsFragment.kt b/features/setting/src/main/java/com/m3u/features/setting/fragments/SubscriptionsFragment.kt index 8640358bd..6a42c5749 100644 --- a/features/setting/src/main/java/com/m3u/features/setting/fragments/SubscriptionsFragment.kt +++ b/features/setting/src/main/java/com/m3u/features/setting/fragments/SubscriptionsFragment.kt @@ -40,18 +40,18 @@ import androidx.compose.ui.unit.sp import com.m3u.core.util.readContentFilename import com.m3u.data.database.entity.Live import com.m3u.features.setting.components.MutedLiveItem -import com.m3u.ui.components.Button -import com.m3u.ui.components.LabelField -import com.m3u.ui.components.TextButton -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme -import com.m3u.i18n.R as I18R +import com.m3u.material.components.Button +import com.m3u.material.components.LabelField +import com.m3u.material.components.TextButton +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme +import com.m3u.i18n.R.string @Composable internal fun SubscriptionsFragment( title: String, url: String, - uri: Uri?, + uri: Uri, localStorage: Boolean, mutedLives: List, onBannedLive: (Int) -> Unit, @@ -59,7 +59,7 @@ internal fun SubscriptionsFragment( onUrl: (String) -> Unit, onSubscribe: () -> Unit, onLocalStorage: () -> Unit, - openDocument: (Uri?) -> Unit, + openDocument: (Uri) -> Unit, modifier: Modifier = Modifier ) { val spacing = LocalSpacing.current @@ -80,7 +80,7 @@ internal fun SubscriptionsFragment( verticalArrangement = Arrangement.spacedBy(1.dp) ) { Text( - text = stringResource(I18R.string.feat_setting_label_muted_lives), + text = stringResource(string.feat_setting_label_muted_lives), style = MaterialTheme.typography.button, color = theme.onTint, modifier = Modifier @@ -105,7 +105,7 @@ internal fun SubscriptionsFragment( item { LabelField( text = title, - placeholder = stringResource(I18R.string.feat_setting_placeholder_title).uppercase(), + placeholder = stringResource(string.feat_setting_placeholder_title).uppercase(), onValueChange = onTitle, keyboardActions = KeyboardActions( onNext = { @@ -124,7 +124,7 @@ internal fun SubscriptionsFragment( if (!localStorage) { LabelField( text = url, - placeholder = stringResource(I18R.string.feat_setting_placeholder_url).uppercase(), + placeholder = stringResource(string.feat_setting_placeholder_url).uppercase(), onValueChange = onUrl, keyboardActions = KeyboardActions( onDone = { @@ -154,7 +154,7 @@ internal fun SubscriptionsFragment( item { Column { Button( - text = stringResource(I18R.string.feat_setting_label_subscribe), + text = stringResource(string.feat_setting_label_subscribe), onClick = onSubscribe, modifier = Modifier.fillMaxWidth() ) @@ -189,7 +189,7 @@ fun LocalStorageSwitch( horizontalArrangement = Arrangement.spacedBy(spacing.medium) ) { Text( - text = stringResource(I18R.string.feat_setting_local_storage), + text = stringResource(string.feat_setting_local_storage), style = MaterialTheme.typography.subtitle1, modifier = Modifier.weight(1f) ) @@ -199,22 +199,21 @@ fun LocalStorageSwitch( @Composable private fun LocalStorageButton( - uri: Uri?, - openDocument: (Uri?) -> Unit, + uri: Uri, + openDocument: (Uri) -> Unit, modifier: Modifier = Modifier ) { val context = LocalContext.current - val selected = uri != null + val selected = uri != Uri.EMPTY val theme = LocalTheme.current val spacing = LocalSpacing.current val launcher = rememberLauncherForActivityResult( - ActivityResultContracts.GetContent(), - openDocument - ) + ActivityResultContracts.GetContent() + ) { openDocument(it ?: Uri.EMPTY) } val icon = Icons.AutoMirrored.Rounded.OpenInNew val text = if (selected) remember(uri) { - uri?.readContentFilename(context.contentResolver).orEmpty() - } else stringResource(I18R.string.feat_setting_label_select_from_local_storage) + uri.readContentFilename(context.contentResolver).orEmpty() + } else stringResource(string.feat_setting_label_select_from_local_storage) Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, @@ -262,7 +261,7 @@ private fun ClipboardButton( val clipboardManager = LocalClipboardManager.current TextButton( enabled = enabled, - text = stringResource(I18R.string.feat_setting_label_parse_from_clipboard), + text = stringResource(string.feat_setting_label_parse_from_clipboard), onClick = { val clipboardUrl = clipboardManager.getText()?.text.orEmpty() val clipboardTitle = run { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c13293c42..7216cbaf3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,6 +20,7 @@ google-dagger = "2.48" io-coil = "2.4.0" kotlinx-serialization-json = "1.6.0" +kotlinx-datetime = "0.4.1" lottie-compose = "6.1.0" net-mm2d-mmupnp = "3.1.6" squareup-retrofit2 = "2.9.0" @@ -103,6 +104,7 @@ net-mm2d-mmupnp-mmupnp = { module = "net.mm2d.mmupnp:mmupnp", version.ref = "net org-jetbrains-kotlin-kotlin-serialization = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" } org-jetbrains-kotlinx-kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" } +org-jetbrains-kotlinx-kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-datetime" } com-google-android-material-material = { group = "com.google.android.material", name = "material", version.ref = "com-google-android-material" } diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 000000000..8b78537e5 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,6 @@ +jdk: + - openjdk17 +before_install: + - sdk install java 17.0.1-open + - sdk use java 17.0.1-open + - ./scripts/prepareJitpackEnvironment.sh \ No newline at end of file diff --git a/material/.gitignore b/material/.gitignore new file mode 100644 index 000000000..3fe82fd65 --- /dev/null +++ b/material/.gitignore @@ -0,0 +1,3 @@ +/build +/src/test +/src/androidTest \ No newline at end of file diff --git a/material/build.gradle.kts b/material/build.gradle.kts new file mode 100644 index 000000000..79b4a6607 --- /dev/null +++ b/material/build.gradle.kts @@ -0,0 +1,71 @@ +plugins { + alias(libs.plugins.com.android.library) + alias(libs.plugins.org.jetbrains.kotlin.android) + `maven-publish` +} + +android { + namespace = "com.m3u.material" + compileSdk = 33 + defaultConfig { + minSdk = 26 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + buildTypes { + release { + isMinifyEnabled = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() + } +} + +dependencies { + implementation(libs.androidx.core.core.ktx) + implementation(libs.androidx.activity.activity.compose) + + implementation(libs.androidx.lifecycle.lifecycle.runtime.ktx) + + api(libs.bundles.androidx.compose) + api(libs.androidx.compose.material.material.icons.extended) + api(libs.androidx.compose.material3.material3) + debugApi(libs.androidx.compose.ui.ui.tooling) + debugApi(libs.androidx.compose.ui.ui.tooling.preview) + + api(libs.androidx.navigation.navigation.compose) + + api(libs.io.coil.kt.coil) + api(libs.io.coil.kt.coil.compose) + + implementation(libs.com.airbnb.android.lottie.compose) +} + +afterEvaluate { + publishing { + publications { + create("release") { + groupId = "com.m3u" + artifactId = "material" + version = android.defaultConfig.versionName + + from(components["release"]) + } + } + } +} \ No newline at end of file diff --git a/material/consumer-rules.pro b/material/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/material/proguard-rules.pro b/material/proguard-rules.pro new file mode 100644 index 000000000..ff59496d8 --- /dev/null +++ b/material/proguard-rules.pro @@ -0,0 +1,21 @@ +# 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 \ No newline at end of file diff --git a/material/src/main/AndroidManifest.xml b/material/src/main/AndroidManifest.xml new file mode 100644 index 000000000..44008a433 --- /dev/null +++ b/material/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ui/src/main/java/com/m3u/ui/components/AppBar.kt b/material/src/main/java/com/m3u/material/components/AppBars.kt similarity index 96% rename from ui/src/main/java/com/m3u/ui/components/AppBar.kt rename to material/src/main/java/com/m3u/material/components/AppBars.kt index b3bebf842..3b08adbe4 100644 --- a/ui/src/main/java/com/m3u/ui/components/AppBar.kt +++ b/material/src/main/java/com/m3u/material/components/AppBars.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDpAsState @@ -49,7 +49,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -57,11 +56,10 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.lerp -import com.m3u.i18n.R as I18R -import com.m3u.ui.ktx.animated -import com.m3u.ui.model.LocalDuration -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.ktx.animated +import com.m3u.material.model.LocalDuration +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme @Composable fun AppTopBar( @@ -72,6 +70,7 @@ fun AppTopBar( consumer: AppTopBarConsumer = AppTopBarDefaults.consumer, windowInsets: WindowInsets = AppTopBarDefaults.windowInsets, onBackPressed: (() -> Unit)? = null, + onBackPressedContentDescription: String? = null, actions: @Composable RowScope.() -> Unit = {}, content: @Composable (PaddingValues) -> Unit ) { @@ -183,7 +182,7 @@ fun AppTopBar( ) { IconButton( icon = Icons.AutoMirrored.Rounded.ArrowBack, - contentDescription = stringResource(I18R.string.ui_cd_top_bar_on_back_pressed), + contentDescription = onBackPressedContentDescription, onClick = { if (progress > 0) onBackPressed?.invoke() }, modifier = Modifier.wrapContentSize() ) diff --git a/ui/src/main/java/com/m3u/ui/components/Background.kt b/material/src/main/java/com/m3u/material/components/Backgrounds.kt similarity index 87% rename from ui/src/main/java/com/m3u/ui/components/Background.kt rename to material/src/main/java/com/m3u/material/components/Backgrounds.kt index 174ab8359..5907d94d0 100644 --- a/ui/src/main/java/com/m3u/ui/components/Background.kt +++ b/material/src/main/java/com/m3u/material/components/Backgrounds.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.LocalAbsoluteElevation @@ -9,9 +9,9 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.m3u.core.util.compose.ifUnspecified -import com.m3u.ui.ktx.animated -import com.m3u.ui.model.LocalTheme +import com.m3u.material.ktx.animated +import com.m3u.material.ktx.ifUnspecified +import com.m3u.material.model.LocalTheme @Composable fun Background( diff --git a/ui/src/main/java/com/m3u/ui/components/Badge.kt b/material/src/main/java/com/m3u/material/components/Badges.kt similarity index 90% rename from ui/src/main/java/com/m3u/ui/components/Badge.kt rename to material/src/main/java/com/m3u/material/components/Badges.kt index b9710e54c..3645137f8 100644 --- a/ui/src/main/java/com/m3u/ui/components/Badge.kt +++ b/material/src/main/java/com/m3u/material/components/Badges.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding @@ -10,9 +10,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import com.m3u.ui.model.LocalScalable -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalScalable +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme @Composable fun TextBadge( diff --git a/ui/src/main/java/com/m3u/ui/components/Brush.kt b/material/src/main/java/com/m3u/material/components/Brushes.kt similarity index 97% rename from ui/src/main/java/com/m3u/ui/components/Brush.kt rename to material/src/main/java/com/m3u/material/components/Brushes.kt index eda0b426f..361ff7145 100644 --- a/ui/src/main/java/com/m3u/ui/components/Brush.kt +++ b/material/src/main/java/com/m3u/material/components/Brushes.kt @@ -1,6 +1,6 @@ @file:Suppress("unused") -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.animation.animateColor import androidx.compose.animation.core.Easing @@ -16,8 +16,8 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TileMode -import com.m3u.ui.model.LocalTheme -import com.m3u.ui.model.StepColor +import com.m3u.material.model.LocalTheme +import com.m3u.material.model.StepColor @Stable @Composable diff --git a/ui/src/main/java/com/m3u/ui/components/Button.kt b/material/src/main/java/com/m3u/material/components/Buttons.kt similarity index 98% rename from ui/src/main/java/com/m3u/ui/components/Button.kt rename to material/src/main/java/com/m3u/material/components/Buttons.kt index 5d9d78842..b1ab18188 100644 --- a/ui/src/main/java/com/m3u/ui/components/Button.kt +++ b/material/src/main/java/com/m3u/material/components/Buttons.kt @@ -1,6 +1,6 @@ @file:Suppress("unused") -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -33,7 +33,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalTheme @Composable fun Button( diff --git a/ui/src/main/java/com/m3u/ui/components/Dialog.kt b/material/src/main/java/com/m3u/material/components/Dialogs.kt similarity index 97% rename from ui/src/main/java/com/m3u/ui/components/Dialog.kt rename to material/src/main/java/com/m3u/material/components/Dialogs.kt index f4f2b4574..47e9549e4 100644 --- a/ui/src/main/java/com/m3u/ui/components/Dialog.kt +++ b/material/src/main/java/com/m3u/material/components/Dialogs.kt @@ -1,6 +1,6 @@ @file:Suppress("unused") -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.animation.animateContentSize import androidx.compose.foundation.BorderStroke @@ -27,9 +27,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties -import com.m3u.core.util.compose.ifUnspecified -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.ktx.ifUnspecified +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme @Composable fun DialogTextField( diff --git a/ui/src/main/java/com/m3u/ui/components/Image.kt b/material/src/main/java/com/m3u/material/components/Images.kt similarity index 92% rename from ui/src/main/java/com/m3u/ui/components/Image.kt rename to material/src/main/java/com/m3u/material/components/Images.kt index cf4c19c8a..33e6e6d1c 100644 --- a/ui/src/main/java/com/m3u/ui/components/Image.kt +++ b/material/src/main/java/com/m3u/material/components/Images.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -15,9 +15,9 @@ import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.layout.ContentScale import coil.compose.SubcomposeAsyncImage -import com.m3u.ui.model.LocalScalable -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalScalable +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme @Composable fun Image( diff --git a/ui/src/main/java/com/m3u/ui/components/CircularProgressIndicator.kt b/material/src/main/java/com/m3u/material/components/Indicators.kt similarity index 98% rename from ui/src/main/java/com/m3u/ui/components/CircularProgressIndicator.kt rename to material/src/main/java/com/m3u/material/components/Indicators.kt index 5e2ec534c..b2224c7c3 100644 --- a/ui/src/main/java/com/m3u/ui/components/CircularProgressIndicator.kt +++ b/material/src/main/java/com/m3u/material/components/Indicators.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.size diff --git a/ui/src/main/java/com/m3u/ui/components/Layout.kt b/material/src/main/java/com/m3u/material/components/Layouts.kt similarity index 96% rename from ui/src/main/java/com/m3u/ui/components/Layout.kt rename to material/src/main/java/com/m3u/material/components/Layouts.kt index cfa36668a..815bc9d7f 100644 --- a/ui/src/main/java/com/m3u/ui/components/Layout.kt +++ b/material/src/main/java/com/m3u/material/components/Layouts.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.m3u.ui.model.LocalSpacing +import com.m3u.material.model.LocalSpacing @Composable fun OuterColumn( diff --git a/ui/src/main/java/com/m3u/ui/components/Lottie.kt b/material/src/main/java/com/m3u/material/components/Lotties.kt similarity index 97% rename from ui/src/main/java/com/m3u/ui/components/Lottie.kt rename to material/src/main/java/com/m3u/material/components/Lotties.kt index a690ffd07..1dd995945 100644 --- a/ui/src/main/java/com/m3u/ui/components/Lottie.kt +++ b/material/src/main/java/com/m3u/material/components/Lotties.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.annotation.RawRes import androidx.compose.runtime.Composable diff --git a/ui/src/main/java/com/m3u/ui/components/Mask.kt b/material/src/main/java/com/m3u/material/components/Mask.kt similarity index 98% rename from ui/src/main/java/com/m3u/ui/components/Mask.kt rename to material/src/main/java/com/m3u/material/components/Mask.kt index 458a22610..7436fbc97 100644 --- a/ui/src/main/java/com/m3u/ui/components/Mask.kt +++ b/material/src/main/java/com/m3u/material/components/Mask.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.annotation.IntRange import androidx.compose.animation.AnimatedVisibility @@ -41,8 +41,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp -import com.m3u.core.util.compose.ifUnspecified -import com.m3u.ui.ktx.animated +import com.m3u.material.ktx.animated +import com.m3u.material.ktx.ifUnspecified import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch diff --git a/ui/src/main/java/com/m3u/ui/components/NavigationSheet.kt b/material/src/main/java/com/m3u/material/components/NavigationSheet.kt similarity index 96% rename from ui/src/main/java/com/m3u/ui/components/NavigationSheet.kt rename to material/src/main/java/com/m3u/material/components/NavigationSheet.kt index 145f91310..238cfcd2a 100644 --- a/ui/src/main/java/com/m3u/ui/components/NavigationSheet.kt +++ b/material/src/main/java/com/m3u/material/components/NavigationSheet.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.ExperimentalLayoutApi @@ -18,7 +18,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalTheme @Composable fun NavigationSheet( diff --git a/features/setting/src/main/java/com/m3u/features/setting/components/Preferences.kt b/material/src/main/java/com/m3u/material/components/Preferences.kt similarity index 93% rename from features/setting/src/main/java/com/m3u/features/setting/components/Preferences.kt rename to material/src/main/java/com/m3u/material/components/Preferences.kt index 2d5145c9e..6b09acc02 100644 --- a/features/setting/src/main/java/com/m3u/features/setting/components/Preferences.kt +++ b/material/src/main/java/com/m3u/material/components/Preferences.kt @@ -1,6 +1,4 @@ -@file:Suppress("unused") - -package com.m3u.features.setting.components +package com.m3u.material.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -42,10 +40,9 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.sp -import com.m3u.core.util.basic.title -import com.m3u.ui.ktx.animated -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.ktx.animated +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme @OptIn( ExperimentalMaterialApi::class, @@ -53,7 +50,7 @@ import com.m3u.ui.model.LocalTheme ExperimentalMaterial3Api::class ) @Composable -internal fun Preference( +fun Preference( title: String, modifier: Modifier = Modifier, enabled: Boolean = true, @@ -88,7 +85,7 @@ internal fun Preference( LocalContentAlpha provides contentAlpha ) { Text( - text = title.title(), + text = title, style = MaterialTheme.typography.subtitle1, fontWeight = FontWeight.Bold, maxLines = 1, @@ -138,7 +135,7 @@ internal fun Preference( } @Composable -internal fun CheckBoxPreference( +fun CheckBoxPreference( title: String, checked: Boolean, onCheckedChange: (Boolean) -> Unit, @@ -174,7 +171,7 @@ internal fun CheckBoxPreference( } @Composable -internal fun SwitchPreference( +fun SwitchPreference( title: String, checked: Boolean, onCheckedChange: (Boolean) -> Unit, @@ -182,6 +179,13 @@ internal fun SwitchPreference( subtitle: String? = null, enabled: Boolean = true, ) { + val combined = Modifier + .toggleable( + value = checked, + onValueChange = { onCheckedChange(it) }, + role = Role.Checkbox + ) + .then(modifier) Preference( title = title, subtitle = subtitle, @@ -191,19 +195,19 @@ internal fun SwitchPreference( onCheckedChange(!checked) } }, - modifier = modifier, + modifier = combined, trailingContent = { Switch( enabled = enabled, checked = checked, - onCheckedChange = onCheckedChange + onCheckedChange = null ) } ) } @Composable -internal fun IconPreference( +fun IconPreference( title: String, imageVector: ImageVector, onClick: () -> Unit, @@ -229,7 +233,7 @@ internal fun IconPreference( } @Composable -internal fun TextPreference( +fun TextPreference( title: String, content: String, onClick: () -> Unit, diff --git a/ui/src/main/java/com/m3u/ui/components/Selection.kt b/material/src/main/java/com/m3u/material/components/Selection.kt similarity index 94% rename from ui/src/main/java/com/m3u/ui/components/Selection.kt rename to material/src/main/java/com/m3u/material/components/Selection.kt index 0ed182bce..34ba757ec 100644 --- a/ui/src/main/java/com/m3u/ui/components/Selection.kt +++ b/material/src/main/java/com/m3u/material/components/Selection.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -14,8 +14,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/ui/src/main/java/com/m3u/ui/components/TextField.kt b/material/src/main/java/com/m3u/material/components/TextFields.kt similarity index 98% rename from ui/src/main/java/com/m3u/ui/components/TextField.kt rename to material/src/main/java/com/m3u/material/components/TextFields.kt index a13d0ad91..306f1cbc0 100644 --- a/ui/src/main/java/com/m3u/ui/components/TextField.kt +++ b/material/src/main/java/com/m3u/material/components/TextFields.kt @@ -1,8 +1,9 @@ @file:Suppress("unused") @file:OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class) -package com.m3u.ui.components +package com.m3u.material.components +import android.util.Log import androidx.annotation.DrawableRes import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image @@ -63,12 +64,12 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.m3u.ui.ktx.InteractionType -import com.m3u.ui.ktx.animateDp -import com.m3u.ui.ktx.animateInt -import com.m3u.ui.ktx.interactionBorder -import com.m3u.ui.model.LocalDuration -import com.m3u.ui.model.LocalTheme +import com.m3u.material.ktx.InteractionType +import com.m3u.material.ktx.animateDp +import com.m3u.material.ktx.animateInt +import com.m3u.material.ktx.interactionBorder +import com.m3u.material.model.LocalDuration +import com.m3u.material.model.LocalTheme import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -473,6 +474,7 @@ fun LabelField( // Bring the composable into view (visible to user). LaunchedEffect(imeVisible, interactionSourceFocus) { + Log.e("TAG", "ime: $imeVisible") if (imeVisible && interactionSourceFocus) { scope.launch { delay(duration.fast.toLong()) diff --git a/ui/src/main/java/com/m3u/ui/components/ThemeSelection.kt b/material/src/main/java/com/m3u/material/components/ThemeSelection.kt similarity index 92% rename from ui/src/main/java/com/m3u/ui/components/ThemeSelection.kt rename to material/src/main/java/com/m3u/material/components/ThemeSelection.kt index 688c54ddc..47775e930 100644 --- a/ui/src/main/java/com/m3u/ui/components/ThemeSelection.kt +++ b/material/src/main/java/com/m3u/material/components/ThemeSelection.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.material.components import androidx.compose.animation.Crossfade import androidx.compose.animation.core.animateFloatAsState @@ -37,15 +37,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.m3u.i18n.R as I18R -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme -import com.m3u.ui.model.SugarColors -import com.m3u.ui.model.Theme +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme +import com.m3u.material.model.SugarColors +import com.m3u.material.model.Theme @OptIn(ExperimentalFoundationApi::class) @Composable @@ -54,7 +52,9 @@ fun ThemeSelection( selected: Boolean, onClick: () -> Unit, onLongClick: () -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + leftContentDescription: String, + rightContentDescription: String, ) { val spacing = LocalSpacing.current val alpha by animateFloatAsState( @@ -134,12 +134,14 @@ fun ThemeSelection( ColorPiece( containerColor = theme.primary, contentColor = theme.onPrimary, - left = true + left = true, + contentDescription = leftContentDescription ) ColorPiece( containerColor = theme.tint, contentColor = theme.onTint, - left = false + left = false, + contentDescription = rightContentDescription ) } Text( @@ -213,7 +215,8 @@ fun ThemeAddSelection( private fun ColorPiece( containerColor: Color, contentColor: Color, - left: Boolean + left: Boolean, + contentDescription: String ) { val spacing = LocalSpacing.current ElevatedCard( @@ -236,8 +239,7 @@ private fun ColorPiece( modifier = Modifier.fillMaxSize() ) { Text( - text = if (left) stringResource(I18R.string.ui_theme_card_left) - else stringResource(I18R.string.ui_theme_card_right), + text = contentDescription, style = MaterialTheme.typography.bodyLarge .copy( fontSize = if (left) 16.sp diff --git a/ui/src/main/java/com/m3u/ui/ktx/Animations.kt b/material/src/main/java/com/m3u/material/ktx/Animations.kt similarity index 94% rename from ui/src/main/java/com/m3u/ui/ktx/Animations.kt rename to material/src/main/java/com/m3u/material/ktx/Animations.kt index 1a1e16977..2146b23fb 100644 --- a/ui/src/main/java/com/m3u/ui/ktx/Animations.kt +++ b/material/src/main/java/com/m3u/material/ktx/Animations.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.ktx +package com.m3u.material.ktx import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.animateDpAsState @@ -8,7 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp -import com.m3u.ui.model.LocalDuration +import com.m3u.material.model.LocalDuration @Composable fun Color.animated(label: String): State = animateColorAsState( diff --git a/ui/src/main/java/com/m3u/ui/ktx/Blurs.kt b/material/src/main/java/com/m3u/material/ktx/Blurs.kt similarity index 99% rename from ui/src/main/java/com/m3u/ui/ktx/Blurs.kt rename to material/src/main/java/com/m3u/material/ktx/Blurs.kt index 000ec9061..8377e731b 100644 --- a/ui/src/main/java/com/m3u/ui/ktx/Blurs.kt +++ b/material/src/main/java/com/m3u/material/ktx/Blurs.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.ktx +package com.m3u.material.ktx import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier diff --git a/ui/src/main/java/com/m3u/ui/ktx/Effects.kt b/material/src/main/java/com/m3u/material/ktx/Effects.kt similarity index 90% rename from ui/src/main/java/com/m3u/ui/ktx/Effects.kt rename to material/src/main/java/com/m3u/material/ktx/Effects.kt index d4befd173..7ecd702c3 100644 --- a/ui/src/main/java/com/m3u/ui/ktx/Effects.kt +++ b/material/src/main/java/com/m3u/material/ktx/Effects.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.ktx +package com.m3u.material.ktx import androidx.compose.foundation.border import androidx.compose.foundation.interaction.InteractionSource @@ -10,9 +10,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Dp -import com.m3u.core.util.compose.ifUnspecified -import com.m3u.ui.model.LocalSpacing -import com.m3u.ui.model.LocalTheme +import com.m3u.material.model.LocalSpacing +import com.m3u.material.model.LocalTheme fun Modifier.interaction( type: InteractionType, diff --git a/ui/src/main/java/com/m3u/ui/ktx/Interaction.kt b/material/src/main/java/com/m3u/material/ktx/Interaction.kt similarity index 96% rename from ui/src/main/java/com/m3u/ui/ktx/Interaction.kt rename to material/src/main/java/com/m3u/material/ktx/Interaction.kt index cd62b2fc6..0981999e8 100644 --- a/ui/src/main/java/com/m3u/ui/ktx/Interaction.kt +++ b/material/src/main/java/com/m3u/material/ktx/Interaction.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.ktx +package com.m3u.material.ktx import androidx.compose.foundation.interaction.InteractionSource import androidx.compose.foundation.interaction.collectIsDraggedAsState diff --git a/ui/src/main/java/com/m3u/ui/ktx/InterceptEvent.kt b/material/src/main/java/com/m3u/material/ktx/InterceptEvent.kt similarity index 98% rename from ui/src/main/java/com/m3u/ui/ktx/InterceptEvent.kt rename to material/src/main/java/com/m3u/material/ktx/InterceptEvent.kt index 44709be19..e5c849de6 100644 --- a/ui/src/main/java/com/m3u/ui/ktx/InterceptEvent.kt +++ b/material/src/main/java/com/m3u/material/ktx/InterceptEvent.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.ktx +package com.m3u.material.ktx import android.view.KeyEvent import androidx.annotation.IntDef diff --git a/ui/src/main/java/com/m3u/ui/ktx/LifecycleEffect.kt b/material/src/main/java/com/m3u/material/ktx/LifecycleEffect.kt similarity index 98% rename from ui/src/main/java/com/m3u/ui/ktx/LifecycleEffect.kt rename to material/src/main/java/com/m3u/material/ktx/LifecycleEffect.kt index 130b36daf..58ff35675 100644 --- a/ui/src/main/java/com/m3u/ui/ktx/LifecycleEffect.kt +++ b/material/src/main/java/com/m3u/material/ktx/LifecycleEffect.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.ktx +package com.m3u.material.ktx import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect diff --git a/ui/src/main/java/com/m3u/ui/ktx/ScrollableState.kt b/material/src/main/java/com/m3u/material/ktx/ScrollableState.kt similarity index 74% rename from ui/src/main/java/com/m3u/ui/ktx/ScrollableState.kt rename to material/src/main/java/com/m3u/material/ktx/ScrollableState.kt index b6591a0c4..47cead875 100644 --- a/ui/src/main/java/com/m3u/ui/ktx/ScrollableState.kt +++ b/material/src/main/java/com/m3u/material/ktx/ScrollableState.kt @@ -1,11 +1,10 @@ @file:Suppress("unused") -package com.m3u.ui.ktx +package com.m3u.material.ktx import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState -import androidx.tv.foundation.lazy.grid.TvLazyGridState val LazyListState.isAtTop get() = firstVisibleItemIndex == 0 && firstVisibleItemScrollOffset == 0 @@ -24,9 +23,3 @@ val LazyStaggeredGridState.isAtTop val LazyStaggeredGridState.isScrolled get() = firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0 - -val TvLazyGridState.isAtTop - get() = firstVisibleItemIndex == 0 && firstVisibleItemScrollOffset == 0 - -val TvLazyGridState.isScrolled - get() = firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0 diff --git a/core/src/main/java/com/m3u/core/util/compose/Specified.kt b/material/src/main/java/com/m3u/material/ktx/Specified.kt similarity index 91% rename from core/src/main/java/com/m3u/core/util/compose/Specified.kt rename to material/src/main/java/com/m3u/material/ktx/Specified.kt index d418ac1aa..7417f5594 100644 --- a/core/src/main/java/com/m3u/core/util/compose/Specified.kt +++ b/material/src/main/java/com/m3u/material/ktx/Specified.kt @@ -1,4 +1,4 @@ -package com.m3u.core.util.compose +package com.m3u.material.ktx import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.isSpecified diff --git a/ui/src/main/java/com/m3u/ui/model/Duration.kt b/material/src/main/java/com/m3u/material/model/Duration.kt similarity index 89% rename from ui/src/main/java/com/m3u/ui/model/Duration.kt rename to material/src/main/java/com/m3u/material/model/Duration.kt index a1cfdaa38..b8ddec383 100644 --- a/ui/src/main/java/com/m3u/ui/model/Duration.kt +++ b/material/src/main/java/com/m3u/material/model/Duration.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.model +package com.m3u.material.model import androidx.compose.runtime.staticCompositionLocalOf diff --git a/ui/src/main/java/com/m3u/ui/model/GradientColors.kt b/material/src/main/java/com/m3u/material/model/GradientColors.kt similarity index 92% rename from ui/src/main/java/com/m3u/ui/model/GradientColors.kt rename to material/src/main/java/com/m3u/material/model/GradientColors.kt index 7ab941399..54da59e48 100644 --- a/ui/src/main/java/com/m3u/ui/model/GradientColors.kt +++ b/material/src/main/java/com/m3u/material/model/GradientColors.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.model +package com.m3u.material.model import androidx.compose.runtime.Immutable import androidx.compose.runtime.staticCompositionLocalOf diff --git a/ui/src/main/java/com/m3u/ui/model/Scalable.kt b/material/src/main/java/com/m3u/material/model/Scalable.kt similarity index 95% rename from ui/src/main/java/com/m3u/ui/model/Scalable.kt rename to material/src/main/java/com/m3u/material/model/Scalable.kt index e18f5ea07..affc00d9f 100644 --- a/ui/src/main/java/com/m3u/ui/model/Scalable.kt +++ b/material/src/main/java/com/m3u/material/model/Scalable.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.model +package com.m3u.material.model import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.unit.TextUnit diff --git a/ui/src/main/java/com/m3u/ui/model/Spacing.kt b/material/src/main/java/com/m3u/material/model/Spacing.kt similarity index 93% rename from ui/src/main/java/com/m3u/ui/model/Spacing.kt rename to material/src/main/java/com/m3u/material/model/Spacing.kt index 97a7376ea..94409162a 100644 --- a/ui/src/main/java/com/m3u/ui/model/Spacing.kt +++ b/material/src/main/java/com/m3u/material/model/Spacing.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.model +package com.m3u.material.model import androidx.compose.runtime.Immutable import androidx.compose.runtime.staticCompositionLocalOf diff --git a/ui/src/main/java/com/m3u/ui/model/StepColor.kt b/material/src/main/java/com/m3u/material/model/StepColor.kt similarity index 83% rename from ui/src/main/java/com/m3u/ui/model/StepColor.kt rename to material/src/main/java/com/m3u/material/model/StepColor.kt index 0c7222580..b7ea0fac3 100644 --- a/ui/src/main/java/com/m3u/ui/model/StepColor.kt +++ b/material/src/main/java/com/m3u/material/model/StepColor.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.model +package com.m3u.material.model import androidx.compose.runtime.Immutable import androidx.compose.ui.graphics.Color diff --git a/ui/src/main/java/com/m3u/ui/model/SugarColors.kt b/material/src/main/java/com/m3u/material/model/SugarColors.kt similarity index 94% rename from ui/src/main/java/com/m3u/ui/model/SugarColors.kt rename to material/src/main/java/com/m3u/material/model/SugarColors.kt index f88373447..4815c36af 100644 --- a/ui/src/main/java/com/m3u/ui/model/SugarColors.kt +++ b/material/src/main/java/com/m3u/material/model/SugarColors.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.model +package com.m3u.material.model import androidx.compose.ui.graphics.Color diff --git a/ui/src/main/java/com/m3u/ui/model/Theme.kt b/material/src/main/java/com/m3u/material/model/Theme.kt similarity index 99% rename from ui/src/main/java/com/m3u/ui/model/Theme.kt rename to material/src/main/java/com/m3u/material/model/Theme.kt index f9e69e7e7..75570fe6c 100644 --- a/ui/src/main/java/com/m3u/ui/model/Theme.kt +++ b/material/src/main/java/com/m3u/material/model/Theme.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.model +package com.m3u.material.model import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.graphics.Color diff --git a/settings.gradle.kts b/settings.gradle.kts index 1fde1f2cb..d7b9381a7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,7 +19,7 @@ rootProject.name = "M3U" include(":androidApp") include(":core") include(":data") -include(":ui") +include(":material") include( ":features:main", ":features:setting", @@ -33,3 +33,4 @@ include( include(":benchmark") include(":lint") include(":i18n") +include(":ui") diff --git a/ui/.gitignore b/ui/.gitignore index 3fe82fd65..42afabfd2 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -1,3 +1 @@ -/build -/src/test -/src/androidTest \ No newline at end of file +/build \ No newline at end of file diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index c66ddee37..bf36caeaa 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -6,11 +6,14 @@ plugins { android { namespace = "com.m3u.ui" compileSdk = 33 + defaultConfig { minSdk = 26 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } + buildTypes { release { isMinifyEnabled = true @@ -37,28 +40,14 @@ android { dependencies { implementation(project(":core")) + api(project(":material")) implementation(libs.androidx.core.core.ktx) - implementation(libs.androidx.activity.activity.compose) - - implementation(libs.androidx.lifecycle.lifecycle.runtime.ktx) - - api(libs.bundles.androidx.compose) - api(libs.androidx.compose.material.material.icons.extended) - api(libs.androidx.compose.material3.material3) - debugApi(libs.androidx.compose.ui.ui.tooling) - debugApi(libs.androidx.compose.ui.ui.tooling.preview) - - implementation(libs.androidx.tv.tv.foundation) - implementation(libs.androidx.tv.tv.material) - - api(libs.androidx.navigation.navigation.compose) - + implementation(libs.androidx.appcompat.appcompat) + implementation(libs.com.google.android.material.material) implementation(libs.androidx.media3.media3.ui) implementation(libs.androidx.media3.media3.exoplayer) - api(libs.io.coil.kt.coil) - api(libs.io.coil.kt.coil.compose) - - implementation(libs.com.airbnb.android.lottie.compose) -} + implementation(libs.androidx.tv.tv.foundation) + implementation(libs.androidx.tv.tv.material) +} \ No newline at end of file diff --git a/ui/proguard-rules.pro b/ui/proguard-rules.pro index ff59496d8..481bb4348 100644 --- a/ui/proguard-rules.pro +++ b/ui/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle.kts. +# proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/ui/src/main/java/com/m3u/ui/model/AppFont.kt b/ui/src/main/java/com/m3u/ui/AppFont.kt similarity index 97% rename from ui/src/main/java/com/m3u/ui/model/AppFont.kt rename to ui/src/main/java/com/m3u/ui/AppFont.kt index 4d2312a16..5dc556239 100644 --- a/ui/src/main/java/com/m3u/ui/model/AppFont.kt +++ b/ui/src/main/java/com/m3u/ui/AppFont.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.model +package com.m3u.ui import androidx.compose.material.Typography import androidx.compose.ui.text.TextStyle @@ -7,7 +7,6 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp -import com.m3u.ui.R object AppFont { val TitilliumWeb = FontFamily( diff --git a/ui/src/main/java/com/m3u/ui/Destination.kt b/ui/src/main/java/com/m3u/ui/Destination.kt index f57a3fe9c..47d46026c 100644 --- a/ui/src/main/java/com/m3u/ui/Destination.kt +++ b/ui/src/main/java/com/m3u/ui/Destination.kt @@ -9,7 +9,7 @@ import androidx.compose.material.icons.rounded.Collections import androidx.compose.material.icons.rounded.Home import androidx.compose.material.icons.rounded.Settings import androidx.compose.ui.graphics.vector.ImageVector -import com.m3u.i18n.R as I18R +import com.m3u.i18n.R.string typealias Navigate = (Destination) -> Unit @@ -23,20 +23,20 @@ sealed interface Destination { Main( selectedIcon = Icons.Rounded.Home, unselectedIcon = Icons.Outlined.Home, - iconTextId = I18R.string.ui_destination_main, - titleTextId = I18R.string.ui_app_name + iconTextId = string.ui_destination_main, + titleTextId = string.ui_app_name ), Favourite( selectedIcon = Icons.Rounded.Collections, unselectedIcon = Icons.Outlined.Collections, - iconTextId = I18R.string.ui_destination_favourite, - titleTextId = I18R.string.ui_title_favourite + iconTextId = string.ui_destination_favourite, + titleTextId = string.ui_title_favourite ), Setting( selectedIcon = Icons.Rounded.Settings, unselectedIcon = Icons.Outlined.Settings, - iconTextId = I18R.string.ui_destination_setting, - titleTextId = I18R.string.ui_title_setting + iconTextId = string.ui_destination_setting, + titleTextId = string.ui_title_setting ); companion object : Key diff --git a/ui/src/main/java/com/m3u/ui/ktx/EventHandler.kt b/ui/src/main/java/com/m3u/ui/EventHandler.kt similarity index 96% rename from ui/src/main/java/com/m3u/ui/ktx/EventHandler.kt rename to ui/src/main/java/com/m3u/ui/EventHandler.kt index c3ab2e5c0..a1a66b0e1 100644 --- a/ui/src/main/java/com/m3u/ui/ktx/EventHandler.kt +++ b/ui/src/main/java/com/m3u/ui/EventHandler.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.ktx +package com.m3u.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect diff --git a/ui/src/main/java/com/m3u/ui/LocalProvider.kt b/ui/src/main/java/com/m3u/ui/M3ULocalProvider.kt similarity index 82% rename from ui/src/main/java/com/m3u/ui/LocalProvider.kt rename to ui/src/main/java/com/m3u/ui/M3ULocalProvider.kt index 3ae39fc08..8e60f2069 100644 --- a/ui/src/main/java/com/m3u/ui/LocalProvider.kt +++ b/ui/src/main/java/com/m3u/ui/M3ULocalProvider.kt @@ -4,15 +4,11 @@ import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import com.m3u.ui.components.Background -import com.m3u.ui.model.DayTheme -import com.m3u.ui.model.EmptyHelper -import com.m3u.ui.model.Helper -import com.m3u.ui.model.LocalHelper -import com.m3u.ui.model.LocalTheme -import com.m3u.ui.model.NightTheme -import com.m3u.ui.model.Theme -import com.m3u.ui.model.Typography +import com.m3u.material.components.Background +import com.m3u.material.model.DayTheme +import com.m3u.material.model.LocalTheme +import com.m3u.material.model.NightTheme +import com.m3u.material.model.Theme import androidx.compose.material3.MaterialTheme as Material3Theme @Composable diff --git a/ui/src/main/java/com/m3u/ui/components/MonoText.kt b/ui/src/main/java/com/m3u/ui/MonoText.kt similarity index 95% rename from ui/src/main/java/com/m3u/ui/components/MonoText.kt rename to ui/src/main/java/com/m3u/ui/MonoText.kt index 9da6dcea6..0034c55a6 100644 --- a/ui/src/main/java/com/m3u/ui/components/MonoText.kt +++ b/ui/src/main/java/com/m3u/ui/MonoText.kt @@ -1,6 +1,4 @@ -@file:Suppress("unused") - -package com.m3u.ui.components +package com.m3u.ui import androidx.compose.material.LocalTextStyle import androidx.compose.material.Text @@ -15,7 +13,6 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.TextUnit -import com.m3u.ui.model.AppFont @Composable fun MonoText( diff --git a/ui/src/main/java/com/m3u/ui/model/Helper.kt b/ui/src/main/java/com/m3u/ui/OnUserLeaveHint.kt similarity index 98% rename from ui/src/main/java/com/m3u/ui/model/Helper.kt rename to ui/src/main/java/com/m3u/ui/OnUserLeaveHint.kt index 18140853d..5ee9a7ab7 100644 --- a/ui/src/main/java/com/m3u/ui/model/Helper.kt +++ b/ui/src/main/java/com/m3u/ui/OnUserLeaveHint.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.model +package com.m3u.ui import android.annotation.SuppressLint import android.graphics.Rect @@ -12,8 +12,7 @@ import androidx.core.app.PictureInPictureModeChangedInfo import androidx.core.util.Consumer import androidx.lifecycle.Lifecycle import com.m3u.core.unspecified.UBoolean -import com.m3u.ui.Destination -import com.m3u.ui.ktx.LifecycleEffect +import com.m3u.material.ktx.LifecycleEffect typealias OnUserLeaveHint = () -> Unit typealias OnPipModeChanged = Consumer diff --git a/ui/src/main/java/com/m3u/ui/components/Player.kt b/ui/src/main/java/com/m3u/ui/Player.kt similarity index 98% rename from ui/src/main/java/com/m3u/ui/components/Player.kt rename to ui/src/main/java/com/m3u/ui/Player.kt index 031e87501..9a206f64f 100644 --- a/ui/src/main/java/com/m3u/ui/components/Player.kt +++ b/ui/src/main/java/com/m3u/ui/Player.kt @@ -1,4 +1,4 @@ -package com.m3u.ui.components +package com.m3u.ui import android.view.ViewGroup import androidx.annotation.OptIn @@ -58,7 +58,7 @@ fun rememberPlayerState( @OptIn(UnstableApi::class) @Composable -fun ExoPlayer( +fun Player( state: PlayerState, modifier: Modifier = Modifier ) { diff --git a/ui/src/main/java/com/m3u/ui/ResumeEvent.kt b/ui/src/main/java/com/m3u/ui/ResumeEvent.kt new file mode 100644 index 000000000..d5d37268c --- /dev/null +++ b/ui/src/main/java/com/m3u/ui/ResumeEvent.kt @@ -0,0 +1,5 @@ +package com.m3u.ui + +import com.m3u.core.wrapper.Event + +typealias ResumeEvent = Event \ No newline at end of file diff --git a/ui/src/main/java/com/m3u/ui/ScrollableState.kt b/ui/src/main/java/com/m3u/ui/ScrollableState.kt new file mode 100644 index 000000000..e5d5ab65e --- /dev/null +++ b/ui/src/main/java/com/m3u/ui/ScrollableState.kt @@ -0,0 +1,9 @@ +package com.m3u.ui + +import androidx.tv.foundation.lazy.grid.TvLazyGridState + +val TvLazyGridState.isAtTop + get() = firstVisibleItemIndex == 0 && firstVisibleItemScrollOffset == 0 + +val TvLazyGridState.isScrolled + get() = firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0