Merge pull request #10875 from snaik20/intro-jetpack-compose
Introducing Jetpack Compose in NewPipe
This commit is contained in:
commit
d479f29e9b
|
@ -92,6 +92,7 @@ android {
|
|||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
compose true
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
|
@ -103,6 +104,10 @@ android {
|
|||
'META-INF/COPYRIGHT']
|
||||
}
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.3"
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
|
@ -284,6 +289,12 @@ dependencies {
|
|||
// Date and time formatting
|
||||
implementation "org.ocpsoft.prettytime:prettytime:5.0.7.Final"
|
||||
|
||||
// Jetpack Compose
|
||||
implementation(platform('androidx.compose:compose-bom:2024.02.01'))
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation 'androidx.activity:activity-compose'
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
|
||||
/** Debugging **/
|
||||
// Memory leak detection
|
||||
debugImplementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
|
||||
|
@ -293,6 +304,9 @@ dependencies {
|
|||
debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
|
||||
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoVersion}"
|
||||
|
||||
// Jetpack Compose
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||
|
||||
/** Testing **/
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.mockito:mockito-core:5.6.0'
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
package org.schabi.newpipe.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SearchBar
|
||||
import androidx.compose.material3.SearchBarDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.ui.theme.AppTheme
|
||||
import org.schabi.newpipe.ui.theme.SizeTokens
|
||||
|
||||
@Composable
|
||||
fun TextAction(text: String, modifier: Modifier = Modifier) {
|
||||
Text(text = text, color = MaterialTheme.colorScheme.onSurface, modifier = modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavigationIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back",
|
||||
modifier = Modifier.padding(horizontal = SizeTokens.SpacingExtraSmall)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SearchSuggestionItem(text: String) {
|
||||
// TODO: Add more components here to display all the required details of a search suggestion item.
|
||||
Text(text = text)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun Toolbar(
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
hasNavigationIcon: Boolean = true,
|
||||
hasSearch: Boolean = false,
|
||||
onSearchQueryChange: ((String) -> List<String>)? = null,
|
||||
actions: @Composable RowScope.() -> Unit = {}
|
||||
) {
|
||||
var isSearchActive by remember { mutableStateOf(false) }
|
||||
var query by remember { mutableStateOf("") }
|
||||
|
||||
Column {
|
||||
TopAppBar(
|
||||
title = { Text(text = title) },
|
||||
modifier = modifier,
|
||||
navigationIcon = { if (hasNavigationIcon) NavigationIcon() },
|
||||
actions = {
|
||||
actions()
|
||||
if (hasSearch) {
|
||||
IconButton(onClick = { isSearchActive = true }) {
|
||||
Icon(
|
||||
painterResource(id = R.drawable.ic_search),
|
||||
contentDescription = stringResource(id = R.string.search),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
if (isSearchActive) {
|
||||
SearchBar(
|
||||
query = query,
|
||||
onQueryChange = { query = it },
|
||||
onSearch = {},
|
||||
placeholder = {
|
||||
Text(text = stringResource(id = R.string.search))
|
||||
},
|
||||
active = true,
|
||||
onActiveChange = {
|
||||
isSearchActive = it
|
||||
},
|
||||
colors = SearchBarDefaults.colors(
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
inputFieldColors = SearchBarDefaults.inputFieldColors(
|
||||
focusedTextColor = MaterialTheme.colorScheme.onBackground,
|
||||
unfocusedTextColor = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
)
|
||||
) {
|
||||
onSearchQueryChange?.invoke(query)?.takeIf { it.isNotEmpty() }
|
||||
?.map { suggestionText -> SearchSuggestionItem(text = suggestionText) }
|
||||
?: run {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column {
|
||||
Text(text = "╰(°●°╰)")
|
||||
Text(text = stringResource(id = R.string.search_no_results))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun ToolbarPreview() {
|
||||
AppTheme {
|
||||
Toolbar(
|
||||
title = "Title",
|
||||
hasSearch = true,
|
||||
onSearchQueryChange = { emptyList() },
|
||||
actions = {
|
||||
TextAction(text = "Action1")
|
||||
TextAction(text = "Action2")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package org.schabi.newpipe.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val md_theme_light_primary = Color(0xFFBB171C)
|
||||
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_primaryContainer = Color(0xFFFFDAD6)
|
||||
val md_theme_light_onPrimaryContainer = Color(0xFF410002)
|
||||
val md_theme_light_secondary = Color(0xFF984061)
|
||||
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_secondaryContainer = Color(0xFFFFD9E2)
|
||||
val md_theme_light_onSecondaryContainer = Color(0xFF3E001D)
|
||||
val md_theme_light_tertiary = Color(0xFF006874)
|
||||
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_tertiaryContainer = Color(0xFF97F0FF)
|
||||
val md_theme_light_onTertiaryContainer = Color(0xFF001F24)
|
||||
val md_theme_light_error = Color(0xFFBA1A1A)
|
||||
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
|
||||
val md_theme_light_onError = Color(0xFFFFFFFF)
|
||||
val md_theme_light_onErrorContainer = Color(0xFF410002)
|
||||
val md_theme_light_background = Color(0xFFEEEEEE)
|
||||
val md_theme_light_onBackground = Color(0xFF1B1B1B)
|
||||
val md_theme_light_surface = Color(0xFFE53835)
|
||||
val md_theme_light_onSurface = Color(0xFFFFFFFF)
|
||||
val md_theme_light_surfaceVariant = Color(0xFFF5DDDB)
|
||||
val md_theme_light_onSurfaceVariant = Color(0xFF534341)
|
||||
val md_theme_light_outline = Color(0xFF857371)
|
||||
val md_theme_light_inverseOnSurface = Color(0xFFD6F6FF)
|
||||
val md_theme_light_inverseSurface = Color(0xFF00363F)
|
||||
val md_theme_light_inversePrimary = Color(0xFFFFB4AC)
|
||||
val md_theme_light_surfaceTint = Color(0xFFBB171C)
|
||||
val md_theme_light_outlineVariant = Color(0xFFD8C2BF)
|
||||
val md_theme_light_scrim = Color(0xFF000000)
|
||||
|
||||
val md_theme_dark_primary = Color(0xFFFFB4AC)
|
||||
val md_theme_dark_onPrimary = Color(0xFF690006)
|
||||
val md_theme_dark_primaryContainer = Color(0xFF93000D)
|
||||
val md_theme_dark_onPrimaryContainer = Color(0xFFFFDAD6)
|
||||
val md_theme_dark_secondary = Color(0xFFFFB1C8)
|
||||
val md_theme_dark_onSecondary = Color(0xFF5E1133)
|
||||
val md_theme_dark_secondaryContainer = Color(0xFF7B2949)
|
||||
val md_theme_dark_onSecondaryContainer = Color(0xFFFFD9E2)
|
||||
val md_theme_dark_tertiary = Color(0xFF4FD8EB)
|
||||
val md_theme_dark_onTertiary = Color(0xFF00363D)
|
||||
val md_theme_dark_tertiaryContainer = Color(0xFF004F58)
|
||||
val md_theme_dark_onTertiaryContainer = Color(0xFF97F0FF)
|
||||
val md_theme_dark_error = Color(0xFFFFB4AB)
|
||||
val md_theme_dark_errorContainer = Color(0xFF93000A)
|
||||
val md_theme_dark_onError = Color(0xFF690005)
|
||||
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
|
||||
val md_theme_dark_background = Color(0xFF212121)
|
||||
val md_theme_dark_onBackground = Color(0xFFFFFFFF)
|
||||
val md_theme_dark_surface = Color(0xFF992521)
|
||||
val md_theme_dark_onSurface = Color(0xFFFFFFFF)
|
||||
val md_theme_dark_surfaceVariant = Color(0xFF534341)
|
||||
val md_theme_dark_onSurfaceVariant = Color(0xFFD8C2BF)
|
||||
val md_theme_dark_outline = Color(0xFFA08C8A)
|
||||
val md_theme_dark_inverseOnSurface = Color(0xFF001F25)
|
||||
val md_theme_dark_inverseSurface = Color(0xFFA6EEFF)
|
||||
val md_theme_dark_inversePrimary = Color(0xFFBB171C)
|
||||
val md_theme_dark_surfaceTint = Color(0xFFFFB4AC)
|
||||
val md_theme_dark_outlineVariant = Color(0xFF534341)
|
||||
val md_theme_dark_scrim = Color(0xFF000000)
|
|
@ -0,0 +1,13 @@
|
|||
package org.schabi.newpipe.ui.theme
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
internal object SizeTokens {
|
||||
val SpacingExtraSmall = 4.dp
|
||||
val SpacingSmall = 8.dp
|
||||
val SpacingMedium = 16.dp
|
||||
val SpacingLarge = 24.dp
|
||||
val SpacingExtraLarge = 32.dp
|
||||
|
||||
val SpaceMinSize = 44.dp // Minimum tappable size required for accessibility
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package org.schabi.newpipe.ui.theme
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
private val LightColors = lightColorScheme(
|
||||
primary = md_theme_light_primary,
|
||||
onPrimary = md_theme_light_onPrimary,
|
||||
primaryContainer = md_theme_light_primaryContainer,
|
||||
onPrimaryContainer = md_theme_light_onPrimaryContainer,
|
||||
secondary = md_theme_light_secondary,
|
||||
onSecondary = md_theme_light_onSecondary,
|
||||
secondaryContainer = md_theme_light_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_light_onSecondaryContainer,
|
||||
tertiary = md_theme_light_tertiary,
|
||||
onTertiary = md_theme_light_onTertiary,
|
||||
tertiaryContainer = md_theme_light_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_light_onTertiaryContainer,
|
||||
error = md_theme_light_error,
|
||||
errorContainer = md_theme_light_errorContainer,
|
||||
onError = md_theme_light_onError,
|
||||
onErrorContainer = md_theme_light_onErrorContainer,
|
||||
background = md_theme_light_background,
|
||||
onBackground = md_theme_light_onBackground,
|
||||
surface = md_theme_light_surface,
|
||||
onSurface = md_theme_light_onSurface,
|
||||
surfaceVariant = md_theme_light_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_light_onSurfaceVariant,
|
||||
outline = md_theme_light_outline,
|
||||
inverseOnSurface = md_theme_light_inverseOnSurface,
|
||||
inverseSurface = md_theme_light_inverseSurface,
|
||||
inversePrimary = md_theme_light_inversePrimary,
|
||||
surfaceTint = md_theme_light_surfaceTint,
|
||||
outlineVariant = md_theme_light_outlineVariant,
|
||||
scrim = md_theme_light_scrim,
|
||||
)
|
||||
|
||||
private val DarkColors = darkColorScheme(
|
||||
primary = md_theme_dark_primary,
|
||||
onPrimary = md_theme_dark_onPrimary,
|
||||
primaryContainer = md_theme_dark_primaryContainer,
|
||||
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
|
||||
secondary = md_theme_dark_secondary,
|
||||
onSecondary = md_theme_dark_onSecondary,
|
||||
secondaryContainer = md_theme_dark_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
|
||||
tertiary = md_theme_dark_tertiary,
|
||||
onTertiary = md_theme_dark_onTertiary,
|
||||
tertiaryContainer = md_theme_dark_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
|
||||
error = md_theme_dark_error,
|
||||
errorContainer = md_theme_dark_errorContainer,
|
||||
onError = md_theme_dark_onError,
|
||||
onErrorContainer = md_theme_dark_onErrorContainer,
|
||||
background = md_theme_dark_background,
|
||||
onBackground = md_theme_dark_onBackground,
|
||||
surface = md_theme_dark_surface,
|
||||
onSurface = md_theme_dark_onSurface,
|
||||
surfaceVariant = md_theme_dark_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
|
||||
outline = md_theme_dark_outline,
|
||||
inverseOnSurface = md_theme_dark_inverseOnSurface,
|
||||
inverseSurface = md_theme_dark_inverseSurface,
|
||||
inversePrimary = md_theme_dark_inversePrimary,
|
||||
surfaceTint = md_theme_dark_surfaceTint,
|
||||
outlineVariant = md_theme_dark_outlineVariant,
|
||||
scrim = md_theme_dark_scrim,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun AppTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
|
||||
MaterialTheme(
|
||||
colorScheme = if (useDarkTheme) DarkColors else LightColors,
|
||||
content = content
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue