Skip to content

Commit 7816091

Browse files
authored
Merge pull request #38 from TeamNewPipe/light_theme
add light theme option
2 parents c1a230f + 4dc6a07 commit 7816091

37 files changed

+402
-412
lines changed

new-player/src/main/java/net/newpipe/newplayer/ui/LoadingPlaceholder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import androidx.compose.ui.Alignment
3737
import androidx.compose.ui.Modifier
3838
import androidx.compose.ui.tooling.preview.Preview
3939
import androidx.compose.ui.unit.dp
40-
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
40+
import net.newpipe.newplayer.ui.theme.VideoPlayerDarkTheme
4141

4242
@Composable
4343

@@ -64,7 +64,7 @@ internal fun LoadingPlaceholder(aspectRatio: Float = 3F / 1F) {
6464
@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape")
6565
@Composable
6666
private fun VideoPlayerLoaidingPlaceholderPreview() {
67-
VideoPlayerTheme {
67+
VideoPlayerDarkTheme {
6868
LoadingPlaceholder()
6969
}
7070
}

new-player/src/main/java/net/newpipe/newplayer/ui/NewPlayerUI.kt

Lines changed: 115 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import android.os.Build
2525
import android.util.Log
2626
import androidx.activity.compose.BackHandler
2727
import androidx.annotation.OptIn
28+
import androidx.compose.foundation.isSystemInDarkTheme
29+
import androidx.compose.material3.ColorScheme
30+
import androidx.compose.material3.MaterialTheme
2831
import androidx.compose.material3.adaptive.currentWindowSize
2932
import androidx.compose.runtime.Composable
3033
import androidx.compose.runtime.LaunchedEffect
@@ -35,6 +38,8 @@ import androidx.compose.ui.tooling.preview.Preview
3538
import androidx.core.view.WindowCompat
3639
import androidx.core.view.WindowInsetsCompat
3740
import androidx.core.view.WindowInsetsControllerCompat
41+
import androidx.compose.material3.Typography
42+
3843

3944
import androidx.media3.common.util.UnstableApi
4045
import net.newpipe.newplayer.data.NewPlayerException
@@ -43,15 +48,16 @@ import net.newpipe.newplayer.uiModel.InternalNewPlayerViewModel
4348
import net.newpipe.newplayer.uiModel.NewPlayerViewModel
4449
import net.newpipe.newplayer.uiModel.NewPlayerViewModelDummy
4550
import net.newpipe.newplayer.ui.audioplayer.AudioPlayerUI
46-
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
51+
import net.newpipe.newplayer.ui.theme.VideoPlayerDarkTheme
4752
import net.newpipe.newplayer.ui.videoplayer.VideoPlayerUi
4853
import net.newpipe.newplayer.ui.common.LockScreenOrientation
4954
import net.newpipe.newplayer.ui.common.activity
50-
import net.newpipe.newplayer.ui.common.findActivity
5155
import net.newpipe.newplayer.ui.common.getDefaultBrightness
5256
import net.newpipe.newplayer.ui.common.isInPowerSaveMode
5357
import net.newpipe.newplayer.ui.common.setScreenBrightness
54-
import net.newpipe.newplayer.ui.common.window
58+
import net.newpipe.newplayer.ui.theme.NewPlayerTypography
59+
import net.newpipe.newplayer.ui.theme.NewPlayerDarkColorScheme
60+
import net.newpipe.newplayer.ui.theme.NewPlayerLightColorScheme
5561

5662
private const val TAG = "VideoPlayerUI"
5763

@@ -65,122 +71,138 @@ private const val TAG = "VideoPlayerUI"
6571
* or views are hidden and only NewPlayerUI is visible. You can read more about this in
6672
* the [NewPlayerViewModel], since the [viewModel] is responsible to tell your UI how to behave
6773
* in such cases.
74+
*
75+
* @param viewModel the NewPlayerViewModel that should control this NewPlayerUi.
76+
* @param lightColorScheme is the color scheme used by non video ui related composables, when the system is in light mode.
77+
* @param darkColorScheme is the color scheme used by the video ui related composables, and the rest of the ui if the system is in dark mode.
78+
* @param the typography used by NewPlayer
6879
*/
6980
@OptIn(UnstableApi::class)
7081
@Composable
7182
fun NewPlayerUI(
7283
viewModel: NewPlayerViewModel?,
84+
lightColorScheme: ColorScheme = NewPlayerLightColorScheme,
85+
darkColorScheme: ColorScheme = NewPlayerDarkColorScheme,
86+
typography: Typography = NewPlayerTypography
7387
) {
74-
if (viewModel !is InternalNewPlayerViewModel?) {
75-
throw NewPlayerException(
76-
"The view model given to NewPlayerUI must be of type InternalNewPlayerViewModel. "
77-
+ "This can not be implemented externally, so do not extend NewPlayerViewModel"
78-
)
79-
}
80-
81-
if (viewModel == null) {
82-
LoadingPlaceholder()
83-
} else if (viewModel.newPlayer == null) {
84-
LoadingPlaceholder(viewModel.uiState.collectAsState().value.embeddedUiRatio)
85-
} else {
86-
val uiState by viewModel.uiState.collectAsState()
88+
MaterialTheme(colorScheme = darkColorScheme, typography = typography) {
89+
if (viewModel !is InternalNewPlayerViewModel?) {
90+
throw NewPlayerException(
91+
"The view model given to NewPlayerUI must be of type InternalNewPlayerViewModel. "
92+
+ "This can not be implemented externally, so do not extend NewPlayerViewModel"
93+
)
94+
}
8795

88-
// find out whether application is light or dark mode from LocalContext.current
89-
val view = LocalView.current
90-
val activity = activity()
91-
val window = activity.window
96+
if (viewModel == null) {
97+
LoadingPlaceholder()
98+
} else if (viewModel.newPlayer == null) {
99+
LoadingPlaceholder(viewModel.uiState.collectAsState().value.embeddedUiRatio)
100+
} else {
101+
val uiState by viewModel.uiState.collectAsState()
92102

93-
// Setup fullscreen
103+
// find out whether application is light or dark mode from LocalContext.current
104+
val view = LocalView.current
105+
val activity = activity()
106+
val window = activity.window
94107

108+
// Setup fullscreen
95109

96-
LaunchedEffect(uiState.uiMode.fullscreen) {
97-
if (uiState.uiMode.fullscreen) {
98-
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars =
99-
false
100-
} else {
101-
uiState.embeddedUiConfig?.let {
110+
val isInDarkTheme = isSystemInDarkTheme()
111+
LaunchedEffect(uiState.uiMode.forceSysUiDarkThemeMode, key2 = isInDarkTheme) {
112+
if (uiState.uiMode.forceSysUiDarkThemeMode) {
102113
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars =
103-
it.systemBarInLightMode
114+
false
115+
} else {
116+
uiState.embeddedUiConfig?.let {
117+
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = isInDarkTheme
118+
}
104119
}
105120
}
106-
}
107121

108-
if (uiState.uiMode.fullscreen) {
109-
BackHandler {
110-
viewModel.onBackPressed()
122+
if (uiState.uiMode.fullscreen) {
123+
BackHandler {
124+
viewModel.onBackPressed()
125+
}
111126
}
112-
}
113127

114-
// setup immersive mode
115-
LaunchedEffect(
116-
key1 = uiState.uiMode.systemInsetsVisible,
117-
) {
118-
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
119-
windowInsetsController.systemBarsBehavior =
120-
WindowInsetsControllerCompat.BEHAVIOR_DEFAULT
121-
122-
if (uiState.uiMode.systemInsetsVisible) {
123-
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
124-
} else {
125-
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
128+
// setup immersive mode
129+
LaunchedEffect(
130+
key1 = uiState.uiMode.systemInsetsVisible,
131+
) {
132+
val windowInsetsController =
133+
WindowCompat.getInsetsController(window, window.decorView)
134+
windowInsetsController.systemBarsBehavior =
135+
WindowInsetsControllerCompat.BEHAVIOR_DEFAULT
136+
137+
if (uiState.uiMode.systemInsetsVisible) {
138+
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
139+
} else {
140+
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
141+
}
126142
}
127-
}
128143

129-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
130-
val isInPowerSaveMode = isInPowerSaveMode()
131-
LaunchedEffect(key1 = isInPowerSaveMode) {
132-
viewModel.deviceInPowerSaveMode = isInPowerSaveMode
144+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
145+
val isInPowerSaveMode = isInPowerSaveMode()
146+
LaunchedEffect(key1 = isInPowerSaveMode) {
147+
viewModel.deviceInPowerSaveMode = isInPowerSaveMode
148+
}
133149
}
134-
}
135150

136-
if (uiState.uiMode.fitScreenRotation) {
137-
if (uiState.contentRatio < 1) {
138-
LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
151+
if (uiState.uiMode.fitScreenRotation) {
152+
if (uiState.contentRatio < 1) {
153+
LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
154+
} else {
155+
LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
156+
}
139157
} else {
140-
LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
141-
}
142-
} else {
143-
uiState.embeddedUiConfig?.let {
144-
LockScreenOrientation(orientation = it.screenOrientation)
158+
uiState.embeddedUiConfig?.let {
159+
LockScreenOrientation(orientation = it.screenOrientation)
160+
}
145161
}
146-
}
147-
148-
val defaultBrightness = activity.getDefaultBrightness()
149-
LaunchedEffect(key1 = uiState.brightness) {
150-
Log.d(TAG, "New Brightness: ${uiState.brightness}")
151162

152-
setScreenBrightness(
153-
if (uiState.brightness < 0) defaultBrightness else uiState.brightness, activity
154-
)
155-
}
163+
val defaultBrightness = activity.getDefaultBrightness()
164+
LaunchedEffect(key1 = uiState.brightness) {
165+
Log.d(TAG, "New Brightness: ${uiState.brightness}")
156166

157-
when (uiState.uiMode) {
158-
159-
UIModeState.FULLSCREEN_VIDEO,
160-
UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI,
161-
UIModeState.FULLSCREEN_VIDEO_CHAPTER_SELECT,
162-
UIModeState.FULLSCREEN_VIDEO_STREAM_SELECT,
163-
UIModeState.EMBEDDED_VIDEO,
164-
UIModeState.EMBEDDED_VIDEO_CONTROLLER_UI,
165-
UIModeState.EMBEDDED_VIDEO_STREAM_SELECT,
166-
UIModeState.EMBEDDED_VIDEO_CHAPTER_SELECT,
167-
UIModeState.PIP -> {
168-
VideoPlayerUi(viewModel = viewModel, uiState = uiState)
169-
}
170-
171-
UIModeState.FULLSCREEN_AUDIO,
172-
UIModeState.EMBEDDED_AUDIO,
173-
UIModeState.AUDIO_STREAM_SELECT,
174-
UIModeState.AUDIO_CHAPTER_SELECT -> {
175-
val windowSize = currentWindowSize()
176-
AudioPlayerUI(
177-
viewModel = viewModel, uiState = uiState,
178-
isLandScape = windowSize.height < windowSize.width
167+
setScreenBrightness(
168+
if (uiState.brightness < 0) defaultBrightness else uiState.brightness, activity
179169
)
180170
}
181171

182-
else -> {
183-
LoadingPlaceholder(uiState.embeddedUiRatio)
172+
when (uiState.uiMode) {
173+
174+
UIModeState.FULLSCREEN_VIDEO,
175+
UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI,
176+
UIModeState.FULLSCREEN_VIDEO_CHAPTER_SELECT,
177+
UIModeState.FULLSCREEN_VIDEO_STREAM_SELECT,
178+
UIModeState.EMBEDDED_VIDEO,
179+
UIModeState.EMBEDDED_VIDEO_CONTROLLER_UI,
180+
UIModeState.EMBEDDED_VIDEO_STREAM_SELECT,
181+
UIModeState.EMBEDDED_VIDEO_CHAPTER_SELECT,
182+
UIModeState.PIP -> {
183+
VideoPlayerUi(viewModel = viewModel, uiState = uiState)
184+
}
185+
186+
UIModeState.FULLSCREEN_AUDIO,
187+
UIModeState.EMBEDDED_AUDIO,
188+
UIModeState.AUDIO_STREAM_SELECT,
189+
UIModeState.AUDIO_CHAPTER_SELECT -> {
190+
val windowSize = currentWindowSize()
191+
MaterialTheme(
192+
colorScheme =
193+
if (isInDarkTheme) darkColorScheme else lightColorScheme,
194+
typography = typography
195+
) {
196+
AudioPlayerUI(
197+
viewModel = viewModel, uiState = uiState,
198+
isLandScape = windowSize.height < windowSize.width
199+
)
200+
}
201+
}
202+
203+
else -> {
204+
LoadingPlaceholder(uiState.embeddedUiRatio)
205+
}
184206
}
185207
}
186208
}
@@ -191,7 +213,7 @@ fun NewPlayerUI(
191213
@Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape")
192214
@Composable
193215
private fun PlayerUIPreviewEmbedded() {
194-
VideoPlayerTheme {
216+
VideoPlayerDarkTheme {
195217
NewPlayerUI(viewModel = NewPlayerViewModelDummy())
196218
}
197219
}

new-player/src/main/java/net/newpipe/newplayer/ui/NewPlayerView.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ import dagger.hilt.android.AndroidEntryPoint
3030
import net.newpipe.newplayer.R
3131
import net.newpipe.newplayer.uiModel.InternalNewPlayerViewModel
3232
import net.newpipe.newplayer.uiModel.NewPlayerViewModel
33-
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
33+
import net.newpipe.newplayer.ui.theme.VideoPlayerDarkTheme
3434
import net.newpipe.newplayer.data.NewPlayerException
35+
import net.newpipe.newplayer.ui.theme.VideoPlayerLightTheme
3536

3637

3738
/**
@@ -49,7 +50,7 @@ class NewPlayerView : FrameLayout {
4950
applyViewModel()
5051
}
5152

52-
private val composeView:ComposeView
53+
private val composeView: ComposeView
5354

5455
@JvmOverloads
5556
constructor(
@@ -67,9 +68,7 @@ class NewPlayerView : FrameLayout {
6768
composeView.apply {
6869
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
6970
setContent {
70-
VideoPlayerTheme {
71-
NewPlayerUI(viewModel = viewModel as InternalNewPlayerViewModel?)
72-
}
71+
NewPlayerUI(viewModel = viewModel as InternalNewPlayerViewModel?)
7372
}
7473
}
7574
}

new-player/src/main/java/net/newpipe/newplayer/ui/audioplayer/AudioPlaybackControllerUI.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import net.newpipe.newplayer.R
5252
import net.newpipe.newplayer.uiModel.NewPlayerUIState
5353
import net.newpipe.newplayer.uiModel.InternalNewPlayerViewModel
5454
import net.newpipe.newplayer.uiModel.NewPlayerViewModelDummy
55-
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
55+
import net.newpipe.newplayer.ui.theme.VideoPlayerDarkTheme
5656

5757
@androidx.annotation.OptIn(UnstableApi::class)
5858
@Composable
@@ -200,7 +200,7 @@ internal fun AudioPlaybackControllerUI(
200200
@Preview(device = "id:pixel_6")
201201
@Composable
202202
private fun AudioPlayerControllerPreview() {
203-
VideoPlayerTheme {
203+
VideoPlayerDarkTheme {
204204
AudioPlaybackControllerUI(
205205
viewModel = NewPlayerViewModelDummy(),
206206
uiState = NewPlayerUIState.DUMMY.copy(playList = emptyList(), isLoading = false)

new-player/src/main/java/net/newpipe/newplayer/ui/audioplayer/AudioPlayerEmbeddedUI.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import net.newpipe.newplayer.uiModel.NewPlayerUIState
4646
import net.newpipe.newplayer.uiModel.InternalNewPlayerViewModel
4747
import net.newpipe.newplayer.uiModel.NewPlayerViewModelDummy
4848
import net.newpipe.newplayer.uiModel.UIModeState
49-
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
49+
import net.newpipe.newplayer.ui.theme.VideoPlayerDarkTheme
5050
import net.newpipe.newplayer.ui.videoplayer.CONTROLLER_UI_BACKGROUND_COLOR
5151
import net.newpipe.newplayer.ui.videoplayer.PreviewBackgroundSurface
5252
import net.newpipe.newplayer.ui.common.Thumbnail
@@ -59,7 +59,7 @@ import net.newpipe.newplayer.ui.common.getTimeStringFromMs
5959

6060
/** @hide */
6161
internal fun AudioPlayerEmbeddedUI(viewModel: InternalNewPlayerViewModel, uiState: NewPlayerUIState) {
62-
val locale = getLocale()!!
62+
val locale = getLocale()
6363

6464
val embeddedUIConfig = getEmbeddedUiConfig()
6565

@@ -119,7 +119,7 @@ internal fun AudioPlayerEmbeddedUI(viewModel: InternalNewPlayerViewModel, uiStat
119119
top = 0.5.dp,
120120
bottom = 0.5.dp
121121
),
122-
color = MaterialTheme.colorScheme.onBackground,
122+
color = MaterialTheme.colorScheme.onPrimary,
123123
text = getTimeStringFromMs(
124124
uiState.durationInMs,
125125
locale,
@@ -162,7 +162,7 @@ internal fun AudioPlayerEmbeddedUI(viewModel: InternalNewPlayerViewModel, uiStat
162162
@Preview(device = "spec:width=1080px,height=1080px,dpi=440,orientation=landscape")
163163
@Composable
164164
private fun AudioPlayerEmbeddedPreview() {
165-
VideoPlayerTheme {
165+
VideoPlayerDarkTheme {
166166
PreviewBackgroundSurface {
167167
AudioPlayerEmbeddedUI(
168168
viewModel = NewPlayerViewModelDummy(),

0 commit comments

Comments
 (0)