refactor ui components

This commit is contained in:
X1nto 2022-02-22 22:27:10 +04:00
parent 5cf2cd1cbe
commit 9d73cae456
87 changed files with 1884 additions and 2547 deletions

View File

@ -1,3 +1,6 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
plugins {
id("com.android.application")
kotlin("android")
@ -57,13 +60,21 @@ android {
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs = freeCompilerArgs + "-Xopt-in=kotlin.RequiresOptIn"
optIn("androidx.compose.material3.ExperimentalMaterial3Api")
optIn("androidx.compose.animation.ExperimentalAnimationApi")
optIn("androidx.compose.foundation.ExperimentalFoundationApi")
}
}
fun KotlinJvmOptions.optIn(library: String) {
freeCompilerArgs = freeCompilerArgs +
"-opt-in=$library"
}
val languages: String get() {
val langs = arrayListOf("en", "bn_BD", "bn_IN", "pa_IN", "pa_PK", "pt_BR", "pt_PT", "zh_CN", "zh_TW")
val exceptions = arrayOf("bn", "pa", "pt", "zh")

View File

@ -7,9 +7,7 @@ import android.content.IntentFilter
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
import com.github.zsoltk.compose.backpress.BackPressHandler
@ -18,11 +16,11 @@ import com.github.zsoltk.compose.router.Router
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.vanced.manager.core.installer.service.AppInstallService
import com.vanced.manager.core.installer.service.AppUninstallService
import com.vanced.manager.ui.component.color.managerSurfaceColor
import com.vanced.manager.ui.screens.*
import com.vanced.manager.ui.screen.*
import com.vanced.manager.ui.theme.ManagerTheme
import com.vanced.manager.ui.theme.isDark
import com.vanced.manager.ui.util.Screen
import com.vanced.manager.ui.util.animated
import com.vanced.manager.ui.viewmodel.InstallViewModel
import com.vanced.manager.ui.viewmodel.MainViewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
@ -52,17 +50,12 @@ class MainActivity : ComponentActivity() {
}
}
@OptIn(
ExperimentalAnimationApi::class,
ExperimentalMaterial3Api::class,
ExperimentalFoundationApi::class,
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainViewModel.fetch()
setContent {
ManagerTheme {
val surfaceColor = managerSurfaceColor()
val surfaceColor = MaterialTheme.colorScheme.surface.animated
val isDark = isDark()
@ -81,12 +74,12 @@ class MainActivity : ComponentActivity() {
Router<Screen>("VancedManager", Screen.Home) { backStack ->
when (val screen = backStack.last()) {
is Screen.Home -> {
HomeLayout(
HomeScreen(
viewModel = mainViewModel,
onToolbarScreenSelected = {
backStack.push(it)
},
onAppInstallPress = { appName, appVersions, installationOptions ->
onAppDownloadClick = { appName, appVersions, installationOptions ->
if (installationOptions != null) {
backStack.push(
Screen.Configuration(
@ -98,18 +91,24 @@ class MainActivity : ComponentActivity() {
} else {
backStack.push(Screen.Install(appName, appVersions))
}
},
onAppLaunchClick = { appName, packageName ->
mainViewModel.launchApp(appName, packageName)
},
onAppUninstallClick = { packageName ->
mainViewModel.uninstallApp(packageName)
}
)
}
is Screen.Settings -> {
SettingsLayout(
SettingsScreen(
onToolbarBackButtonClick = {
backStack.pop()
}
)
}
is Screen.About -> {
AboutLayout(
AboutScreen(
onToolbarBackButtonClick = {
backStack.pop()
}

View File

@ -0,0 +1,148 @@
package com.vanced.manager.ui.component
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
@Composable
fun ManagerButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
shape: Shape = RoundedCornerShape(20.0.dp),
border: BorderStroke? = null,
colors: ButtonColors = ButtonDefaults.buttonColors(),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
) {
Button(
onClick,
modifier,
enabled,
interactionSource,
elevation,
shape,
border,
colors,
contentPadding,
content
)
}
@Composable
fun ManagerElevatedButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = ButtonDefaults.elevatedButtonElevation(),
shape: Shape = RoundedCornerShape(20.0.dp),
border: BorderStroke? = null,
colors: ButtonColors = ButtonDefaults.elevatedButtonColors(),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
) {
ElevatedButton(
onClick,
modifier,
enabled,
interactionSource,
elevation,
shape,
border,
colors,
contentPadding,
content
)
}
@Composable
fun ManagerFilledTonalButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = ButtonDefaults.filledTonalButtonElevation(),
shape: Shape = RoundedCornerShape(20.0.dp),
border: BorderStroke? = null,
colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
) {
FilledTonalButton(
onClick,
modifier,
enabled,
interactionSource,
elevation,
shape,
border,
colors,
contentPadding,
content
)
}
@Composable
fun ManagerOutlinedButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = null,
shape: Shape = RoundedCornerShape(20.0.dp),
border: BorderStroke? = ButtonDefaults.outlinedButtonBorder,
colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
) {
Button(
onClick,
modifier,
enabled,
interactionSource,
elevation,
shape,
border,
colors,
contentPadding,
content
)
}
@Composable
fun ManagerTextButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = null,
shape: Shape = RoundedCornerShape(20.0.dp),
border: BorderStroke? = null,
colors: ButtonColors = ButtonDefaults.textButtonColors(),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
) {
TextButton(
onClick,
modifier,
enabled,
interactionSource,
elevation,
shape,
border,
colors,
contentPadding,
content
)
}

View File

@ -0,0 +1,125 @@
package com.vanced.manager.ui.component
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import com.vanced.manager.ui.theme.MediumShape
import com.vanced.manager.ui.util.animated
@Composable
fun ManagerCard(
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null,
shape: Shape = MediumShape,
containerColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(containerColor),
elevation: CardElevation = CardDefaults.cardElevation(),
content: @Composable ColumnScope.() -> Unit,
) {
if (onClick != null) {
val interactionSource = remember { MutableInteractionSource() }
Card(
modifier = modifier
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = onClick
),
interactionSource = interactionSource,
shape = shape,
containerColor = containerColor.animated,
contentColor = contentColor.animated,
elevation = elevation,
content = content
)
} else {
Card(
modifier = modifier,
shape = shape,
containerColor = containerColor.animated,
contentColor = contentColor.animated,
elevation = elevation,
content = content
)
}
}
@Composable
fun ManagerElevatedCard(
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null,
shape: Shape = MediumShape,
containerColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(containerColor),
elevation: CardElevation = CardDefaults.elevatedCardElevation(),
content: @Composable ColumnScope.() -> Unit,
) {
if (onClick != null) {
val interactionSource = remember { MutableInteractionSource() }
ElevatedCard(
modifier = modifier
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = onClick
),
interactionSource = interactionSource,
shape = shape,
containerColor = containerColor.animated,
contentColor = contentColor.animated,
elevation = elevation,
content = content
)
} else {
ElevatedCard(
modifier = modifier,
shape = shape,
containerColor = containerColor.animated,
contentColor = contentColor.animated,
elevation = elevation,
content = content
)
}
}
@Composable
fun ManagerOutlinedCard(
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null,
shape: Shape = MediumShape,
containerColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(containerColor),
elevation: CardElevation = CardDefaults.outlinedCardElevation(),
content: @Composable ColumnScope.() -> Unit,
) {
if (onClick != null) {
val interactionSource = remember { MutableInteractionSource() }
OutlinedCard(
modifier = modifier
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = onClick
),
interactionSource = interactionSource,
shape = shape,
containerColor = containerColor.animated,
contentColor = contentColor.animated,
elevation = elevation,
content = content
)
} else {
OutlinedCard(
modifier = modifier,
shape = shape,
containerColor = containerColor.animated,
contentColor = contentColor.animated,
elevation = elevation,
content = content
)
}
}

View File

@ -1,12 +1,10 @@
package com.vanced.manager.ui.component.dialog
package com.vanced.manager.ui.component
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.AlertDialog
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.theme.LargeShape
@Composable
@ -23,7 +21,6 @@ fun ManagerDialog(
modifier = modifier,
title = {
ManagerText(
modifier = Modifier.fillMaxWidth(),
text = title,
textAlign = TextAlign.Center
)

View File

@ -1,4 +1,4 @@
package com.vanced.manager.ui.component.menu
package com.vanced.manager.ui.component
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.MutableTransitionState
@ -9,24 +9,26 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.width
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.OutlinedCard
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.*
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.window.PopupProperties
import com.vanced.manager.ui.component.card.ManagerCard
import com.vanced.manager.ui.theme.SmallShape
private const val TransitionDuration = 200
@ExperimentalAnimationApi
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun ManagerDropdownMenu(
expanded: Boolean,
@ -47,21 +49,19 @@ fun ManagerDropdownMenu(
if (expandedStates.currentState || expandedStates.targetState) {
val density = LocalDensity.current
val popupPositionProvider =
ManagerDropdownMenuPopupPositionProvider(density)
val popupPositionProvider = ManagerDropdownMenuPopupPositionProvider(density)
Popup(
popupPositionProvider = popupPositionProvider,
onDismissRequest = onDismissRequest,
properties = PopupProperties(focusable = true)
) {
ManagerCard(
OutlinedCard(
modifier = Modifier
.width(IntrinsicSize.Max)
.alpha(alphaAndScale)
.scale(alphaAndScale),
tonalElevation = 2.dp,
shadowElevation = 2.dp
elevation = CardDefaults.elevatedCardElevation()
) {
Column(content = content)
}
@ -69,8 +69,22 @@ fun ManagerDropdownMenu(
}
}
@Composable
fun ManagerDropdownMenuItem(
title: String,
onClick: () -> Unit
) {
DropdownMenuItem(
onClick = onClick,
modifier = Modifier.clip(SmallShape),
text = {
ManagerText(text = title)
}
)
}
//Kanged from Menu.kt
data class ManagerDropdownMenuPopupPositionProvider(
private data class ManagerDropdownMenuPopupPositionProvider(
val density: Density
) : PopupPositionProvider {
@ -101,8 +115,7 @@ data class ManagerDropdownMenuPopupPositionProvider(
val toCenter = anchorBounds.top - popupContentSize.height / 2
val toDisplayBottom = windowSize.height - popupContentSize.height - verticalMargin
val y = sequenceOf(toBottom, toTop, toCenter, toDisplayBottom).firstOrNull {
it >= verticalMargin &&
it + popupContentSize.height <= windowSize.height - verticalMargin
it >= verticalMargin && it + popupContentSize.height <= windowSize.height - verticalMargin
} ?: toTop
return IntOffset(x, y)

View File

@ -0,0 +1,63 @@
package com.vanced.manager.ui.component
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.util.EdgeToEdgeContentPadding
@Composable
fun ManagerLazyColumn(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(
start = EdgeToEdgeContentPadding,
end = EdgeToEdgeContentPadding,
bottom = 8.dp
),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical =
Arrangement.spacedBy(8.dp, if (!reverseLayout) Alignment.Top else Alignment.Bottom),
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
content: LazyListScope.() -> Unit
) {
LazyColumn(
modifier = modifier,
state = state,
contentPadding = contentPadding,
reverseLayout = reverseLayout,
verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment,
flingBehavior = flingBehavior,
content = content
)
}
inline fun LazyListScope.managerCategory(
noinline categoryName: @Composable () -> String,
content: LazyListScope.() -> Unit,
) {
item {
ManagerText(
modifier = Modifier
.padding(
start = 8.dp,
top = 4.dp
),
text = categoryName(),
textStyle = MaterialTheme.typography.headlineSmall,
)
}
content()
}

View File

@ -0,0 +1,42 @@
package com.vanced.manager.ui.component
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.util.EdgeToEdgeContentPadding
@Composable
fun ManagerLazyRow(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(
start = EdgeToEdgeContentPadding,
end = EdgeToEdgeContentPadding,
),
reverseLayout: Boolean = false,
horizontalArrangement: Arrangement.Horizontal =
Arrangement.spacedBy(8.dp, if (!reverseLayout) Alignment.Start else Alignment.End),
verticalAlignment: Alignment.Vertical = Alignment.Top,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
content: LazyListScope.() -> Unit
) {
LazyRow(
modifier = modifier,
state = state,
contentPadding = contentPadding,
reverseLayout = reverseLayout,
horizontalArrangement = horizontalArrangement,
verticalAlignment = verticalAlignment,
flingBehavior = flingBehavior,
content = content
)
}

View File

@ -1,13 +1,17 @@
package com.vanced.manager.ui.component.list
package com.vanced.manager.ui.component
import androidx.compose.foundation.layout.*
import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
@Composable
fun ManagerListItem(
@ -17,7 +21,10 @@ fun ManagerListItem(
icon: @Composable (() -> Unit)? = null,
trailing: @Composable (() -> Unit)? = null
) {
Row(modifier = modifier) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(DefaultContentPaddingHorizontal)
) {
if (icon != null) {
Box(
modifier = Modifier.align(Alignment.CenterVertically)
@ -29,25 +36,28 @@ fun ManagerListItem(
modifier = Modifier
.weight(1f)
.padding(
start = if (icon != null) 12.dp else 0.dp,
top = if (description != null) 6.dp else 12.dp,
bottom = if (description != null) 6.dp else 12.dp,
end = if (trailing != null) 12.dp else 0.dp,
vertical =
if (description != null) DefaultContentPaddingVertical - 4.dp else DefaultContentPaddingVertical,
)
.align(Alignment.CenterVertically)
) {
title()
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.titleSmall
) {
title()
}
if (description != null) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.medium,
LocalTextStyle provides MaterialTheme.typography.bodySmall
) {
description()
}
}
}
if (trailing != null) {
Box(
modifier = Modifier
.size(48.dp)
.align(Alignment.CenterVertically),
modifier = Modifier.align(Alignment.CenterVertically),
contentAlignment = Alignment.Center,
) {
trailing()

View File

@ -2,17 +2,54 @@ package com.vanced.manager.ui.navigation
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.with
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.compose.runtime.snapshots.SnapshotStateList
import com.vanced.manager.ui.util.Screen
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun <T> rememberManagerNavigationController(
initialScreen: T
) = remember {
ManagerNavigationControllerImpl(initialScreen)
}
interface ManagerNavigationController<T> {
val screens: SnapshotStateList<T>
fun push(item: T)
fun pop(): Boolean
}
class ManagerNavigationControllerImpl<T>(
initialScreen: T
) : ManagerNavigationController<T> {
override val screens: SnapshotStateList<T> =
mutableStateListOf(initialScreen)
override fun push(item: T) {
screens.add(item)
}
override fun pop(): Boolean {
if (screens.size > 1) {
screens.removeLast()
return true
}
return false
}
}
@Composable
fun <T : Screen> ManagerNavigator(
navigationController: NavigationController<T>,
navigationController: ManagerNavigationController<T>,
content: @Composable (targetContent: T) -> Unit
) {
val saveableStateHolder = rememberSaveableStateHolder()

View File

@ -0,0 +1,263 @@
package com.vanced.manager.ui.component
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Checkbox
import androidx.compose.material.Switch
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.vanced.manager.R
import com.vanced.manager.core.preferences.RadioButtonPreference
import com.vanced.manager.ui.resource.managerString
import com.vanced.manager.ui.theme.LargeShape
import com.vanced.manager.ui.theme.SmallShape
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
@Composable
fun ManagerPreference(
preferenceTitle: String,
preferenceDescription: String? = null,
trailing: @Composable () -> Unit = {},
onClick: () -> Unit
) {
ManagerElevatedCard(
shape = LargeShape,
onClick = onClick
) {
ManagerListItem(
modifier = Modifier
.padding(horizontal = DefaultContentPaddingHorizontal),
title = {
ManagerText(text = preferenceTitle)
},
description = if (preferenceDescription != null) {
{
ManagerText(text = preferenceDescription)
}
} else null,
trailing = trailing,
)
}
}
@Composable
fun ManagerSwitchPreference(
preferenceTitle: String,
preferenceDescription: String? = null,
isChecked: Boolean,
onCheckedChange: (isChecked: Boolean) -> Unit
) {
ManagerPreference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
onClick = {
onCheckedChange(!isChecked)
},
trailing = {
Switch(
checked = isChecked,
onCheckedChange = null
)
}
)
}
@Composable
fun ManagerDialogPreference(
preferenceTitle: String,
preferenceDescription: String? = null,
trailing: @Composable () -> Unit = {},
onPreferenceClick: () -> Unit,
isDialogVisible: Boolean,
onDismissRequest: () -> Unit,
confirmButton: @Composable () -> Unit,
dismissButton: @Composable () -> Unit = {},
content: @Composable () -> Unit
) {
ManagerPreference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing,
onClick = onPreferenceClick
)
if (isDialogVisible) {
ManagerDialog(
title = preferenceTitle,
onDismissRequest = onDismissRequest,
confirmButton = confirmButton,
dismissButton = dismissButton,
content = content
)
}
}
@Composable
fun ManagerSingleSelectDialogPreference(
preferenceTitle: String,
preferenceDescription: String,
isDialogVisible: Boolean,
currentSelectedKey: String,
buttons: List<RadioButtonPreference>,
trailing: @Composable () -> Unit = {},
onPreferenceClick: () -> Unit,
onDismissRequest: () -> Unit,
onItemClick: (itemKey: String) -> Unit,
onSave: () -> Unit,
) {
ManagerDialogPreference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing,
confirmButton = {
TextButton(onClick = onSave) {
ManagerText(managerString(R.string.dialog_button_save))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
ManagerText(managerString(R.string.dialog_button_cancel))
}
},
onDismissRequest = onDismissRequest,
isDialogVisible = isDialogVisible,
onPreferenceClick = onPreferenceClick
) {
LazyColumn(
modifier = Modifier.heightIn(max = 400.dp)
) {
items(buttons) { button ->
val (title, key) = button
ListDialogRadiobuttonItem(
modifier = Modifier.fillMaxWidth(),
text = title,
selected = currentSelectedKey == key,
onClick = {
onItemClick(key)
}
)
}
}
}
}
@Composable
fun ManagerMultiSelectDialogPreference(
preferenceTitle: String,
preferenceDescription: String,
isDialogVisible: Boolean,
currentSelectedKeys: List<String>,
buttons: List<RadioButtonPreference>,
trailing: @Composable () -> Unit = {},
onPreferenceClick: () -> Unit,
onDismissRequest: () -> Unit,
onItemCheckChange: (isChecked: Boolean, itemKey: String) -> Unit,
onSave: () -> Unit,
) {
ManagerDialogPreference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing,
confirmButton = {
androidx.compose.material.TextButton(onClick = onSave) {
ManagerText(managerString(R.string.dialog_button_save))
}
},
dismissButton = {
androidx.compose.material.TextButton(onClick = onDismissRequest) {
ManagerText(managerString(R.string.dialog_button_cancel))
}
},
onDismissRequest = onDismissRequest,
isDialogVisible = isDialogVisible,
onPreferenceClick = onPreferenceClick
) {
LazyColumn(
modifier = Modifier.heightIn(max = 400.dp)
) {
items(buttons) { button ->
val (title, key) = button
ListDialogCheckboxItem(
text = title,
checked = currentSelectedKeys.contains(key),
onCheckedChange = { isChecked ->
onItemCheckChange(isChecked, key)
}
)
}
}
}
}
@Composable
private fun ListDialogRadiobuttonItem(
text: String,
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
ListDialogItem(
modifier = modifier,
text = text,
onClick = onClick,
trailing = {
RadioButton(
selected = selected,
onClick = null
)
}
)
}
@Composable
private fun ListDialogCheckboxItem(
text: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier
) {
ListDialogItem(
modifier = modifier,
text = text,
onClick = {
onCheckedChange(!checked)
},
trailing = {
Checkbox(
checked = checked,
onCheckedChange = null
)
}
)
}
@Composable
private fun ListDialogItem(
text: String,
onClick: () -> Unit,
trailing: @Composable () -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier
.clip(SmallShape)
.clickable(onClick = onClick)
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
ManagerText(
modifier = Modifier.weight(1f),
text = text,
textStyle = MaterialTheme.typography.titleSmall
)
trailing()
}
}

View File

@ -0,0 +1,43 @@
package com.vanced.manager.ui.component
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.material.ProgressIndicatorDefaults
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.vanced.manager.ui.util.animated
@Composable
fun ManagerProgressIndicator(
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.primary,
trackColor: Color = MaterialTheme.colorScheme.surfaceVariant,
) {
LinearProgressIndicator(
modifier = modifier,
color = color.animated,
trackColor = trackColor.animated
)
}
@Composable
fun ManagerProgressIndicator(
progress: Float,
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.primary,
trackColor: Color = MaterialTheme.colorScheme.surfaceVariant,
) {
val animatedProgress by animateFloatAsState(
targetValue = progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
)
LinearProgressIndicator(
progress = animatedProgress,
modifier = modifier,
color = color.animated,
trackColor = trackColor.animated
)
}

View File

@ -1,17 +1,25 @@
package com.vanced.manager.ui.component.layout
package com.vanced.manager.ui.component
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.*
import androidx.compose.material3.FabPosition
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.vanced.manager.ui.util.animated
@ExperimentalMaterial3Api
@Composable
fun ManagerScaffold(
modifier: Modifier = Modifier,
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = contentColorFor(containerColor),
content: @Composable (PaddingValues) -> Unit
) {
// //M3 Scaffold doesn't support tonal elevation for Surface
@ -22,8 +30,12 @@ fun ManagerScaffold(
Scaffold(
modifier = modifier,
topBar = topBar,
bottomBar = bottomBar,
snackbarHost = snackbarHost,
floatingActionButton = floatingActionButton,
floatingActionButtonPosition = floatingActionButtonPosition,
containerColor = containerColor.animated,
contentColor = contentColor.animated,
content = content
)
// }

View File

@ -1,4 +1,4 @@
package com.vanced.manager.ui.component.list
package com.vanced.manager.ui.component
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row

View File

@ -1,22 +1,31 @@
package com.vanced.manager.ui.component.layout
package com.vanced.manager.ui.component
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.SwipeRefreshIndicator
import com.google.accompanist.swiperefresh.SwipeRefreshState
@Composable
fun ManagerSwipeRefresh(
refreshState: SwipeRefreshState,
swipeRefreshState: SwipeRefreshState,
onRefresh: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
swipeEnabled: Boolean = true,
refreshTriggerDistance: Dp = 80.dp,
indicatorAlignment: Alignment = Alignment.TopCenter,
indicatorPadding: PaddingValues = PaddingValues(0.dp),
clipIndicatorToPadding: Boolean = true,
content: @Composable () -> Unit,
) {
SwipeRefresh(
modifier = modifier,
state = refreshState,
state = swipeRefreshState,
onRefresh = onRefresh,
indicator = { state, trigger ->
SwipeRefreshIndicator(
@ -27,6 +36,11 @@ fun ManagerSwipeRefresh(
backgroundColor = MaterialTheme.colorScheme.surface
)
},
swipeEnabled = swipeEnabled,
refreshTriggerDistance = refreshTriggerDistance,
indicatorAlignment = indicatorAlignment,
indicatorPadding = indicatorPadding,
clipIndicatorToPadding = clipIndicatorToPadding,
content = content
)
}

View File

@ -1,4 +1,4 @@
package com.vanced.manager.ui.component.text
package com.vanced.manager.ui.component
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text

View File

@ -0,0 +1,44 @@
package com.vanced.manager.ui.component
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun ManagerSmallTopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {},
colors: TopAppBarColors = TopAppBarDefaults.smallTopAppBarColors(),
scrollBehavior: TopAppBarScrollBehavior? = null
) {
SmallTopAppBar(
modifier = modifier,
title = title,
actions = actions,
navigationIcon = navigationIcon,
colors = colors,
scrollBehavior = scrollBehavior
)
}
@Composable
fun ManagerCenterAlignedTopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {},
colors: TopAppBarColors = TopAppBarDefaults.smallTopAppBarColors(),
scrollBehavior: TopAppBarScrollBehavior? = null
) {
CenterAlignedTopAppBar(
modifier = modifier,
title = title,
actions = actions,
navigationIcon = navigationIcon,
colors = colors,
scrollBehavior = scrollBehavior
)
}

View File

@ -1,26 +0,0 @@
package com.vanced.manager.ui.component.animation
import android.annotation.SuppressLint
import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.keyframes
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@SuppressLint("UnusedTransitionTargetStateParameter")
@Composable
fun <T> Transition<T>.jumpAnimation(
initialValue: Dp,
label: String
) = animateDp(
transitionSpec = {
keyframes {
durationMillis = 250
initialValue - 8.dp at 50
initialValue + 8.dp at 150
initialValue at 250
}
},
label = label
) { initialValue }

View File

@ -1,41 +0,0 @@
package com.vanced.manager.ui.component.button
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
@Composable
fun ManagerIconButton(
icon: ImageVector,
contentDescription: String,
onClick: () -> Unit
) {
val interactionSource = remember { MutableInteractionSource() }
val buttonSize = 36.dp
Box(
modifier = Modifier
.clickable(
onClick = onClick,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple(radius = buttonSize / 2)
)
.size(buttonSize),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = icon,
contentDescription = contentDescription
)
}
}

View File

@ -1,38 +0,0 @@
package com.vanced.manager.ui.component.button
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.component.color.managerAccentColor
import com.vanced.manager.ui.theme.MediumShape
@Composable
fun ManagerThemedButton(
modifier: Modifier = Modifier,
backgroundColor: Color = managerAccentColor(),
onClick: () -> Unit,
content: @Composable RowScope.() -> Unit
) {
Button(
modifier = modifier.fillMaxWidth(),
onClick = onClick,
shape = MediumShape,
colors = ButtonDefaults.buttonColors(
containerColor = backgroundColor,
contentColor =
if (backgroundColor.luminance() > 0.7)
Color.Black
else
Color.White
),
elevation = ButtonDefaults.elevatedButtonElevation(0.dp, 0.dp, 0.dp, 0.dp)
) {
content()
}
}

View File

@ -1,23 +0,0 @@
package com.vanced.manager.ui.component.button
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.vanced.manager.ui.component.color.managerAccentColor
import com.vanced.manager.ui.component.text.ManagerText
@Composable
fun ManagerThemedTextButton(
modifier: Modifier = Modifier,
backgroundColor: Color = managerAccentColor(),
text: String,
onClick: () -> Unit
) {
ManagerThemedButton(
modifier = modifier,
backgroundColor = backgroundColor,
onClick = onClick
) {
ManagerText(text = text)
}
}

View File

@ -1,53 +0,0 @@
package com.vanced.manager.ui.component.card
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.theme.MediumShape
@Composable
fun ManagerCard(
modifier: Modifier = Modifier,
shape: Shape = MediumShape,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
tonalElevation: Dp = 0.dp,
shadowElevation: Dp = 0.dp,
content: @Composable () -> Unit,
) {
Surface(
modifier = modifier,
shape = shape,
color = backgroundColor,
shadowElevation = shadowElevation,
tonalElevation = tonalElevation,
content = content
)
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ManagerCard(
modifier: Modifier = Modifier,
shape: Shape = MediumShape,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
tonalElevation: Dp = 0.dp,
shadowElevation: Dp = 0.dp,
onClick: () -> Unit,
content: @Composable () -> Unit,
) {
Surface(
modifier = modifier,
shape = shape,
color = backgroundColor,
shadowElevation = shadowElevation,
tonalElevation = tonalElevation,
onClick = onClick,
content = content
)
}

View File

@ -1,75 +0,0 @@
package com.vanced.manager.ui.component.card
import androidx.annotation.DrawableRes
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.github.zsoltk.compose.backpress.LocalBackPressHandler
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
private val cardModifier = Modifier.sizeIn(
minHeight = 100.dp,
minWidth = 100.dp
)
@Composable
fun ManagerItemCard(
title: String,
@DrawableRes icon: Int? = null
) {
LocalBackPressHandler.current.handle()
ManagerCard(
modifier = cardModifier,
) {
ManagerItemCardContent(title, icon)
}
}
@Composable
fun ManagerItemCard(
title: String,
@DrawableRes icon: Int? = null,
onClick: () -> Unit
) {
ManagerTonalCard(
modifier = cardModifier,
onClick = onClick
) {
ManagerItemCardContent(title, icon)
}
}
@Composable
private fun ManagerItemCardContent(
title: String,
@DrawableRes icon: Int? = null,
) {
Column(
modifier = Modifier.padding(
horizontal = DefaultContentPaddingHorizontal,
vertical = DefaultContentPaddingVertical
),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.SpaceBetween
) {
if (icon != null) {
Icon(
modifier = Modifier
.size(32.dp),
painter = painterResource(id = icon),
contentDescription = title,
)
}
ManagerText(
text = title,
textStyle = MaterialTheme.typography.labelLarge
)
}
}

View File

@ -1,32 +0,0 @@
package com.vanced.manager.ui.component.card
import android.content.Intent
import android.net.Uri
import androidx.annotation.DrawableRes
import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import com.vanced.manager.core.preferences.holder.useCustomTabsPref
@Composable
fun ManagerLinkCard(
title: String,
@DrawableRes icon: Int,
link: String
) {
val context = LocalContext.current
val customTabs = remember { CustomTabsIntent.Builder().build() }
val uri = remember { Uri.parse(link) }
val intent = remember { Intent(Intent.ACTION_VIEW, uri) }
ManagerItemCard(
title = title,
icon = icon
) {
if (useCustomTabsPref) {
customTabs.launchUrl(context, uri)
} else {
context.startActivity(intent)
}
}
}

View File

@ -1,37 +0,0 @@
package com.vanced.manager.ui.component.card
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import com.vanced.manager.ui.theme.MediumShape
@Composable
fun ManagerContainerCard(
modifier: Modifier = Modifier,
shape: Shape = MediumShape,
content: @Composable () -> Unit,
) {
ManagerCard(
modifier = modifier,
shape = shape,
backgroundColor = MaterialTheme.colorScheme.primaryContainer,
content = content
)
}
@Composable
fun ManagerContainerCard(
modifier: Modifier = Modifier,
shape: Shape = MediumShape,
onClick: () -> Unit,
content: @Composable () -> Unit,
) {
ManagerCard(
modifier = modifier,
shape = shape,
backgroundColor = MaterialTheme.colorScheme.primaryContainer,
onClick = onClick,
content = content,
)
}

View File

@ -1,37 +0,0 @@
package com.vanced.manager.ui.component.card
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.theme.MediumShape
@Composable
fun ManagerTonalCard(
modifier: Modifier = Modifier,
shape: Shape = MediumShape,
content: @Composable () -> Unit,
) {
ManagerCard(
modifier = modifier,
shape = shape,
tonalElevation = 4.dp,
content = content
)
}
@Composable
fun ManagerTonalCard(
modifier: Modifier = Modifier,
shape: Shape = MediumShape,
onClick: () -> Unit,
content: @Composable () -> Unit,
) {
ManagerCard(
modifier = modifier,
shape = shape,
tonalElevation = 4.dp,
onClick = onClick,
content = content
)
}

View File

@ -1,60 +0,0 @@
package com.vanced.manager.ui.component.checkbox
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.requiredSizeIn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.Done
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import com.vanced.manager.ui.component.card.ManagerCard
import com.vanced.manager.ui.component.color.managerAnimatedColor
import com.vanced.manager.ui.theme.MediumShape
@SuppressLint("UnusedTransitionTargetStateParameter")
@Composable
fun ManagerCheckbox(
modifier: Modifier,
isChecked: Boolean,
shape: Shape = MediumShape,
onCheckedChange: (isChecked: Boolean) -> Unit
) {
val cardColor =
managerAnimatedColor(if (isChecked) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.primaryContainer)
val iconTint =
managerAnimatedColor(
if (isChecked)
contentColorFor(MaterialTheme.colorScheme.primary)
else
contentColorFor(MaterialTheme.colorScheme.primaryContainer)
)
ManagerCard(
modifier = modifier,
shape = shape,
onClick = { onCheckedChange(!isChecked) },
backgroundColor = cardColor
) {
BoxWithConstraints {
Icon(
modifier = Modifier
.requiredSizeIn(
minWidth = minWidth / 1.6f,
minHeight = minHeight / 1.6f,
maxWidth = maxWidth / 1.6f,
maxHeight = maxHeight / 1.6f,
)
.align(Alignment.Center),
imageVector = if (isChecked) Icons.Rounded.Done else Icons.Rounded.Close,
tint = iconTint,
contentDescription = null
)
}
}
}

View File

@ -1,35 +0,0 @@
package com.vanced.manager.ui.component.color
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@Composable
fun ThemedItemContentColorProvider(
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalContentColor provides contentColorForColor(managerAccentColor()),
content = content
)
}
@Composable
fun ThemedCardContentColorProvider(
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalContentColor provides contentColorForColor(managerAccentColor()),
content = content
)
}
@Composable
fun ThemedContentColorProvider(
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalContentColor provides managerAccentColor(),
content = content
)
}

View File

@ -1,27 +0,0 @@
package com.vanced.manager.ui.component.color
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.skydoves.colorpickerview.ColorEnvelope
import com.skydoves.orchestra.colorpicker.ColorPicker
@Composable
fun ManagerColorPicker(
onColorSelected: (color: Long) -> Unit
) {
val (selectedColor, setColor) = remember { mutableStateOf(ColorEnvelope(0)) }
ColorPicker(
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
onColorListener = { envelope, _ -> setColor(envelope) },
children = {},
initialColor = managerAccentColor()
)
onColorSelected(selectedColor.color.toLong())
}

View File

@ -1,38 +0,0 @@
package com.vanced.manager.ui.component.color
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import com.vanced.manager.core.preferences.holder.managerAccentColorPref
@Composable
fun contentColorForColor(color: Color) =
if (color.luminance() > 0.7)
Color.Black
else
Color.White
@Composable
fun managerAccentColor(): Color {
return Color(managerAccentColorPref)
}
@Composable
fun managerThemedCardColor() = managerAccentColor().copy(alpha = 0.27f)
@Composable
fun managerTextColor(): Color = managerAnimatedColor(color = MaterialTheme.colorScheme.onSurface)
@Composable
fun managerSurfaceColor(): Color = managerAnimatedColor(color = MaterialTheme.colorScheme.surface)
@Composable
fun managerAnimatedColor(
color: Color
): Color = animateColorAsState(
targetValue = color,
animationSpec = tween(500)
).value

View File

@ -1,20 +0,0 @@
package com.vanced.manager.ui.component.layout
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
@Composable
fun ManagerButtonColumn(
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(DefaultContentPaddingVertical),
content = content
)
}

View File

@ -1,20 +0,0 @@
package com.vanced.manager.ui.component.layout
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun ManagerLazyColumn(
modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(),
content: LazyListScope.() -> Unit
) {
LazyColumn(
modifier = modifier,
contentPadding = contentPadding,
content = content
)
}

View File

@ -1,50 +0,0 @@
package com.vanced.manager.ui.component.layout
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.vanced.manager.ui.theme.isDark
@Composable
fun ManagerScrim(
show: Boolean
) {
val systemUiController = rememberSystemUiController()
val scrimColor = Color.Black.copy(alpha = 0.5f)
val surfaceColor = MaterialTheme.colorScheme.surface
val isDark = isDark()
val scrimAlpha by animateFloatAsState(
targetValue = if (show) 1f else 0f,
animationSpec = tween()
)
SideEffect {
//TODO fix colors in navigation bars
if (show) {
systemUiController.setSystemBarsColor(scrimColor, false)
} else {
systemUiController.setSystemBarsColor(surfaceColor, !isDark)
}
}
Canvas(
Modifier
.fillMaxSize()
) {
drawRect(
color = scrimColor,
alpha = scrimAlpha
)
}
}

View File

@ -1,32 +0,0 @@
package com.vanced.manager.ui.component.layout
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
@Composable
fun ManagerScrollableColumn(
modifier: Modifier = Modifier,
contentPaddingVertical: Dp = DefaultContentPaddingVertical,
itemSpacing: Dp = 0.dp,
content: @Composable ColumnScope.() -> Unit
) {
val scrollState = rememberScrollState()
Column(
modifier = modifier
.verticalScroll(scrollState)
.padding(vertical = contentPaddingVertical),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(itemSpacing),
content = content
)
}

View File

@ -1,18 +0,0 @@
package com.vanced.manager.ui.component.layout
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.component.color.managerSurfaceColor
@Composable
fun ManagerSurface(
content: @Composable () -> Unit
) {
Surface(
modifier = Modifier.fillMaxSize(),
color = managerSurfaceColor(),
content = content
)
}

View File

@ -1,35 +0,0 @@
package com.vanced.manager.ui.component.layout
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun <T> ScrollableItemRow(
items: List<T>,
modifier: Modifier = Modifier,
content: @Composable (T) -> Unit
) {
val state = rememberLazyListState()
LazyRow(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(8.dp),
state = state
) {
item {
Spacer(Modifier.width(8.dp))
}
items(items) { item ->
content(item)
}
item {
Spacer(Modifier.width(8.dp))
}
}
}

View File

@ -1,25 +0,0 @@
package com.vanced.manager.ui.component.menu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.theme.SmallShape
@Composable
fun ManagerDropdownMenuItem(
title: String,
onClick: () -> Unit
) {
DropdownMenuItem(
onClick = onClick,
modifier = Modifier.clip(SmallShape),
) {
ManagerText(
text = title,
textStyle = MaterialTheme.typography.labelLarge
)
}
}

View File

@ -1,25 +0,0 @@
package com.vanced.manager.ui.component.modifier
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import com.vanced.manager.ui.theme.MediumShape
fun Modifier.managerClickable(
onClick: () -> Unit
) = composed {
val ripple = rememberRipple()
val interactionSource = remember { MutableInteractionSource() }
then(clip(MediumShape)).then(
clickable(
interactionSource = interactionSource,
onClick = onClick,
indication = ripple
)
)
}

View File

@ -1,18 +0,0 @@
package com.vanced.manager.ui.component.modifier
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import com.google.accompanist.placeholder.PlaceholderHighlight
import com.google.accompanist.placeholder.material.placeholder
import com.google.accompanist.placeholder.material.shimmer
fun Modifier.managerPlaceholder(
visible: Boolean
) = composed {
then(
placeholder(
visible = visible,
highlight = PlaceholderHighlight.shimmer()
)
)
}

View File

@ -1,7 +0,0 @@
package com.vanced.manager.ui.component.modifier
import androidx.compose.ui.Modifier
fun modifierBuilder(
block: Modifier.() -> Unit
) = Modifier.block()

View File

@ -1,30 +0,0 @@
package com.vanced.manager.ui.component.preference
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.widget.checkbox.ManagerAnimatedCheckbox
@Composable
fun CheckboxPreference(
preferenceTitle: String,
preferenceDescription: String? = null,
isChecked: Boolean,
onCheckedChange: (isChecked: Boolean) -> Unit = {}
) {
val onClick: () -> Unit = {
onCheckedChange(!isChecked)
}
Preference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
onClick = onClick,
trailing = {
ManagerAnimatedCheckbox(
isChecked = isChecked,
onCheckedChange = { onClick() },
size = 40.dp
)
}
)
}

View File

@ -1,33 +0,0 @@
package com.vanced.manager.ui.component.preference
import androidx.compose.runtime.Composable
import com.vanced.manager.ui.component.dialog.ManagerDialog
@Composable
fun DialogPreference(
preferenceTitle: String,
preferenceDescription: String? = null,
trailing: @Composable () -> Unit = {},
onPreferenceClick: () -> Unit,
isDialogVisible: Boolean,
onDismissRequest: () -> Unit,
confirmButton: @Composable () -> Unit,
dismissButton: @Composable () -> Unit = {},
content: @Composable () -> Unit
) {
Preference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing,
onClick = onPreferenceClick
)
if (isDialogVisible) {
ManagerDialog(
title = preferenceTitle,
onDismissRequest = onDismissRequest,
confirmButton = confirmButton,
dismissButton = dismissButton,
content = content
)
}
}

View File

@ -1,62 +0,0 @@
package com.vanced.manager.ui.component.preference
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.R
import com.vanced.manager.core.preferences.RadioButtonPreference
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.resources.managerString
import com.vanced.manager.ui.widget.list.CheckboxItem
@Composable
fun MultiSelectDialogPreference(
preferenceTitle: String,
preferenceDescription: String,
isDialogVisible: Boolean,
currentSelectedKeys: List<String>,
buttons: List<RadioButtonPreference>,
trailing: @Composable () -> Unit = {},
onPreferenceClick: () -> Unit,
onDismissRequest: () -> Unit,
onItemCheckChange: (isChecked: Boolean, itemKey: String) -> Unit,
onSave: () -> Unit,
) {
DialogPreference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing,
confirmButton = {
TextButton(onClick = onSave) {
ManagerText(managerString(R.string.dialog_button_save))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
ManagerText(managerString(R.string.dialog_button_cancel))
}
},
onDismissRequest = onDismissRequest,
isDialogVisible = isDialogVisible,
onPreferenceClick = onPreferenceClick
) {
LazyColumn(
modifier = Modifier.heightIn(max = 400.dp)
) {
items(buttons) { button ->
val (title, key) = button
CheckboxItem(
text = title,
checked = currentSelectedKeys.contains(key),
onCheckedChange = { isChecked ->
onItemCheckChange(isChecked, key)
}
)
}
}
}
}

View File

@ -1,57 +0,0 @@
package com.vanced.manager.ui.component.preference
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.component.card.ManagerTonalCard
import com.vanced.manager.ui.component.color.managerAnimatedColor
import com.vanced.manager.ui.component.list.ManagerListItem
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.theme.LargeShape
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
@Composable
fun Preference(
preferenceTitle: String,
preferenceDescription: String? = null,
trailing: @Composable () -> Unit = {},
onClick: () -> Unit
) {
val color = managerAnimatedColor(color = MaterialTheme.colorScheme.onSurface)
ManagerTonalCard(
shape = LargeShape,
onClick = onClick
) {
ManagerListItem(
modifier = Modifier
.padding(
horizontal = DefaultContentPaddingHorizontal,
vertical = 8.dp
),
title = {
CompositionLocalProvider(
LocalContentColor provides color,
LocalTextStyle provides MaterialTheme.typography.titleSmall
) {
ManagerText(text = preferenceTitle)
}
},
description = if (preferenceDescription != null) {
{
CompositionLocalProvider(
LocalContentColor provides color,
LocalTextStyle provides MaterialTheme.typography.bodySmall
) {
ManagerText(text = preferenceDescription)
}
}
} else null,
trailing = trailing,
)
}
}

View File

@ -1,62 +0,0 @@
package com.vanced.manager.ui.component.preference
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.R
import com.vanced.manager.core.preferences.RadioButtonPreference
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.resources.managerString
import com.vanced.manager.ui.widget.list.RadiobuttonItem
@Composable
fun SingleSelectDialogPreference(
preferenceTitle: String,
preferenceDescription: String,
isDialogVisible: Boolean,
currentSelectedKey: String,
buttons: List<RadioButtonPreference>,
trailing: @Composable () -> Unit = {},
onPreferenceClick: () -> Unit,
onDismissRequest: () -> Unit,
onItemClick: (itemKey: String) -> Unit,
onSave: () -> Unit,
) {
DialogPreference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing,
confirmButton = {
TextButton(onClick = onSave) {
ManagerText(managerString(R.string.dialog_button_save))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
ManagerText(managerString(R.string.dialog_button_cancel))
}
},
onDismissRequest = onDismissRequest,
isDialogVisible = isDialogVisible,
onPreferenceClick = onPreferenceClick
) {
LazyColumn(
modifier = Modifier.heightIn(max = 400.dp)
) {
items(buttons) { button ->
val (title, key) = button
RadiobuttonItem(
text = title,
selected = currentSelectedKey == key,
onClick = {
onItemClick(key)
}
)
}
}
}
}

View File

@ -1,44 +0,0 @@
package com.vanced.manager.ui.component.progressindicator
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.ProgressIndicatorDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.component.color.managerAccentColor
import com.vanced.manager.ui.theme.MediumShape
private val progressBarModifier = Modifier.composed {
then(height(5.dp))
.then(fillMaxWidth())
.then(clip(MediumShape))
}
@Composable
fun ManagerProgressIndicator() {
LinearProgressIndicator(
modifier = progressBarModifier,
color = managerAccentColor()
)
}
@Composable
fun ManagerProgressIndicator(
progress: Float
) {
val animatedProgress by animateFloatAsState(
targetValue = progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
)
LinearProgressIndicator(
modifier = progressBarModifier,
color = managerAccentColor(),
progress = animatedProgress
)
}

View File

@ -1,29 +0,0 @@
package com.vanced.manager.ui.component.radiobutton
import android.annotation.SuppressLint
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import com.vanced.manager.ui.component.card.ManagerCard
import com.vanced.manager.ui.component.color.managerAnimatedColor
import com.vanced.manager.ui.theme.MediumShape
@SuppressLint("UnusedTransitionTargetStateParameter")
@Composable
fun ManagerRadiobutton(
modifier: Modifier,
isSelected: Boolean,
shape: Shape = MediumShape,
onClick: () -> Unit
) {
val cardColor =
managerAnimatedColor(if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.primaryContainer)
ManagerCard(
modifier = modifier,
onClick = onClick,
shape = shape,
backgroundColor = cardColor,
content = {}
)
}

View File

@ -1,17 +0,0 @@
package com.vanced.manager.ui.component.text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun AppVersionText(
text: String,
modifier: Modifier = Modifier,
) {
ManagerText(
modifier = modifier,
text = text,
textStyle = MaterialTheme.typography.bodySmall,
)
}

View File

@ -1,20 +0,0 @@
package com.vanced.manager.ui.component.text
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.component.color.managerAnimatedColor
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
@Composable
fun CategoryTitleText(
text: String
) {
ManagerText(
modifier = Modifier.padding(start = DefaultContentPaddingHorizontal),
text = text,
textStyle = MaterialTheme.typography.headlineSmall,
color = managerAnimatedColor(MaterialTheme.colorScheme.onSurface)
)
}

View File

@ -1,16 +0,0 @@
package com.vanced.manager.ui.component.text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import com.vanced.manager.ui.component.color.managerAnimatedColor
@Composable
fun ToolbarTitleText(
text: String
) {
ManagerText(
text = text,
textStyle = MaterialTheme.typography.headlineMedium,
color = managerAnimatedColor(MaterialTheme.colorScheme.onSurface)
)
}

View File

@ -1,30 +0,0 @@
package com.vanced.manager.ui.component.topappbar
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SmallTopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.component.color.managerAnimatedColor
import com.vanced.manager.ui.component.text.ManagerText
@Composable
fun ManagerTopAppBar(
title: String,
modifier: Modifier = Modifier,
actions: @Composable RowScope.() -> Unit = {},
navigationIcon: @Composable () -> Unit = {},
) {
SmallTopAppBar(
modifier = modifier,
title = {
ManagerText(
text = title,
textStyle = MaterialTheme.typography.headlineMedium,
color = managerAnimatedColor(MaterialTheme.colorScheme.onSurface)
)
},
actions = actions,
navigationIcon = navigationIcon,
)
}

View File

@ -1,44 +0,0 @@
package com.vanced.manager.ui.navigation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.SnapshotStateList
@Composable
fun <T> rememberNavigationController(
initialScreen: T
) = remember {
NavigationControllerImpl(initialScreen)
}
interface NavigationController<T> {
val screens: SnapshotStateList<T>
fun push(item: T)
fun pop(): Boolean
}
class NavigationControllerImpl<T>(
initialScreen: T
) : NavigationController<T> {
override val screens: SnapshotStateList<T> =
mutableStateListOf(initialScreen)
override fun push(item: T) {
screens.add(item)
}
override fun pop(): Boolean {
if (screens.size > 1) {
screens.removeLast()
return true
}
return false
}
}

View File

@ -1,4 +1,4 @@
package com.vanced.manager.ui.resources
package com.vanced.manager.ui.resource
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable

View File

@ -1,8 +1,9 @@
package com.vanced.manager.ui.screens
package com.vanced.manager.ui.screen
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBackIosNew
import androidx.compose.material3.ExperimentalMaterial3Api
@ -13,25 +14,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import com.vanced.manager.BuildConfig
import com.vanced.manager.R
import com.vanced.manager.ui.component.card.ManagerLinkCard
import com.vanced.manager.ui.component.card.ManagerTonalCard
import com.vanced.manager.ui.component.layout.ManagerLazyColumn
import com.vanced.manager.ui.component.layout.ManagerScaffold
import com.vanced.manager.ui.component.layout.ScrollableItemRow
import com.vanced.manager.ui.component.list.ManagerListItem
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.component.topappbar.ManagerTopAppBar
import com.vanced.manager.ui.resources.managerString
import com.vanced.manager.ui.component.*
import com.vanced.manager.ui.resource.managerString
import com.vanced.manager.ui.theme.LargeShape
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
import com.vanced.manager.ui.util.Screen
import com.vanced.manager.ui.widget.layout.managerCategory
import com.vanced.manager.ui.widget.LinkCard
data class Person(
val name: String,
@ -109,17 +104,15 @@ private val sources = listOf(
@ExperimentalMaterial3Api
@Composable
fun AboutLayout(
fun AboutScreen(
onToolbarBackButtonClick: () -> Unit
) {
val aboutCategoryVancedTeam = managerString(R.string.about_category_credits_vanced_team)
val aboutCategoryOtherContributors = managerString(R.string.about_category_credits_other)
val aboutCategorySources = managerString(R.string.about_category_sources)
ManagerScaffold(
topBar = {
ManagerTopAppBar(
title = managerString(Screen.About.displayName),
ManagerSmallTopAppBar(
title = {
ManagerText(managerString(Screen.About.displayName))
},
navigationIcon = {
IconButton(onClick = onToolbarBackButtonClick) {
Icon(
@ -137,12 +130,17 @@ fun AboutLayout(
.padding(paddingValues),
) {
item {
ManagerTonalCard(
ManagerElevatedCard(
modifier = Modifier.fillMaxWidth(),
shape = LargeShape
) {
Column(
modifier = Modifier.padding(vertical = DefaultContentPaddingVertical),
modifier = Modifier
.fillMaxWidth()
.padding(
vertical = DefaultContentPaddingVertical,
horizontal = DefaultContentPaddingHorizontal
),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
@ -168,62 +166,73 @@ fun AboutLayout(
}
}
}
managerCategory(
categoryName = aboutCategoryVancedTeam,
items = vancedTeam
) { person ->
CreditPersonItem(
modifier = Modifier.fillMaxWidth(),
personName = person.name,
personContribution = person.contribution
)
}
managerCategory(
categoryName = aboutCategoryOtherContributors,
items = otherContributors
) { person ->
CreditPersonItem(
modifier = Modifier.fillMaxWidth(),
personName = person.name,
personContribution = person.contribution
)
}
managerCategory(aboutCategorySources) {
ScrollableItemRow(
modifier = Modifier
.fillMaxWidth(),
items = sources
) { source ->
ManagerLinkCard(
title = managerString(source.nameId),
icon = source.iconId,
link = source.link
managerCategory(categoryName = {
managerString(R.string.about_category_credits_vanced_team)
}) {
items(vancedTeam) { person ->
CreditCard(
modifier = Modifier.fillMaxWidth(),
personName = person.name,
personContribution = person.contribution
)
}
}
managerCategory(categoryName = {
managerString(R.string.about_category_credits_other)
}) {
items(otherContributors) { person ->
CreditCard(
modifier = Modifier.fillMaxWidth(),
personName = person.name,
personContribution = person.contribution
)
}
}
managerCategory(categoryName = {
managerString(R.string.about_category_sources)
}) {
item {
ManagerLazyRow(modifier = Modifier.fillMaxWidth()) {
items(sources) { source ->
LinkCard(
text = managerString(source.nameId),
icon = painterResource(source.iconId),
url = source.link
)
}
}
}
}
}
}
}
@Composable
private fun CreditPersonItem(
private fun CreditCard(
personName: String,
personContribution: String,
modifier: Modifier = Modifier,
) {
ManagerListItem(
modifier = modifier.padding(horizontal = DefaultContentPaddingHorizontal),
title = {
ManagerText(
text = personName,
textStyle = MaterialTheme.typography.titleSmall
)
},
description = {
ManagerText(
text = personContribution,
textStyle = MaterialTheme.typography.bodySmall
)
}
)
ManagerElevatedCard(
modifier = modifier,
shape = LargeShape
) {
ManagerListItem(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = DefaultContentPaddingHorizontal),
title = {
ManagerText(
text = personName,
textStyle = MaterialTheme.typography.titleSmall
)
},
description = {
ManagerText(
text = personContribution,
textStyle = MaterialTheme.typography.bodySmall
)
}
)
}
}

View File

@ -1,38 +1,28 @@
package com.vanced.manager.ui.screens
package com.vanced.manager.ui.screen
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ListItem
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBackIosNew
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.R
import com.vanced.manager.domain.model.InstallationOption
import com.vanced.manager.ui.component.card.ManagerTonalCard
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.component.topappbar.ManagerTopAppBar
import com.vanced.manager.ui.resources.managerString
import com.vanced.manager.ui.component.*
import com.vanced.manager.ui.resource.managerString
import com.vanced.manager.ui.theme.LargeShape
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
import com.vanced.manager.ui.viewmodel.ConfigurationViewModel
import com.vanced.manager.ui.widget.layout.managerCategory
import org.koin.androidx.compose.getViewModel
private const val enterDuration = 300
private const val exitDuration = 250
@ExperimentalFoundationApi
@ExperimentalAnimationApi
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ConfigurationScreen(
installationOptions: List<InstallationOption>,
@ -42,8 +32,10 @@ fun ConfigurationScreen(
val viewModel: ConfigurationViewModel = getViewModel()
Scaffold(
topBar = {
ManagerTopAppBar(
title = managerString(R.string.toolbar_installation_preferences),
ManagerSmallTopAppBar(
title = {
ManagerText(managerString(R.string.toolbar_installation_preferences))
},
navigationIcon = {
IconButton(
onClick = {
@ -60,7 +52,7 @@ fun ConfigurationScreen(
)
},
bottomBar = {
ConfigurationButtons(
ConfigurationBottomBar(
modifier = Modifier
.padding(
horizontal = DefaultContentPaddingHorizontal,
@ -91,7 +83,6 @@ fun ConfigurationScreen(
}
}
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterial3Api::class)
@Composable
private fun ConfigurationBody(
currentIndex: Int,
@ -112,55 +103,52 @@ private fun ConfigurationBody(
}
) { optionIndex ->
val installationOption = installationOptions[optionIndex]
val categoryName = managerString(installationOption.titleId)
LazyColumn {
when (installationOption) {
is InstallationOption.SingleSelect -> {
managerCategory(
categoryName = categoryName,
items = installationOption.items
) { item ->
val preference = installationOption.getOption()
ConfigurationItem(
modifier = Modifier
.fillMaxWidth(),
text = item.displayText(item.key),
onClick = {
installationOption.setOption(item.key)
},
trailing = {
RadioButton(
selected = preference == item.key,
onClick = null
)
}
)
}
}
is InstallationOption.MultiSelect -> {
managerCategory(
categoryName = categoryName,
items = installationOption.items
) { item ->
val preference = installationOption.getOption()
ConfigurationItem(
modifier = Modifier
.fillMaxWidth(),
text = item.displayText(item.key),
onClick = {
if (preference.contains(item.key)) {
installationOption.removeOption(item.key)
} else {
installationOption.addOption(item.key)
ManagerLazyColumn {
managerCategory(categoryName = {
managerString(installationOption.titleId)
}) {
when (installationOption) {
is InstallationOption.SingleSelect -> {
items(installationOption.items) { item ->
val preference = installationOption.getOption()
ConfigurationItem(
modifier = Modifier
.fillMaxWidth(),
text = item.displayText(item.key),
onClick = {
installationOption.setOption(item.key)
},
trailing = {
RadioButton(
selected = preference == item.key,
onClick = null
)
}
},
trailing = {
Checkbox(
checked = preference.contains(item.key),
onCheckedChange = null
)
}
)
)
}
}
is InstallationOption.MultiSelect -> {
items(installationOption.items) { item ->
val preference = installationOption.getOption()
ConfigurationItem(
modifier = Modifier
.fillMaxWidth(),
text = item.displayText(item.key),
onClick = {
if (preference.contains(item.key)) {
installationOption.removeOption(item.key)
} else {
installationOption.addOption(item.key)
}
},
trailing = {
Checkbox(
checked = preference.contains(item.key),
onCheckedChange = null
)
}
)
}
}
}
}
@ -168,9 +156,8 @@ private fun ConfigurationBody(
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun ConfigurationButtons(
private fun ConfigurationBottomBar(
currentIndex: Int,
lastIndex: Int,
onBackClick: () -> Unit,
@ -224,7 +211,6 @@ private fun ConfigurationButtons(
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun ConfigurationItem(
text: String,
@ -232,18 +218,16 @@ private fun ConfigurationItem(
trailing: @Composable () -> Unit,
modifier: Modifier = Modifier,
) {
ManagerTonalCard(
modifier = modifier
.padding(
start = 6.dp,
end = 6.dp,
bottom = 8.dp
),
ManagerElevatedCard(
modifier = modifier,
shape = LargeShape,
onClick = onClick
) {
ListItem(
text = {
ManagerListItem(
modifier = Modifier.padding(
horizontal = DefaultContentPaddingHorizontal
),
title = {
ManagerText(
text = text,
textStyle = MaterialTheme.typography.titleSmall

View File

@ -0,0 +1,284 @@
package com.vanced.manager.ui.screen
import androidx.compose.animation.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.TextButton
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import coil.compose.rememberImagePainter
import coil.request.CachePolicy
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.vanced.manager.R
import com.vanced.manager.core.util.socialMedia
import com.vanced.manager.core.util.sponsors
import com.vanced.manager.domain.model.App
import com.vanced.manager.domain.model.InstallationOption
import com.vanced.manager.ui.component.*
import com.vanced.manager.ui.resource.managerString
import com.vanced.manager.ui.util.Screen
import com.vanced.manager.ui.viewmodel.MainViewModel
import com.vanced.manager.ui.widget.AppCard
import com.vanced.manager.ui.widget.AppCardPlaceholder
import com.vanced.manager.ui.widget.LinkCard
@Composable
fun HomeScreen(
viewModel: MainViewModel,
onToolbarScreenSelected: (Screen) -> Unit,
onAppDownloadClick: (
appName: String,
appVersions: List<String>?,
installationOptions: List<InstallationOption>?
) -> Unit,
onAppUninstallClick: (appPackage: String) -> Unit,
onAppLaunchClick: (appName: String, appPackage: String) -> Unit,
) {
val refreshState =
rememberSwipeRefreshState(isRefreshing = viewModel.appState.isFetching)
var menuExpanded by remember { mutableStateOf(false) }
val dropdownScreens = remember { listOf(Screen.Settings, Screen.About) }
ManagerScaffold(
topBar = {
HomeScreenTopBar(
modifier = Modifier,
menuExpanded = menuExpanded,
dropdownScreens = dropdownScreens,
onActionClick = {
menuExpanded = true
},
onDropdownItemClick = onToolbarScreenSelected,
onDropdownDismissRequest = {
menuExpanded = false
})
}
) { paddingValues ->
ManagerSwipeRefresh(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
swipeRefreshState = refreshState,
onRefresh = { viewModel.fetch() }
) {
AnimatedContent(
modifier = Modifier.fillMaxSize(),
targetState = viewModel.appState,
transitionSpec = {
scaleIn(initialScale = 0.9f) + fadeIn() with
scaleOut(targetScale = 0.9f) + fadeOut()
}
) { animatedAppState ->
when (animatedAppState) {
is StateFetching -> {
HomeScreenLoading(
modifier = Modifier.fillMaxSize(),
appsCount = animatedAppState.placeholderAppsCount
)
}
is StateSuccess -> {
HomeScreenLoaded(
modifier = Modifier.fillMaxSize(),
apps = animatedAppState.apps,
onAppDownloadClick = onAppDownloadClick,
onAppUninstallClick = onAppUninstallClick,
onAppLaunchClick = onAppLaunchClick
)
}
is StateError -> {
//TODO
}
}
}
}
}
}
typealias StateFetching = MainViewModel.AppState.Fetching
typealias StateSuccess = MainViewModel.AppState.Success
typealias StateError = MainViewModel.AppState.Error
@Composable
private fun HomeScreenTopBar(
menuExpanded: Boolean,
dropdownScreens: List<Screen>,
onActionClick: () -> Unit,
onDropdownItemClick: (Screen) -> Unit,
onDropdownDismissRequest: () -> Unit,
modifier: Modifier = Modifier
) {
ManagerCenterAlignedTopAppBar(
modifier = modifier,
title = {
ManagerText(managerString(Screen.Home.displayName))
},
actions = {
IconButton(onClick = onActionClick) {
Icon(
Icons.Rounded.MoreVert,
contentDescription = "Navigation"
)
}
ManagerDropdownMenu(
expanded = menuExpanded,
onDismissRequest = onDropdownDismissRequest
) {
for (dropdownScreen in dropdownScreens) {
ManagerDropdownMenuItem(
title = managerString(dropdownScreen.displayName),
onClick = {
onDropdownItemClick(dropdownScreen)
}
)
}
}
}
)
}
@Composable
private fun HomeScreenLoaded(
modifier: Modifier = Modifier,
apps: List<App>,
onAppDownloadClick: (
appName: String,
appVersions: List<String>?,
installationOptions: List<InstallationOption>?
) -> Unit,
onAppUninstallClick: (appPackage: String) -> Unit,
onAppLaunchClick: (appName: String, appPackage: String) -> Unit,
) {
HomeScreenBody(modifier = modifier) {
managerCategory(categoryName = {
managerString(R.string.home_category_apps)
}) {
items(apps) { app ->
val appIcon = rememberImagePainter(app.iconUrl) {
diskCachePolicy(CachePolicy.ENABLED)
}
var showAppInfoDialog by remember { mutableStateOf(false) }
AppCard(
modifier = Modifier
.fillMaxWidth(),
appName = app.name,
appIcon = appIcon,
appInstalledVersion = app.installedVersion,
appRemoteVersion = app.remoteVersion,
onAppDownloadClick = {
onAppDownloadClick(
app.name,
app.versions,
app.installationOptions
)
},
onAppUninstallClick = {
onAppUninstallClick(
app.packageName
)
},
onAppLaunchClick = {
onAppLaunchClick(
app.name,
app.packageName,
)
},
onAppInfoClick = {
showAppInfoDialog = true
}
)
if (showAppInfoDialog) {
ManagerDialog(
title = managerString(
R.string.app_info_title,
app.name
),
onDismissRequest = { showAppInfoDialog = false },
confirmButton = {
TextButton(onClick = {
showAppInfoDialog = false
}) {
ManagerText(text = managerString(R.string.dialog_button_close))
}
},
) {
ManagerText(
modifier = Modifier.padding(top = 4.dp),
text = app.changelog,
)
}
}
}
}
}
}
@Composable
private fun HomeScreenLoading(
modifier: Modifier = Modifier,
appsCount: Int,
) {
HomeScreenBody(modifier = modifier) {
managerCategory(categoryName = {
managerString(R.string.home_category_apps)
}) {
items(appsCount) {
AppCardPlaceholder(
modifier = Modifier.fillMaxWidth()
)
}
}
}
}
@Composable
private inline fun HomeScreenBody(
modifier: Modifier = Modifier,
crossinline appsCategory: LazyListScope.() -> Unit,
) {
ManagerLazyColumn(modifier = modifier) {
appsCategory()
managerCategory(categoryName = {
managerString(R.string.home_category_support_us)
}) {
item {
ManagerLazyRow(modifier = Modifier.fillMaxWidth()) {
items(sponsors) { sponsor ->
LinkCard(
text = sponsor.title,
icon = painterResource(sponsor.icon),
url = sponsor.link
)
}
}
}
}
managerCategory(categoryName = {
managerString(R.string.home_category_social_media)
}) {
item {
ManagerLazyRow(modifier = Modifier.fillMaxWidth()) {
items(socialMedia) { socialMedia ->
LinkCard(
text = socialMedia.title,
icon = painterResource(socialMedia.icon),
url = socialMedia.link
)
}
}
}
}
}
}

View File

@ -1,14 +1,13 @@
package com.vanced.manager.ui.screens
package com.vanced.manager.ui.screen
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowDropDown
import androidx.compose.material.icons.rounded.Done
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@ -24,20 +23,11 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.sp
import com.vanced.manager.R
import com.vanced.manager.ui.component.layout.ManagerLazyColumn
import com.vanced.manager.ui.component.layout.ManagerScaffold
import com.vanced.manager.ui.component.modifier.managerClickable
import com.vanced.manager.ui.component.progressindicator.ManagerProgressIndicator
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.component.topappbar.ManagerTopAppBar
import com.vanced.manager.ui.resources.managerString
import com.vanced.manager.ui.component.*
import com.vanced.manager.ui.resource.managerString
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
import com.vanced.manager.ui.viewmodel.InstallViewModel
@OptIn(
ExperimentalFoundationApi::class,
ExperimentalMaterial3Api::class
)
@Composable
fun InstallScreen(
appName: String,
@ -62,8 +52,10 @@ fun InstallScreen(
ManagerScaffold(
topBar = {
Column {
ManagerTopAppBar(
title = managerString(R.string.toolbar_install),
ManagerSmallTopAppBar(
title = {
ManagerText(managerString(R.string.toolbar_install))
},
)
when (status) {
is InstallViewModel.Status.Progress -> {
@ -127,7 +119,7 @@ fun InstallScreen(
Column(
modifier = Modifier
.fillMaxWidth()
.managerClickable {
.clickable {
visible = !visible
}
.padding(horizontal = DefaultContentPaddingHorizontal),

View File

@ -0,0 +1,73 @@
package com.vanced.manager.ui.screen
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBackIosNew
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.R
import com.vanced.manager.core.util.notificationApps
import com.vanced.manager.ui.component.*
import com.vanced.manager.ui.resource.managerString
import com.vanced.manager.ui.util.Screen
import com.vanced.manager.ui.widget.SettingsCustomTabsItem
import com.vanced.manager.ui.widget.SettingsManagerVariantItem
import com.vanced.manager.ui.widget.SettingsNotificationsItem
import com.vanced.manager.ui.widget.ThemeSettingsItem
@ExperimentalMaterial3Api
@Composable
fun SettingsScreen(
onToolbarBackButtonClick: () -> Unit,
) {
ManagerScaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
ManagerSmallTopAppBar(
title = {
ManagerText(managerString(Screen.Settings.displayName))
},
navigationIcon = {
IconButton(onClick = onToolbarBackButtonClick) {
Icon(
imageVector = Icons.Rounded.ArrowBackIosNew,
contentDescription = "Back"
)
}
}
)
}
) { paddingValues ->
ManagerLazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
) {
managerCategory(categoryName = {
managerString(R.string.settings_category_behaviour)
}) {
item {
SettingsCustomTabsItem()
}
items(notificationApps) { notificationApp ->
SettingsNotificationsItem(notificationApp)
}
item {
SettingsManagerVariantItem()
}
}
managerCategory(categoryName = {
managerString(R.string.settings_category_appearance)
}) {
item {
ThemeSettingsItem()
}
}
}
}
}

View File

@ -1,217 +0,0 @@
package com.vanced.manager.ui.screens
import androidx.compose.animation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.TextButton
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import coil.compose.rememberImagePainter
import coil.request.CachePolicy
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.vanced.manager.R
import com.vanced.manager.core.util.socialMedia
import com.vanced.manager.core.util.sponsors
import com.vanced.manager.domain.model.InstallationOption
import com.vanced.manager.ui.component.card.ManagerLinkCard
import com.vanced.manager.ui.component.dialog.ManagerDialog
import com.vanced.manager.ui.component.layout.ManagerLazyColumn
import com.vanced.manager.ui.component.layout.ManagerScaffold
import com.vanced.manager.ui.component.layout.ManagerSwipeRefresh
import com.vanced.manager.ui.component.layout.ScrollableItemRow
import com.vanced.manager.ui.component.menu.ManagerDropdownMenu
import com.vanced.manager.ui.component.menu.ManagerDropdownMenuItem
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.component.topappbar.ManagerTopAppBar
import com.vanced.manager.ui.resources.managerString
import com.vanced.manager.ui.util.Screen
import com.vanced.manager.ui.viewmodel.MainViewModel
import com.vanced.manager.ui.widget.app.AppCard
import com.vanced.manager.ui.widget.app.AppCardPlaceholder
import com.vanced.manager.ui.widget.layout.managerCategory
@ExperimentalMaterial3Api
@ExperimentalAnimationApi
@Composable
fun HomeLayout(
viewModel: MainViewModel,
onToolbarScreenSelected: (Screen) -> Unit,
onAppInstallPress: (
appName: String,
appVersions: List<String>?,
installationOptions: List<InstallationOption>?
) -> Unit
) {
val appState = viewModel.appState
val refreshState =
rememberSwipeRefreshState(isRefreshing = appState is MainViewModel.AppState.Fetching)
var isMenuExpanded by remember { mutableStateOf(false) }
val dropdownScreens = remember { listOf(Screen.Settings, Screen.About) }
val homeCategoryApps = managerString(R.string.home_category_apps)
val homeCategorySupportUs = managerString(R.string.home_category_support_us)
val homeCategorySocialMedia = managerString(R.string.home_category_social_media)
ManagerScaffold(
topBar = {
ManagerTopAppBar(
title = managerString(Screen.Home.displayName),
actions = {
IconButton(onClick = { isMenuExpanded = true }) {
Icon(
Icons.Rounded.MoreVert,
contentDescription = "Navigation"
)
}
ManagerDropdownMenu(
expanded = isMenuExpanded,
onDismissRequest = {
isMenuExpanded = false
}
) {
for (dropdownScreen in dropdownScreens) {
ManagerDropdownMenuItem(
title = managerString(dropdownScreen.displayName),
onClick = {
onToolbarScreenSelected(dropdownScreen)
isMenuExpanded = false
}
)
}
}
}
)
}
) { paddingValues ->
ManagerSwipeRefresh(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
refreshState = refreshState,
onRefresh = { viewModel.fetch() }
) {
ManagerLazyColumn {
managerCategory(homeCategoryApps) {
AnimatedContent(
targetState = appState,
transitionSpec = {
scaleIn(initialScale = 0.9f) + fadeIn() with
scaleOut(targetScale = 0.9f) + fadeOut()
}
) { animatedAppState ->
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
when (animatedAppState) {
is MainViewModel.AppState.Success -> {
for (app in animatedAppState.apps) {
val appIcon = rememberImagePainter(app.iconUrl) {
diskCachePolicy(CachePolicy.ENABLED)
}
var showAppInfoDialog by rememberSaveable {
mutableStateOf(
false
)
}
AppCard(
appName = app.name,
appIcon = appIcon,
appInstalledVersion = app.installedVersion,
appRemoteVersion = app.remoteVersion,
onAppDownloadClick = {
onAppInstallPress(
app.name,
app.versions,
app.installationOptions
)
},
onAppUninstallClick = {
viewModel.uninstallApp(
appPackage = app.packageName
)
},
onAppLaunchClick = {
viewModel.launchApp(
appName = app.name,
appPackage = app.packageName,
)
},
onAppInfoClick = {
showAppInfoDialog = true
}
)
if (showAppInfoDialog) {
ManagerDialog(
title = managerString(
R.string.app_info_title,
app.name
),
onDismissRequest = { showAppInfoDialog = false },
confirmButton = {
TextButton(onClick = {
showAppInfoDialog = false
}) {
ManagerText(text = managerString(R.string.dialog_button_close))
}
},
) {
ManagerText(
modifier = Modifier.padding(top = 4.dp),
text = app.changelog,
//textStyle = MaterialTheme.typography.bodyMedium
)
}
}
}
}
is MainViewModel.AppState.Fetching -> {
for (i in 0 until animatedAppState.placeholderAppsCount) {
AppCardPlaceholder()
}
}
is MainViewModel.AppState.Error -> {
}
}
}
}
}
managerCategory(homeCategorySupportUs) {
ScrollableItemRow(
modifier = Modifier.fillMaxWidth(),
items = sponsors
) { sponsor ->
ManagerLinkCard(
icon = sponsor.icon,
title = sponsor.title,
link = sponsor.link
)
}
}
managerCategory(homeCategorySocialMedia) {
ScrollableItemRow(
modifier = Modifier.fillMaxWidth(),
items = socialMedia
) { socialMedia ->
ManagerLinkCard(
icon = socialMedia.icon,
title = socialMedia.title,
link = socialMedia.link
)
}
}
}
}
}
}

View File

@ -1,72 +0,0 @@
package com.vanced.manager.ui.screens
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBackIosNew
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.R
import com.vanced.manager.ui.component.layout.ManagerLazyColumn
import com.vanced.manager.ui.component.topappbar.ManagerTopAppBar
import com.vanced.manager.ui.resources.managerString
import com.vanced.manager.ui.util.Screen
import com.vanced.manager.ui.widget.layout.managerCategory
import com.vanced.manager.ui.widget.settings.*
@ExperimentalMaterial3Api
@Composable
fun SettingsLayout(
onToolbarBackButtonClick: () -> Unit,
) {
val settingsCategoryBehaviour = managerString(R.string.settings_category_behaviour)
val settingsCategoryApperance = managerString(R.string.settings_category_appearance)
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
ManagerTopAppBar(
title = managerString(Screen.Settings.displayName),
navigationIcon = {
IconButton(onClick = onToolbarBackButtonClick) {
Icon(
imageVector = Icons.Rounded.ArrowBackIosNew,
contentDescription = "Back"
)
}
}
)
}
) { paddingValues ->
ManagerLazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
) {
managerCategory(settingsCategoryBehaviour) {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
SettingsCustomTabsItem()
SettingsNotificationsItem()
SettingsManagerVariantItem()
}
}
managerCategory(settingsCategoryApperance) {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
SettingsAccentColorItem()
ThemeSettingsItem()
}
}
}
}
}

View File

@ -1,13 +1,9 @@
package com.vanced.manager.ui.theme
import android.annotation.SuppressLint
import android.os.Build
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.gestures.LocalOverScrollConfiguration
import androidx.compose.foundation.gestures.OverScrollConfiguration
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@ -79,38 +75,42 @@ fun isDark(): Boolean = when (managerThemePref) {
else -> throw IllegalArgumentException("Unknown theme")
}
@SuppressLint("NewApi")
@ExperimentalFoundationApi
@Composable
inline fun apiDependantColorScheme(
dynamic: () -> ColorScheme,
static: () -> ColorScheme
): ColorScheme {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
dynamic()
} else {
static()
}
}
@Composable
fun ManagerTheme(
content: @Composable () -> Unit
) {
val isAndroid12OrHigher = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val context = LocalContext.current
val colorScheme =
if (isAndroid12OrHigher) {
val context = LocalContext.current
if (isDark()) {
dynamicDarkColorScheme(context)
} else {
dynamicLightColorScheme(context)
}
if (isDark()) {
apiDependantColorScheme(
dynamic = { dynamicDarkColorScheme(context) },
static = { DarkThemeColors }
)
} else {
if (isDark()) {
DarkThemeColors
} else {
LightThemeColors
}
apiDependantColorScheme(
dynamic = { dynamicLightColorScheme(context)},
static = { LightThemeColors }
)
}
MaterialTheme(
colorScheme = colorScheme,
typography = ManagerTypography,
) {
val rippleIndication = rememberRipple()
CompositionLocalProvider(
LocalIndication provides rippleIndication,
LocalOverScrollConfiguration provides OverScrollConfiguration(
forceShowAlways = isAndroid12OrHigher
forceShowAlways = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
)
) {
content()

View File

@ -0,0 +1,10 @@
package com.vanced.manager.ui.util
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
val Color.animated
@Composable
get() = animateColorAsState(this, animationSpec = tween(400)).value

View File

@ -3,4 +3,6 @@ package com.vanced.manager.ui.util
import androidx.compose.ui.unit.dp
val DefaultContentPaddingHorizontal = 16.dp
val DefaultContentPaddingVertical = 12.dp
val DefaultContentPaddingVertical = 12.dp
val EdgeToEdgeContentPadding = 8.dp

View File

@ -36,6 +36,10 @@ class MainViewModel(
data class Fetching(val placeholderAppsCount: Int) : AppState()
data class Success(val apps: List<App>) : AppState()
data class Error(val error: String) : AppState()
val isFetching get() = this is Fetching
val isSuccess get() = this is Success
val isError get() = this is Error
}
var appState by mutableStateOf<AppState>(AppState.Fetching(appCount))

View File

@ -0,0 +1,257 @@
package com.vanced.manager.ui.widget
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.rounded.DeleteForever
import androidx.compose.material.icons.rounded.Download
import androidx.compose.material.icons.rounded.Launch
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import coil.compose.ImagePainter
import com.google.accompanist.placeholder.PlaceholderHighlight
import com.google.accompanist.placeholder.placeholder
import com.google.accompanist.placeholder.shimmer
import com.vanced.manager.R
import com.vanced.manager.ui.component.ManagerElevatedCard
import com.vanced.manager.ui.component.ManagerListItem
import com.vanced.manager.ui.component.ManagerText
import com.vanced.manager.ui.theme.LargeShape
import com.vanced.manager.ui.theme.MediumShape
import com.vanced.manager.ui.theme.SmallShape
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
@Composable
fun AppCard(
appName: String,
appIcon: ImagePainter,
appInstalledVersion: String?,
appRemoteVersion: String?,
onAppDownloadClick: () -> Unit,
onAppUninstallClick: () -> Unit,
onAppLaunchClick: () -> Unit,
onAppInfoClick: () -> Unit,
modifier: Modifier = Modifier,
) {
BaseAppCard(
modifier = modifier,
appTitle = {
ManagerText(
modifier = Modifier.fillMaxSize(),
text = appName,
textStyle = MaterialTheme.typography.titleMedium
)
},
appIcon = {
Image(
modifier = Modifier.size(48.dp),
painter = appIcon,
contentDescription = "App Icon",
)
},
appTrailing = {
IconButton(onClick = onAppInfoClick) {
Icon(
imageVector = Icons.Outlined.Info,
contentDescription = "App Info"
)
}
},
appVersionsColumn = {
ManagerText(
text = stringResource(
id = R.string.app_version_latest,
appRemoteVersion ?: stringResource(
id = R.string.app_content_unavailable
)
)
)
ManagerText(
text = stringResource(
id = R.string.app_version_installed,
appInstalledVersion ?: stringResource(
id = R.string.app_content_unavailable
)
)
)
},
appActionsRow = {
if (appInstalledVersion != null) {
IconButton(onClick = onAppUninstallClick) {
Icon(
imageVector = Icons.Rounded.DeleteForever,
contentDescription = "Uninstall"
)
}
IconButton(onClick = onAppLaunchClick) {
Icon(
imageVector = Icons.Rounded.Launch,
contentDescription = "Launch",
)
}
}
IconButton(onClick = onAppDownloadClick) {
Icon(
imageVector = Icons.Rounded.Download,
contentDescription = "Install",
)
}
}
)
}
@Composable
fun AppCardPlaceholder(
modifier: Modifier = Modifier
) {
BaseAppCard(
modifier = modifier,
appTitle = {
ManagerText(
modifier = Modifier
.managerPlaceholder(
visible = true,
shape = MediumShape
),
text = " ".repeat(40),
textStyle = MaterialTheme.typography.titleMedium
)
},
appIcon = {
Box(
modifier = Modifier
.managerPlaceholder(
visible = true,
shape = RoundedCornerShape(24.dp)
)
.size(48.dp)
)
},
appTrailing = {
Box(
modifier = Modifier
.managerPlaceholder(
visible = true,
shape = MediumShape
)
.size(24.dp)
)
},
appVersionsColumn = {
ManagerText(
modifier = Modifier
.managerPlaceholder(
visible = true,
shape = SmallShape
),
text = " ".repeat(30)
)
ManagerText(
modifier = Modifier
.managerPlaceholder(
visible = true,
shape = SmallShape
),
text = " ".repeat(30)
)
},
appActionsRow = {
Box(
modifier = Modifier
.fillMaxWidth(0.8f)
.height(36.dp)
.managerPlaceholder(
visible = true,
shape = MediumShape
)
)
}
)
}
@Composable
private fun BaseAppCard(
appTitle: @Composable () -> Unit,
appIcon: @Composable () -> Unit,
appTrailing: @Composable () -> Unit,
appVersionsColumn: @Composable ColumnScope.() -> Unit,
appActionsRow: @Composable RowScope.() -> Unit,
modifier: Modifier = Modifier
) {
ManagerElevatedCard(
modifier = modifier,
shape = LargeShape,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = DefaultContentPaddingHorizontal,
vertical = DefaultContentPaddingVertical
),
verticalArrangement = Arrangement
.spacedBy(DefaultContentPaddingVertical)
) {
ManagerListItem(
modifier = Modifier.fillMaxWidth(),
title = appTitle,
icon = appIcon,
trailing = appTrailing
)
Divider(
modifier = Modifier
.fillMaxWidth()
.clip(LargeShape),
thickness = 2.dp,
)
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(
verticalArrangement = Arrangement.spacedBy(4.dp),
horizontalAlignment = Alignment.Start
) {
ManagerText(
text = stringResource(id = R.string.app_versions),
textStyle = MaterialTheme.typography.bodyMedium
)
ProvideTextStyle(value = MaterialTheme.typography.bodySmall) {
appVersionsColumn()
}
}
Row(
modifier = Modifier.wrapContentWidth(),
content = appActionsRow,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
)
}
}
}
}
private fun Modifier.managerPlaceholder(
visible: Boolean,
shape: Shape
) = composed {
placeholder(
visible = visible,
shape = shape,
color = MaterialTheme.colorScheme.surfaceVariant,
highlight = PlaceholderHighlight.shimmer(
highlightColor = MaterialTheme.colorScheme.onSurfaceVariant
)
)
}

View File

@ -0,0 +1,68 @@
package com.vanced.manager.ui.widget
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.vanced.manager.core.preferences.holder.useCustomTabsPref
import com.vanced.manager.ui.component.ManagerElevatedCard
import com.vanced.manager.ui.component.ManagerText
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
//TODO this composable should not handle opening links
@Composable
fun LinkCard(
text: String,
icon: Painter,
url: String,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
val customTabs = remember { CustomTabsIntent.Builder().build() }
val uri = remember { Uri.parse(url) }
val intent = remember { Intent(Intent.ACTION_VIEW, uri) }
ManagerElevatedCard(
modifier = modifier
.height(100.dp)
.widthIn(min = 100.dp),
onClick = {
if (useCustomTabsPref) {
customTabs.launchUrl(context, uri)
} else {
context.startActivity(intent)
}
}
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(
horizontal = DefaultContentPaddingHorizontal,
vertical = DefaultContentPaddingVertical
),
) {
Icon(
modifier = Modifier
.size(32.dp)
.align(Alignment.TopStart),
painter = icon,
contentDescription = null,
)
ManagerText(
modifier = Modifier.align(Alignment.BottomStart),
text = text,
textStyle = MaterialTheme.typography.labelLarge
)
}
}
}

View File

@ -0,0 +1,138 @@
package com.vanced.manager.ui.widget
import androidx.compose.runtime.*
import androidx.compose.ui.res.stringResource
import com.vanced.manager.R
import com.vanced.manager.core.preferences.RadioButtonPreference
import com.vanced.manager.core.preferences.holder.managerThemePref
import com.vanced.manager.core.preferences.holder.managerVariantPref
import com.vanced.manager.core.preferences.holder.useCustomTabsPref
import com.vanced.manager.core.preferences.managerBooleanPreference
import com.vanced.manager.core.util.isMagiskInstalled
import com.vanced.manager.domain.model.NotificationPrefModel
import com.vanced.manager.ui.component.ManagerPreference
import com.vanced.manager.ui.component.ManagerSingleSelectDialogPreference
import com.vanced.manager.ui.component.ManagerSwitchPreference
import com.vanced.manager.ui.resource.managerString
@Composable
fun SettingsClearFilesItem() {
ManagerPreference(
preferenceTitle = managerString(
stringId = R.string.settings_preference_clear_files_title
),
preferenceDescription = null,
onClick = {}
)
}
@Composable
fun SettingsCustomTabsItem() {
ManagerSwitchPreference(
preferenceTitle = stringResource(id = R.string.settings_preference_use_custom_tabs_title),
preferenceDescription = stringResource(id = R.string.settings_preference_use_custom_tabs_summary),
isChecked = useCustomTabsPref,
onCheckedChange = {
useCustomTabsPref = it
}
)
}
@Composable
fun SettingsManagerVariantItem() {
var showDialog by remember { mutableStateOf(false) }
var selectedKey by remember { mutableStateOf(managerVariantPref) }
ManagerSingleSelectDialogPreference(
preferenceTitle = managerString(
stringId = R.string.settings_preference_variant_title
),
preferenceDescription = managerVariantPref,
isDialogVisible = showDialog,
currentSelectedKey = selectedKey,
buttons = listOf(
RadioButtonPreference(
title = "nonroot",
key = "nonroot"
),
RadioButtonPreference(
title = "root",
key = "root"
),
),
onPreferenceClick = {
showDialog = true
},
onDismissRequest = {
showDialog = false
selectedKey = managerVariantPref
},
onItemClick = {
if (it == "root" && !isMagiskInstalled)
return@ManagerSingleSelectDialogPreference
selectedKey = it
},
onSave = {
managerVariantPref = selectedKey
showDialog = false
}
)
}
@Composable
fun SettingsNotificationsItem(notificationApp: NotificationPrefModel) {
with(notificationApp) {
var appNotificationsPref by managerBooleanPreference(
key = "${prefKey}_notifications",
defaultValue = true
)
ManagerSwitchPreference(
preferenceTitle = "$app Push Notifications",
preferenceDescription = "Receive push notifications when an update for $app is released",
isChecked = appNotificationsPref,
onCheckedChange = {
appNotificationsPref = it
}
)
}
}
@Composable
fun ThemeSettingsItem() {
var showDialog by remember { mutableStateOf(false) }
var selectedKey by remember { mutableStateOf(managerThemePref) }
ManagerSingleSelectDialogPreference(
preferenceTitle = managerString(stringId = R.string.settings_preference_theme_title),
preferenceDescription = managerThemePref,
isDialogVisible = showDialog,
currentSelectedKey = selectedKey,
buttons = listOf(
RadioButtonPreference(
title = managerString(R.string.settings_preference_theme_light),
key = "Light"
),
RadioButtonPreference(
title = managerString(R.string.settings_preference_theme_dark),
key = "Dark"
),
RadioButtonPreference(
title = managerString(R.string.settings_option_system_default),
key = "System Default"
)
),
onPreferenceClick = {
showDialog = true
},
onDismissRequest = {
showDialog = false
selectedKey = managerThemePref
},
onItemClick = {
selectedKey = it
},
onSave = {
managerThemePref = selectedKey
showDialog = false
}
)
}

View File

@ -1,100 +0,0 @@
package com.vanced.manager.ui.widget.app
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.rounded.DeleteForever
import androidx.compose.material.icons.rounded.Download
import androidx.compose.material.icons.rounded.Launch
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import coil.compose.ImagePainter
import com.vanced.manager.R
import com.vanced.manager.ui.component.text.AppVersionText
import com.vanced.manager.ui.component.text.ManagerText
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AppCard(
appName: String,
appIcon: ImagePainter,
appInstalledVersion: String?,
appRemoteVersion: String?,
onAppDownloadClick: () -> Unit,
onAppUninstallClick: () -> Unit,
onAppLaunchClick: () -> Unit,
onAppInfoClick: () -> Unit,
) {
BaseAppCard(
appTitle = {
ManagerText(
modifier = Modifier.fillMaxSize(),
text = appName,
textStyle = MaterialTheme.typography.titleMedium
)
},
appIcon = {
Image(
modifier = Modifier.size(48.dp),
painter = appIcon,
contentDescription = "App Icon",
)
},
appTrailing = {
IconButton(onClick = onAppInfoClick) {
Icon(
imageVector = Icons.Outlined.Info,
contentDescription = "App Info"
)
}
},
appVersionsColumn = {
AppVersionText(
text = stringResource(
id = R.string.app_version_latest,
appRemoteVersion ?: stringResource(
id = R.string.app_content_unavailable
)
)
)
AppVersionText(
text = stringResource(
id = R.string.app_version_installed,
appInstalledVersion ?: stringResource(
id = R.string.app_content_unavailable
)
)
)
},
appActionsRow = {
if (appInstalledVersion != null) {
IconButton(onClick = onAppUninstallClick) {
Icon(
imageVector = Icons.Rounded.DeleteForever,
contentDescription = "Uninstall"
)
}
IconButton(onClick = onAppLaunchClick) {
Icon(
imageVector = Icons.Rounded.Launch,
contentDescription = "Launch",
)
}
}
IconButton(onClick = onAppDownloadClick) {
Icon(
imageVector = Icons.Rounded.Download,
contentDescription = "Install",
)
}
}
)
}

View File

@ -1,71 +0,0 @@
package com.vanced.manager.ui.widget.app
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.component.modifier.managerPlaceholder
import com.vanced.manager.ui.component.text.AppVersionText
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.theme.MediumShape
import com.vanced.manager.ui.theme.SmallShape
@Composable
fun AppCardPlaceholder() {
BaseAppCard(
appTitle = {
ManagerText(
modifier = Modifier
.clip(MediumShape)
.managerPlaceholder(true),
text = " ".repeat(40),
textStyle = MaterialTheme.typography.titleMedium
)
},
appIcon = {
Box(
Modifier
.clip(MediumShape)
.managerPlaceholder(true)
.size(48.dp)
)
},
appTrailing = {
Box(
Modifier
.clip(CircleShape)
.managerPlaceholder(true)
.size(24.dp)
)
},
appVersionsColumn = {
AppVersionText(
modifier = Modifier
.managerPlaceholder(true)
.clip(SmallShape),
text = " ".repeat(30)
)
AppVersionText(
modifier = Modifier
.managerPlaceholder(true)
.clip(SmallShape),
text = " ".repeat(30)
)
},
appActionsRow = {
Box(
Modifier
.clip(MediumShape)
.fillMaxWidth(0.8f)
.height(36.dp)
.managerPlaceholder(true)
)
}
)
}

View File

@ -1,82 +0,0 @@
package com.vanced.manager.ui.widget.app
import androidx.compose.foundation.layout.*
import androidx.compose.material.Divider
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.vanced.manager.R
import com.vanced.manager.ui.component.card.ManagerTonalCard
import com.vanced.manager.ui.component.list.ManagerListItem
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.theme.LargeShape
import com.vanced.manager.ui.theme.MediumShape
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
@Composable
fun BaseAppCard(
appTitle: @Composable () -> Unit,
appIcon: @Composable () -> Unit,
appTrailing: @Composable () -> Unit,
appVersionsColumn: @Composable ColumnScope.() -> Unit,
appActionsRow: @Composable RowScope.() -> Unit,
) {
ManagerTonalCard(
modifier = Modifier.fillMaxWidth(),
shape = LargeShape,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = DefaultContentPaddingHorizontal,
vertical = DefaultContentPaddingVertical
),
verticalArrangement = Arrangement
.spacedBy(DefaultContentPaddingVertical)
) {
ManagerListItem(
modifier = Modifier.fillMaxWidth(),
title = appTitle,
icon = appIcon,
trailing = appTrailing
)
Divider(
modifier = Modifier
.fillMaxWidth()
.clip(MediumShape),
thickness = 2.dp,
color = LocalContentColor.current.copy(alpha = 0.12f)
)
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(
modifier = Modifier.wrapContentWidth(),
verticalArrangement = Arrangement.spacedBy(4.dp),
horizontalAlignment = Alignment.Start
) {
ManagerText(
text = stringResource(id = R.string.app_versions),
textStyle = MaterialTheme.typography.bodyMedium
)
appVersionsColumn()
}
Row(
modifier = Modifier.wrapContentWidth(),
content = appActionsRow,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
)
}
}
}
}

View File

@ -1,21 +0,0 @@
package com.vanced.manager.ui.widget.button
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.R
import com.vanced.manager.ui.component.button.ManagerThemedTextButton
import com.vanced.manager.ui.resources.managerString
@Composable
fun ManagerCancelButton(
onClick: () -> Unit
) {
ManagerThemedTextButton(
modifier = Modifier.fillMaxWidth(),
text = managerString(
stringId = R.string.dialog_button_cancel
),
onClick = onClick
)
}

View File

@ -1,21 +0,0 @@
package com.vanced.manager.ui.widget.button
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.R
import com.vanced.manager.ui.component.button.ManagerThemedTextButton
import com.vanced.manager.ui.resources.managerString
@Composable
fun ManagerCloseButton(
onClick: () -> Unit
) {
ManagerThemedTextButton(
modifier = Modifier.fillMaxWidth(),
text = managerString(
stringId = R.string.dialog_button_close
),
onClick = onClick
)
}

View File

@ -1,21 +0,0 @@
package com.vanced.manager.ui.widget.button
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.R
import com.vanced.manager.ui.component.button.ManagerThemedTextButton
import com.vanced.manager.ui.resources.managerString
@Composable
fun ManagerDownloadButton(
onClick: () -> Unit
) {
ManagerThemedTextButton(
modifier = Modifier.fillMaxWidth(),
text = managerString(
stringId = R.string.app_download_dialog_confirm
),
onClick = onClick
)
}

View File

@ -1,25 +0,0 @@
package com.vanced.manager.ui.widget.button
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.vanced.manager.R
import com.vanced.manager.ui.component.button.ManagerThemedTextButton
import com.vanced.manager.ui.component.color.managerAccentColor
import com.vanced.manager.ui.resources.managerString
@Composable
fun ManagerResetButton(
backgroundColor: Color = managerAccentColor(),
onClick: () -> Unit
) {
ManagerThemedTextButton(
modifier = Modifier.fillMaxWidth(),
text = managerString(
stringId = R.string.dialog_button_reset
),
backgroundColor = backgroundColor,
onClick = onClick
)
}

View File

@ -1,25 +0,0 @@
package com.vanced.manager.ui.widget.button
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.vanced.manager.R
import com.vanced.manager.ui.component.button.ManagerThemedTextButton
import com.vanced.manager.ui.component.color.managerAccentColor
import com.vanced.manager.ui.resources.managerString
@Composable
fun ManagerSaveButton(
backgroundColor: Color = managerAccentColor(),
onClick: () -> Unit
) {
ManagerThemedTextButton(
modifier = Modifier.fillMaxWidth(),
text = managerString(
stringId = R.string.dialog_button_save
),
backgroundColor = backgroundColor,
onClick = onClick
)
}

View File

@ -1,35 +0,0 @@
package com.vanced.manager.ui.widget.checkbox
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import com.vanced.manager.ui.component.animation.jumpAnimation
import com.vanced.manager.ui.component.checkbox.ManagerCheckbox
import com.vanced.manager.ui.theme.MediumShape
@Composable
fun ManagerAnimatedCheckbox(
size: Dp,
isChecked: Boolean,
shape: Shape = MediumShape,
onCheckedChange: (isChecked: Boolean) -> Unit,
) {
val transition = updateTransition(
targetState = isChecked,
label = "Checkbox Animation"
)
val animatedSize by transition.jumpAnimation(
initialValue = size,
label = "Checkbox Size"
)
ManagerCheckbox(
modifier = Modifier.size(animatedSize),
isChecked = isChecked,
shape = shape,
onCheckedChange = onCheckedChange
)
}

View File

@ -1,76 +0,0 @@
package com.vanced.manager.ui.widget.layout
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.component.color.managerAnimatedColor
import com.vanced.manager.ui.component.text.ManagerText
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
@Composable
fun CategoryLayout(
categoryName: String,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(DefaultContentPaddingVertical)
) {
ManagerText(
modifier = Modifier.padding(start = DefaultContentPaddingHorizontal),
text = categoryName,
textStyle = MaterialTheme.typography.headlineSmall,
color = managerAnimatedColor(MaterialTheme.colorScheme.onSurface)
)
content()
}
}
fun <T> LazyListScope.managerCategory(
categoryName: String,
items: List<T>,
itemContent: @Composable (T) -> Unit
) {
item {
ManagerText(
modifier = Modifier.padding(
horizontal = DefaultContentPaddingHorizontal,
vertical = DefaultContentPaddingVertical
),
text = categoryName,
textStyle = MaterialTheme.typography.headlineSmall,
color = managerAnimatedColor(MaterialTheme.colorScheme.onSurface)
)
}
items(items) { item ->
itemContent(item)
}
}
fun LazyListScope.managerCategory(
categoryName: String,
content: @Composable LazyItemScope.() -> Unit,
) {
item {
ManagerText(
modifier = Modifier
.padding(
horizontal = DefaultContentPaddingHorizontal,
vertical = DefaultContentPaddingVertical
),
text = categoryName,
textStyle = MaterialTheme.typography.headlineSmall,
color = managerAnimatedColor(MaterialTheme.colorScheme.onSurface)
)
}
item(content = content)
}

View File

@ -1,18 +0,0 @@
package com.vanced.manager.ui.widget.layout
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
@Composable
fun SettingsCategoryLayout(
categoryName: String,
content: @Composable () -> Unit
) {
CategoryLayout(
categoryName = categoryName,
) {
Column {
content()
}
}
}

View File

@ -1,40 +0,0 @@
package com.vanced.manager.ui.widget.list
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.component.color.managerTextColor
import com.vanced.manager.ui.component.list.ManagerSelectableListItem
import com.vanced.manager.ui.component.modifier.managerClickable
import com.vanced.manager.ui.component.text.ManagerText
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CheckboxItem(
text: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
ManagerSelectableListItem(
modifier = modifier
.managerClickable(onClick = {
onCheckedChange(!checked)
}),
title = {
ManagerText(
text = text,
color = managerTextColor(),
textStyle = MaterialTheme.typography.titleSmall
)
},
trailing = {
Checkbox(
checked = checked,
onCheckedChange = null
)
}
)
}

View File

@ -1,38 +0,0 @@
package com.vanced.manager.ui.widget.list
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.component.color.managerTextColor
import com.vanced.manager.ui.component.list.ManagerSelectableListItem
import com.vanced.manager.ui.component.modifier.managerClickable
import com.vanced.manager.ui.component.text.ManagerText
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RadiobuttonItem(
text: String,
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
ManagerSelectableListItem(
modifier = modifier
.managerClickable(onClick = onClick),
title = {
ManagerText(
text = text,
color = managerTextColor(),
textStyle = MaterialTheme.typography.titleSmall
)
},
trailing = {
RadioButton(
selected = selected,
onClick = null
)
}
)
}

View File

@ -1,35 +0,0 @@
package com.vanced.manager.ui.widget.radiobutton
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import com.vanced.manager.ui.component.animation.jumpAnimation
import com.vanced.manager.ui.component.radiobutton.ManagerRadiobutton
import com.vanced.manager.ui.theme.MediumShape
@Composable
fun ManagerAnimatedRadiobutton(
size: Dp,
isSelected: Boolean,
shape: Shape = MediumShape,
onClick: () -> Unit
) {
val transition = updateTransition(
targetState = isSelected,
label = "Radiobutton Animation"
)
val animatedSize by transition.jumpAnimation(
initialValue = size,
label = "Checkbox Size"
)
ManagerRadiobutton(
modifier = Modifier.size(animatedSize),
isSelected = isSelected,
shape = shape,
onClick = onClick
)
}

View File

@ -1,32 +0,0 @@
package com.vanced.manager.ui.widget.settings
import androidx.compose.runtime.Composable
@Composable
fun SettingsAccentColorItem() {
// var localAccentColor by remember { mutableStateOf(managerAccentColorPref.value.value) }
// DialogPreference(
// preferenceTitle = managerString(
// stringId = R.string.settings_preference_accent_color_title
// ),
// preferenceDescription = "#" + Integer.toHexString(localAccentColor.toInt()),
// buttons = { isShown ->
// ManagerResetButton(
// backgroundColor = Color(localAccentColor)
// ) {
// isShown.value = false
// managerAccentColorPref.save(defAccentColor)
// }
// ManagerSaveButton(
// backgroundColor = Color(localAccentColor)
// ) {
// isShown.value = false
// managerAccentColorPref.save(localAccentColor)
// }
// }
// ) {
// ManagerColorPicker {
// localAccentColor = it
// }
// }
}

View File

@ -1,17 +0,0 @@
package com.vanced.manager.ui.widget.settings
import androidx.compose.runtime.Composable
import com.vanced.manager.R
import com.vanced.manager.ui.component.preference.Preference
import com.vanced.manager.ui.resources.managerString
@Composable
fun SettingsClearFilesItem() {
Preference(
preferenceTitle = managerString(
stringId = R.string.settings_preference_clear_files_title
),
preferenceDescription = null,
onClick = {}
)
}

View File

@ -1,19 +0,0 @@
package com.vanced.manager.ui.widget.settings
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import com.vanced.manager.R
import com.vanced.manager.core.preferences.holder.useCustomTabsPref
import com.vanced.manager.ui.component.preference.CheckboxPreference
@Composable
fun SettingsCustomTabsItem() {
CheckboxPreference(
preferenceTitle = stringResource(id = R.string.settings_preference_use_custom_tabs_title),
preferenceDescription = stringResource(id = R.string.settings_preference_use_custom_tabs_summary),
isChecked = useCustomTabsPref,
onCheckedChange = {
useCustomTabsPref = it
}
)
}

View File

@ -1,50 +0,0 @@
package com.vanced.manager.ui.widget.settings
import androidx.compose.runtime.*
import com.vanced.manager.R
import com.vanced.manager.core.preferences.RadioButtonPreference
import com.vanced.manager.core.preferences.holder.managerVariantPref
import com.vanced.manager.core.util.isMagiskInstalled
import com.vanced.manager.ui.component.preference.SingleSelectDialogPreference
import com.vanced.manager.ui.resources.managerString
@Composable
fun SettingsManagerVariantItem() {
var showDialog by remember { mutableStateOf(false) }
var selectedKey by remember { mutableStateOf(managerVariantPref) }
SingleSelectDialogPreference(
preferenceTitle = managerString(
stringId = R.string.settings_preference_variant_title
),
preferenceDescription = managerVariantPref,
isDialogVisible = showDialog,
currentSelectedKey = selectedKey,
buttons = listOf(
RadioButtonPreference(
title = "nonroot",
key = "nonroot"
),
RadioButtonPreference(
title = "root",
key = "root"
),
),
onPreferenceClick = {
showDialog = true
},
onDismissRequest = {
showDialog = false
selectedKey = managerVariantPref
},
onItemClick = {
if (it == "root" && !isMagiskInstalled)
return@SingleSelectDialogPreference
selectedKey = it
},
onSave = {
managerVariantPref = selectedKey
showDialog = false
}
)
}

View File

@ -1,26 +0,0 @@
package com.vanced.manager.ui.widget.settings
import androidx.compose.runtime.Composable
import com.vanced.manager.core.preferences.managerBooleanPreference
import com.vanced.manager.core.util.notificationApps
import com.vanced.manager.ui.component.preference.CheckboxPreference
@Composable
fun SettingsNotificationsItem() {
notificationApps.forEach { notificationApp ->
with(notificationApp) {
var appNotificationsPref by managerBooleanPreference(
key = "${prefKey}_notifications",
defaultValue = true
)
CheckboxPreference(
preferenceTitle = "$app Push Notifications",
preferenceDescription = "Receive push notifications when an update for $app is released",
isChecked = appNotificationsPref,
onCheckedChange = {
appNotificationsPref = it
}
)
}
}
}

View File

@ -1,48 +0,0 @@
package com.vanced.manager.ui.widget.settings
import androidx.compose.runtime.*
import com.vanced.manager.R
import com.vanced.manager.core.preferences.RadioButtonPreference
import com.vanced.manager.core.preferences.holder.managerThemePref
import com.vanced.manager.ui.component.preference.SingleSelectDialogPreference
import com.vanced.manager.ui.resources.managerString
@Composable
fun ThemeSettingsItem() {
var showDialog by remember { mutableStateOf(false) }
var selectedKey by remember { mutableStateOf(managerThemePref) }
SingleSelectDialogPreference(
preferenceTitle = managerString(stringId = R.string.settings_preference_theme_title),
preferenceDescription = managerThemePref,
isDialogVisible = showDialog,
currentSelectedKey = selectedKey,
buttons = listOf(
RadioButtonPreference(
title = managerString(R.string.settings_preference_theme_light),
key = "Light"
),
RadioButtonPreference(
title = managerString(R.string.settings_preference_theme_dark),
key = "Dark"
),
RadioButtonPreference(
title = managerString(R.string.settings_option_system_default),
key = "System Default"
)
),
onPreferenceClick = {
showDialog = true
},
onDismissRequest = {
showDialog = false
selectedKey = managerThemePref
},
onItemClick = {
selectedKey = it
},
onSave = {
managerThemePref = selectedKey
showDialog = false
}
)
}