0
0
Fork 0
mirror of https://github.com/YTVanced/VancedManager synced 2024-11-25 20:55:12 +00:00
This commit is contained in:
X1nto 2021-06-26 21:09:39 +04:00
parent c6017fe248
commit e6214a1734
172 changed files with 3123 additions and 2161 deletions

View file

@ -1,3 +1,9 @@
import Dependencies.accompanistVersion
import Dependencies.composeVersion
import Dependencies.koinVersion
import Dependencies.orchestraVersion
import Dependencies.retrofitVersion
plugins {
id("com.android.application")
kotlin("android")
@ -47,13 +53,16 @@ android {
targetCompatibility = JavaVersion.VERSION_1_8
}
composeOptions {
kotlinCompilerExtensionVersion = composeVersion
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
jvmTarget = "1.8"
useIR = true
freeCompilerArgs = freeCompilerArgs + "-Xopt-in=kotlin.RequiresOptIn"
freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
}
@ -81,39 +90,36 @@ dependencies {
implementation("com.google.android.material:material:1.3.0")
implementation("androidx.browser:browser:1.3.0")
val composeVersion = "1.0.0-beta07"
implementation("androidx.compose.ui:ui:$composeVersion")
implementation("androidx.compose.material:material:$composeVersion")
implementation("androidx.compose.ui:ui-tooling:$composeVersion")
implementation("androidx.compose.ui:ui-util:$composeVersion")
implementation("androidx.compose.compiler:compiler:$composeVersion")
implementation("androidx.compose.foundation:foundation:$composeVersion")
implementation("androidx.compose.material:material:$composeVersion")
implementation("androidx.compose.material:material-icons-core:$composeVersion")
implementation("androidx.compose.material:material-icons-extended:$composeVersion")
implementation("androidx.compose.material:material:$composeVersion")
implementation("androidx.compose.material:material:$composeVersion")
implementation("androidx.compose.runtime:runtime-livedata:$composeVersion")
implementation("androidx.compose.ui:ui-tooling:$composeVersion")
implementation("androidx.compose.ui:ui-util:$composeVersion")
implementation("androidx.compose.ui:ui:$composeVersion")
implementation("androidx.activity:activity-compose:1.3.0-alpha08")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.1")
implementation("androidx.navigation:navigation-compose:2.4.0-alpha01")
implementation("androidx.preference:preference-ktx:1.1.1")
implementation("androidx.activity:activity-compose:1.3.0-beta02")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-alpha02")
implementation("androidx.navigation:navigation-compose:2.4.0-alpha03")
val accompanistVersion = "0.10.0"
implementation("com.google.accompanist:accompanist-glide:$accompanistVersion")
implementation("com.google.accompanist:accompanist-swiperefresh:$accompanistVersion")
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
implementation("com.google.accompanist:accompanist-placeholder-material:$accompanistVersion")
implementation("com.github.madrapps:pikolo:2.0.1")
implementation("com.github.skydoves:orchestra-colorpicker:$orchestraVersion")
implementation("androidx.datastore:datastore-preferences:1.0.0-beta02")
val koinVersion = "3.0.1"
implementation("io.insert-koin:koin-android:$koinVersion")
implementation("io.insert-koin:koin-androidx-compose:$koinVersion")
val retrofitVersion = "2.9.0"
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.retrofit2:converter-gson:$retrofitVersion")
implementation("dev.burnoo:compose-remember-preference:0.3.0")
implementation("com.github.x1nto:apkhelper:1.1.0")
testImplementation("junit:junit:4.13.2")

View file

@ -25,7 +25,7 @@
</activity>
<activity
android:name=".MainActivity"
android:name=".ui.MainActivity"
android:theme="@style/Theme.MaterialComponents.NoActionBar"
android:label="@string/app_name"/>

View file

@ -14,13 +14,15 @@ class ManagerApplication : Application() {
androidContext(this@ManagerApplication)
modules(
apiModule,
downloaderModule,
mapperModule,
viewModelModule,
serviceModule,
repositoryModule,
packageManagerModule,
networkModule,
downloaderModule
packageManagerModule,
preferenceModule,
repositoryModule,
serviceModule,
viewModelModule,
)
}
}

View file

@ -3,6 +3,7 @@ package com.vanced.manager
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import com.vanced.manager.ui.MainActivity
class SplashScreenActivity : ComponentActivity() {

View file

@ -0,0 +1,21 @@
package com.vanced.manager.di
import com.vanced.manager.downloader.api.VancedAPI
import com.vanced.manager.network.util.BASE
import okhttp3.OkHttpClient
import org.koin.dsl.module
import retrofit2.Retrofit
val apiModule = module {
fun provideVancedAPI(
okHttpClient: OkHttpClient
): VancedAPI = Retrofit.Builder()
.baseUrl(BASE)
.client(okHttpClient)
.build()
.create(VancedAPI::class.java)
single { provideVancedAPI(get()) }
}

View file

@ -1,29 +1,17 @@
package com.vanced.manager.di
import com.vanced.manager.downloader.impl.VancedDownloader
import com.vanced.manager.downloader.api.VancedAPI
import com.vanced.manager.network.util.BASE
import okhttp3.OkHttpClient
import com.vanced.manager.downloader.impl.VancedDownloader
import org.koin.dsl.module
import retrofit2.Retrofit
val downloaderModule = module {
fun provideVancedAPI(
okHttpClient: OkHttpClient
): VancedAPI = Retrofit.Builder()
.baseUrl(BASE)
.client(okHttpClient)
.build()
.create(VancedAPI::class.java)
fun provideVancedDownloader(
vancedAPI: VancedAPI
): VancedDownloader = VancedDownloader(
vancedAPI = vancedAPI
)
vancedAPI: VancedAPI,
): VancedDownloader = VancedDownloader(vancedAPI)
single { provideVancedAPI(get()) }
single { provideVancedDownloader(get()) }
single {
provideVancedDownloader(get())
}
}

View file

@ -0,0 +1,15 @@
package com.vanced.manager.di
import android.content.Context
import com.vanced.manager.installer.AppInstaller
import com.vanced.manager.ui.viewmodel.HomeViewModel
import org.koin.dsl.module
val installerModule = module {
fun provideAppInstaller(
context: Context,
homeViewModel: HomeViewModel
) = AppInstaller(context, homeViewModel)
single { provideAppInstaller(get(), get()) }
}

View file

@ -0,0 +1,15 @@
package com.vanced.manager.di
import android.content.Context
import androidx.datastore.preferences.preferencesDataStore
import org.koin.dsl.module
val Context.dataStore by preferencesDataStore("manager_settings")
val preferenceModule = module {
fun provideDatastore(
context: Context
) = context.dataStore
single { provideDatastore(get()) }
}

View file

@ -1,6 +1,7 @@
package com.vanced.manager.domain.model
import com.vanced.manager.downloader.base.BaseDownloader
import com.vanced.manager.ui.widgets.home.installation.InstallationOption
data class App(
val name: String? = null,
@ -17,7 +18,9 @@ data class App(
val packageNameRoot: String? = null,
val changelog: String? = null,
val url: String? = null,
val versions: List<String>? = null,
val themes: List<String>? = null,
val languages: List<String>? = null,
val downloader: BaseDownloader? = null
val downloader: BaseDownloader? = null,
val installationOptions: List<InstallationOption>? = null
)

View file

@ -4,6 +4,4 @@ interface EntityMapper <T, Model> {
suspend fun mapToModel(entity: T): Model
suspend fun mapFromModel(model: Model): T
}

View file

@ -8,7 +8,7 @@ import retrofit2.http.Streaming
interface VancedAPI {
@GET("apks/v{version}/{variant}/{type}/{apkName}.apk")
@GET("apks/{version}/{variant}/{type}/{apkName}.apk")
@Streaming
fun getApk(
@Path("version") version: String,

View file

@ -4,17 +4,19 @@ import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.vanced.manager.ui.composables.InstallationOption
import com.vanced.manager.installer.AppInstaller
import com.vanced.manager.util.log
import okhttp3.ResponseBody
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import retrofit2.Call
import retrofit2.awaitResponse
import java.io.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
abstract class BaseDownloader(
private val appName: String
val appName: String
) : KoinComponent {
var downloadProgress by mutableStateOf(0f)
@ -27,14 +29,13 @@ abstract class BaseDownloader(
private lateinit var call: Call<ResponseBody>
private val context: Context by inject()
private val tag = this::class.simpleName!!
val context: Context by inject()
val appInstaller: AppInstaller by inject()
abstract suspend fun download()
abstract val installationOptions: List<InstallationOption>
private val tag = this::class.simpleName!!
suspend fun downloadFile(
call: Call<ResponseBody>,
folderStructure: String,
@ -61,7 +62,6 @@ abstract class BaseDownloader(
val error = response.errorBody()?.toString()
if (error != null) {
error(error)
log(tag, error)
}
}
} catch (e: Exception) {

View file

@ -1,7 +1,6 @@
package com.vanced.manager.downloader.impl
import com.vanced.manager.downloader.base.BaseDownloader
import com.vanced.manager.ui.composables.InstallationOption
object MicrogDownloader : BaseDownloader("") {
@ -9,7 +8,4 @@ object MicrogDownloader : BaseDownloader("") {
}
override val installationOptions: List<InstallationOption>
get() = TODO("Not yet implemented")
}

View file

@ -1,7 +1,6 @@
package com.vanced.manager.downloader.impl
import com.vanced.manager.downloader.base.BaseDownloader
import com.vanced.manager.ui.composables.InstallationOption
object MusicDownloader : BaseDownloader("") {
@ -9,7 +8,4 @@ object MusicDownloader : BaseDownloader("") {
}
override val installationOptions: List<InstallationOption>
get() = TODO("Not yet implemented")
}

View file

@ -2,110 +2,71 @@ package com.vanced.manager.downloader.impl
import com.vanced.manager.downloader.api.VancedAPI
import com.vanced.manager.downloader.base.BaseDownloader
import com.vanced.manager.ui.composables.InstallationOption
import com.vanced.manager.ui.composables.InstallationOptionKey
class VancedDownloader(
private val vancedAPI: VancedAPI
private val vancedAPI: VancedAPI,
) : BaseDownloader(
appName = "vanced"
) {
private val themeInstallationOption = InstallationOption(
title = "Languages",
descriptionKey = InstallationOptionKey(
keyName = "vanced_languages",
keyDefaultValue = "en"
)
) { show, preference ->
// DialogRadioButtonPreference(
// preferenceTitle = "Language",
// preferenceKey = ,
// defaultValue = ,
// buttons =
// )
}
private lateinit var version: String
private lateinit var variant: String
private val versionInstallationOption = InstallationOption(
title = "Languages",
descriptionKey = InstallationOptionKey(
keyName = "vanced_languages",
keyDefaultValue = "en"
)
) { show, preference ->
// DialogRadioButtonPreference(
// preferenceTitle = "Language",
// preferenceKey = ,
// defaultValue = ,
// buttons =
// )
}
private val languageInstallationOption = InstallationOption(
title = "Languages",
descriptionKey = InstallationOptionKey(
keyName = "vanced_languages",
keyDefaultValue = "en"
)
) { show, preference ->
// DialogRadioButtonPreference(
// preferenceTitle = "Language",
// preferenceKey = ,
// defaultValue = ,
// buttons =
// )
}
override val installationOptions: List<InstallationOption> get() = listOf(
languageInstallationOption
)
private lateinit var folderStructure: String
override suspend fun download() {
version = "v16.16.38"
variant = "nonroot"
folderStructure = "$appName/$version/$variant"
downloadTheme()
}
private suspend fun downloadTheme() {
downloadFile(
vancedAPI.getApk(
version = "16.16.38",
variant = "nonroot",
type = "Theme",
apkName = "black.apk"
),
folderStructure = "vanced/nonroot/v16.16.38",
fileName = "black.apk"
downloadVancedApk(
type = "Theme",
apkName = "black.apk"
) {
downloadArch()
}
}
private suspend fun downloadArch() {
downloadFile(
vancedAPI.getApk(
version = "16.16.38",
variant = "nonroot",
type = "Arch",
apkName = "split_config.x86.apk"
),
folderStructure = "vanced/nonroot/arch",
fileName = "black.apk"
downloadVancedApk(
type = "Arch",
apkName = "split_config.x86.apk"
) {
downloadLanguage()
}
}
private suspend fun downloadLanguage() {
downloadVancedApk(
type = "Language",
apkName = "split_config.en.apk"
) {
appInstaller.installVanced(version)
}
}
private suspend fun downloadVancedApk(
type: String,
apkName: String,
onDownload: suspend () -> Unit,
) {
downloadFile(
vancedAPI.getApk(
version = "16.16.38",
variant = "nonroot",
type = "Arch",
apkName = "split_config.x86.apk"
version = version,
variant = variant,
type = type,
apkName = apkName
),
folderStructure = "vanced/nonroot/lang",
fileName = "black.apk"
folderStructure = folderStructure,
fileName = apkName,
onError = {
}
) {
downloadLanguage()
onDownload()
}
}

View file

@ -1,15 +1,18 @@
package com.vanced.manager.installer
import android.content.Context
import com.vanced.manager.ui.viewmodel.HomeViewModel
import com.xinto.apkhelper.installSplitApks
import com.xinto.apkhelper.statusCallback
import com.xinto.apkhelper.statusCallbackBuilder
import com.vanced.manager.ui.viewmodel.HomeViewModel
class AppInstaller(
private val context: Context,
private val viewModel: HomeViewModel
) {
fun installVanced() {
fun installVanced(version: String) {
installSplitApks(context.getExternalFilesDir("vanced/$version")!!.path, context)
}
fun installMusic() {

View file

@ -11,6 +11,7 @@ data class AppDto(
@SerializedName("package_name_root") val packageNameRoot: String? = null,
@SerializedName("icon_url") val iconUrl: String? = null,
@SerializedName("url") val url: String? = null,
@SerializedName("versions") val versions: List<String>? = null,
@SerializedName("themes") val themes: List<String>? = null,
@SerializedName("languages") val languages: List<String>? = null
)

View file

@ -1,5 +1,6 @@
package com.vanced.manager.network.model
import com.vanced.manager.R
import com.vanced.manager.domain.datasource.PackageInformationDataSource
import com.vanced.manager.domain.model.App
import com.vanced.manager.domain.model.AppStatus
@ -11,6 +12,14 @@ import com.vanced.manager.downloader.impl.VancedDownloader
import com.vanced.manager.network.util.MICROG_NAME
import com.vanced.manager.network.util.MUSIC_NAME
import com.vanced.manager.network.util.VANCED_NAME
import com.vanced.manager.ui.preferences.*
import com.vanced.manager.ui.preferences.holder.musicVersionPref
import com.vanced.manager.ui.preferences.holder.vancedLanguagesPref
import com.vanced.manager.ui.preferences.holder.vancedThemePref
import com.vanced.manager.ui.preferences.holder.vancedVersionPref
import com.vanced.manager.ui.widgets.home.installation.CheckboxInstallationOption
import com.vanced.manager.ui.widgets.home.installation.InstallationOption
import com.vanced.manager.ui.widgets.home.installation.RadiobuttonInstallationOption
class AppDtoMapper(
private val packageInformationDataSource: PackageInformationDataSource,
@ -38,25 +47,11 @@ class AppDtoMapper(
iconUrl = iconUrl,
changelog = changelog,
url = url,
versions = versions,
themes = themes,
languages = languages,
downloader = getDownloader(name)
)
}
override suspend fun mapFromModel(model: App): AppDto =
with (model) {
AppDto(
name = name,
version = remoteVersion,
versionCode = remoteVersionCode,
changelog = changelog,
url = url,
themes = themes,
languages = languages,
packageName = packageName,
packageNameRoot = packageNameRoot,
iconUrl = iconUrl
downloader = getDownloader(name),
installationOptions = getInstallationOptions(entity)
)
}
@ -78,4 +73,53 @@ class AppDtoMapper(
MICROG_NAME -> MicrogDownloader
else -> null
}
private fun getInstallationOptions(app: AppDto): List<InstallationOption>? =
when (app.name) {
VANCED_NAME -> listOf(
RadiobuttonInstallationOption(
title = R.string.app_installation_options_theme,
preference = vancedThemePref,
buttons = app.versions?.map {
RadioButtonPreference(
title = it,
key = it
)
} ?: emptyList()
),
RadiobuttonInstallationOption(
title = R.string.app_installation_options_version,
preference = vancedVersionPref,
buttons = app.versions?.map {
RadioButtonPreference(
title = it,
key = it
)
} ?: emptyList()
),
CheckboxInstallationOption(
title = R.string.app_installation_options_language,
preference = vancedLanguagesPref,
buttons = app.versions?.map {
CheckboxPreference(
title = it,
key = it
)
} ?: emptyList()
),
)
MUSIC_NAME -> listOf(
RadiobuttonInstallationOption(
title = R.string.app_installation_options_version,
preference = musicVersionPref,
buttons = app.versions?.map {
RadioButtonPreference(
title = it,
key = it
)
} ?: emptyList()
),
)
else -> null
}
}

View file

@ -18,15 +18,4 @@ class JsonDtoMapper(
)
}
override suspend fun mapFromModel(model: Json): JsonDto =
with (model) {
JsonDto(
isMicrogBroken = isMicrogBroken,
manager = appDtoMapper.mapFromModel(manager),
vanced = appDtoMapper.mapFromModel(vanced),
music = appDtoMapper.mapFromModel(music),
microg = appDtoMapper.mapFromModel(microg)
)
}
}

View file

@ -1,16 +0,0 @@
package com.vanced.manager.preference
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.preference.PreferenceManager
val Context.defPrefs: SharedPreferences get() = PreferenceManager.getDefaultSharedPreferences(this)
var SharedPreferences.managerTheme
get() = getString("manager_theme", "Light")
set(value) {
edit {
putString("manager_theme", value)
}
}

View file

@ -1,9 +1,8 @@
package com.vanced.manager
package com.vanced.manager.ui
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.background
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
@ -11,37 +10,36 @@ import androidx.compose.material.icons.filled.ArrowBackIos
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.vanced.manager.preference.defPrefs
import com.vanced.manager.preference.managerTheme
import com.vanced.manager.ui.composables.*
import com.vanced.manager.ui.layouts.*
import com.vanced.manager.ui.components.color.managerAnimatedColor
import com.vanced.manager.ui.components.color.managerCardColor
import com.vanced.manager.ui.components.color.managerSurfaceColor
import com.vanced.manager.ui.components.color.managerTextColor
import com.vanced.manager.ui.components.menu.ManagerDropdownMenuItem
import com.vanced.manager.ui.screens.Screen
import com.vanced.manager.ui.theme.ComposeTestTheme
import com.vanced.manager.ui.theme.ManagerTheme
import com.vanced.manager.ui.theme.isDark
import com.vanced.manager.ui.theme.managerTheme
import com.vanced.manager.ui.widgets.text.ToolbarTitleText
class MainActivity : AppCompatActivity() {
@ExperimentalAnimationApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
managerTheme = defPrefs.managerTheme
ComposeTestTheme {
//ButtonLayoutActivity()
ManagerTheme {
MainActivityLayout()
}
}
}
@ExperimentalAnimationApi
@Composable
fun MainActivityLayout() {
val isMenuExpanded = remember { mutableStateOf(false) }
@ -50,10 +48,11 @@ class MainActivity : AppCompatActivity() {
val isDark = isDark()
val navController = rememberNavController()
val dropdownScreens = listOf(
val screens = listOf(
Screen.Home,
Screen.Settings,
Screen.Logs,
Screen.About
Screen.About,
Screen.Logs
)
SideEffect {
@ -63,9 +62,9 @@ class MainActivity : AppCompatActivity() {
Scaffold(
topBar = {
MainToolbar(
navController,
dropdownScreens,
isMenuExpanded
navController = navController,
screens = screens,
isMenuExpanded = isMenuExpanded
)
},
backgroundColor = managerSurfaceColor()
@ -74,8 +73,10 @@ class MainActivity : AppCompatActivity() {
navController = navController,
startDestination = Screen.Home.route
) {
composable(Screen.Home.route) {
HomeLayout()
screens.fastForEach { screen ->
composable(screen.route) {
screen.content()
}
}
}
}
@ -84,23 +85,17 @@ class MainActivity : AppCompatActivity() {
@Composable
fun MainToolbar(
navController: NavController,
dropdownScreens: List<Screen>,
screens: List<Screen>,
isMenuExpanded: MutableState<Boolean>
) {
val currentScreenRoute = navController.currentBackStackEntryAsState().value?.destination?.route
TopAppBar(
title = {
navController.currentDestination
Text(
//This does not look good at all
text = Screen::class.sealedSubclasses.find {
it.objectInstance?.route == navController.currentDestination?.route
}?.objectInstance?.displayName ?: "",
color = managerAnimatedColor(color = MaterialTheme.colors.onSurface)
)
ToolbarTitleText(screens.find { it.route == currentScreenRoute }?.displayName)
},
backgroundColor = managerAnimatedColor(color = MaterialTheme.colors.surface),
actions = {
if (navController.currentDestination?.route == Screen.Home.route) {
if (currentScreenRoute == Screen.Home.route) {
IconButton(
onClick = { isMenuExpanded.value = !isMenuExpanded.value }
) {
@ -118,27 +113,22 @@ class MainActivity : AppCompatActivity() {
},
modifier = Modifier.background(managerCardColor())
) {
dropdownScreens.forEach {
screens.filter { it.route != currentScreenRoute }.forEach {
ManagerDropdownMenuItem(
isMenuExpanded = isMenuExpanded,
title = it.displayName
title = stringResource(id = it.displayName)
) {
isMenuExpanded.value = !isMenuExpanded.value
navController.navigate(it.route) {
popUpTo(Screen.Home.route)
anim {
enter = R.animator.fragment_enter
exit = R.animator.fragment_exit
popEnter = R.animator.fragment_enter_pop
popEnter = R.animator.fragment_exit_pop
popUpTo(Screen.Home.route) {
saveState = true
}
restoreState = true
}
}
}
}
}
},
navigationIcon = if (navController.currentDestination?.route != Screen.Home.route) { {
navigationIcon = if (currentScreenRoute != Screen.Home.route) { {
IconButton(onClick = {
navController.popBackStack()
}) {

View file

@ -0,0 +1,46 @@
package com.vanced.manager.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp
import com.vanced.manager.ui.theme.managerAccentColor
import java.util.*
@Composable
fun HeaderView(
modifier: Modifier = Modifier,
headerName: String,
headerPadding: Dp = 11.dp,
content: @Composable ColumnScope.() -> Unit
) {
Column(
modifier = modifier
) {
Header(headerName = headerName, headerPadding = headerPadding)
content()
}
}
@Composable
fun Header(
headerName: String,
headerPadding: Dp = 11.dp,
) {
Text(
headerName.uppercase(Locale.ROOT),
letterSpacing = 0.15.em,
color = MaterialTheme.colors.managerAccentColor,
modifier = Modifier.padding(horizontal = headerPadding),
fontWeight = FontWeight.Bold,
fontSize = 13.sp
)
}

View file

@ -0,0 +1,41 @@
package com.vanced.manager.ui.components.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.Icon
import androidx.compose.material.ripple.rememberRipple
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 IconButton(
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(bounded = false, radius = 18.dp)
)
.size(buttonSize),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = icon,
contentDescription = contentDescription
)
}
}

View file

@ -0,0 +1,37 @@
package com.vanced.manager.ui.components.button
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
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.components.color.managerAccentColor
@Composable
fun ManagerThemedButton(
modifier: Modifier = Modifier,
onClick: () -> Unit,
content: @Composable RowScope.() -> Unit
) {
val accentColor = managerAccentColor()
Button(
modifier = modifier.fillMaxWidth(),
onClick = onClick,
shape = MaterialTheme.shapes.medium,
colors = ButtonDefaults.buttonColors(
backgroundColor = managerAccentColor()
),
elevation = ButtonDefaults.elevation(0.dp, 0.dp, 0.dp)
) {
CompositionLocalProvider(LocalContentColor provides if (accentColor.luminance() > 0.7) Color.Black else Color.White) {
content()
}
}
}

View file

@ -0,0 +1,20 @@
package com.vanced.manager.ui.components.button
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.components.text.ManagerText
@Composable
fun ManagerThemedTextButton(
modifier: Modifier = Modifier,
@StringRes stringId: Int,
onClick: () -> Unit
) {
ManagerThemedButton(
modifier = modifier,
onClick = onClick
) {
ManagerText(stringId = stringId)
}
}

View file

@ -0,0 +1,46 @@
package com.vanced.manager.ui.components.card
import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
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 com.vanced.manager.ui.components.color.managerCardColor
@Composable
fun ManagerCard(
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = MaterialTheme.colors.surface,
content: @Composable () -> Unit,
) {
Card(
modifier = modifier,
shape = shape,
backgroundColor = backgroundColor,
elevation = 0.dp,
content = content
)
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ManagerCard(
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = managerCardColor(),
onClick: () -> Unit,
content: @Composable () -> Unit,
) {
Card(
modifier = modifier,
shape = shape,
backgroundColor = backgroundColor,
elevation = 0.dp,
content = content,
onClick = onClick
)
}

View file

@ -0,0 +1,84 @@
package com.vanced.manager.ui.components.card
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vanced.manager.ui.components.color.ThemedItemContentColorProvider
import com.vanced.manager.ui.components.color.managerAccentColor
private val cardModifier = Modifier.sizeIn(
minHeight = 95.dp,
minWidth = 95.dp
)
@Composable
fun ManagerItemCard(
title: String,
@DrawableRes icon: Int? = null
) {
ManagerThemedCard(
modifier = cardModifier,
) {
ManagerItemCardContent(title, icon)
}
}
@Composable
fun ManagerClickableItemCard(
title: String,
@DrawableRes icon: Int? = null,
onClick: () -> Unit
) {
ManagerClickableThemedCard(
modifier = cardModifier,
onClick = onClick
) {
ManagerItemCardContent(title, icon)
}
}
@Composable
private fun ManagerItemCardContent(
title: String,
@DrawableRes icon: Int? = null,
) {
Canvas(modifier = Modifier.requiredSize(72.dp)) {
drawCircle(
color = managerAccentColor(),
center = Offset(32f,32f)
)
}
Box(
modifier = Modifier.padding(12.dp)
) {
if (icon != null) {
ThemedItemContentColorProvider {
Icon(
modifier = Modifier
.size(28.dp)
.align(Alignment.TopStart),
painter = painterResource(id = icon),
contentDescription = title
)
}
}
Text(
modifier = Modifier.align(Alignment.BottomStart),
text = title,
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colors.onSurface
)
}
}

View file

@ -0,0 +1,33 @@
package com.vanced.manager.ui.components.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.ui.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) }
val useCustomTabs by useCustomTabsPref
ManagerClickableItemCard(
title = title,
icon = icon
) {
if (useCustomTabs) {
customTabs.launchUrl(context, uri)
} else {
context.startActivity(intent)
}
}
}

View file

@ -0,0 +1,38 @@
package com.vanced.manager.ui.components.card
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import com.vanced.manager.ui.components.color.managerThemedCardColor
@Composable
fun ManagerThemedCard(
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.medium,
content: @Composable () -> Unit,
) {
ManagerCard(
modifier = modifier,
shape = shape,
backgroundColor = managerThemedCardColor(),
content = content
)
}
@Composable
fun ManagerClickableThemedCard(
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.medium,
onClick: () -> Unit,
content: @Composable () -> Unit,
) {
ManagerCard(
modifier = modifier,
shape = shape,
backgroundColor = managerThemedCardColor(),
onClick = onClick,
) {
content()
}
}

View file

@ -0,0 +1,67 @@
package com.vanced.manager.ui.components.checkbox
import android.annotation.SuppressLint
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.keyframes
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.Done
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.components.card.ManagerCard
import com.vanced.manager.ui.components.color.contentColorForColor
import com.vanced.manager.ui.components.color.managerAccentColor
import com.vanced.manager.ui.components.color.managerAnimatedColor
import com.vanced.manager.ui.components.color.managerThemedCardColor
@SuppressLint("UnusedTransitionTargetStateParameter")
@Composable
fun ManagerCheckbox(
isChecked: Boolean,
onCheckedChange: (isChecked: Boolean) -> Unit
) {
val transition = updateTransition(targetState = isChecked, label = "Checked")
val cardSize by transition.animateDp(
transitionSpec = {
keyframes {
durationMillis = 250
32.dp at 50
48.dp at 150
40.dp at 250
}
},
label = "Checkbox size"
) { 40.dp }
val iconSize by transition.animateDp(
transitionSpec = {
keyframes {
durationMillis = 250
18.dp at 50
30.dp at 150
24.dp at 250
}
},
label = "Icon size"
) { 24.dp }
val cardColor = managerAnimatedColor(if (isChecked) managerAccentColor() else managerThemedCardColor())
val iconTint = managerAnimatedColor(if (isChecked) contentColorForColor(cardColor) else managerAccentColor())
ManagerCard(
modifier = Modifier.size(cardSize),
onClick = { onCheckedChange(!isChecked) },
backgroundColor = cardColor
) {
Icon(
modifier = Modifier.requiredSize(iconSize),
imageVector = if (isChecked) Icons.Rounded.Done else Icons.Rounded.Close,
tint = iconTint,
contentDescription = null
)
}
}

View file

@ -0,0 +1,35 @@
package com.vanced.manager.ui.components.color
import androidx.compose.material.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(managerThemedCardColor()),
content = content
)
}
@Composable
fun ThemedContentColorProvider(
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalContentColor provides managerAccentColor(),
content = content
)
}

View file

@ -0,0 +1,26 @@
package com.vanced.manager.ui.components.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(400.dp),
onColorListener = { envelope, _ -> setColor(envelope) },
children = {}
)
onColorSelected(selectedColor.color.toLong())
}

View file

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

View file

@ -0,0 +1,82 @@
package com.vanced.manager.ui.components.dialog
import androidx.annotation.StringRes
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.material.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.window.Dialog
import com.vanced.manager.ui.components.card.ManagerCard
import com.vanced.manager.ui.components.text.ManagerText
import com.vanced.manager.ui.utils.defaultContentPaddingHorizontal
@Composable
fun ManagerDialog(
@StringRes titleId: Int,
onDismissRequest: () -> Unit,
buttons: @Composable ColumnScope.() -> Unit,
content: @Composable ColumnScope.() -> Unit,
) {
ManagerDialog(
title = {
ManagerText(
modifier = Modifier.align(Alignment.CenterHorizontally),
stringId = titleId,
textStyle = MaterialTheme.typography.h2
)
},
onDismissRequest = onDismissRequest,
buttons = buttons,
content = content
)
}
@Composable
fun ManagerDialog(
title: String,
onDismissRequest: () -> Unit,
buttons: @Composable ColumnScope.() -> Unit,
content: @Composable ColumnScope.() -> Unit,
) {
ManagerDialog(
title = {
ManagerText(
modifier = Modifier.align(Alignment.CenterHorizontally),
textStyle = MaterialTheme.typography.h2,
text = title
)
},
onDismissRequest = onDismissRequest,
buttons = buttons,
content = content
)
}
@Composable
fun ManagerDialog(
title: @Composable ColumnScope.() -> Unit,
onDismissRequest: () -> Unit,
buttons: @Composable ColumnScope.() -> Unit,
content: @Composable ColumnScope.() -> Unit,
) {
Dialog(
onDismissRequest = onDismissRequest,
content = {
ManagerCard {
Column(
modifier = Modifier.padding(defaultContentPaddingHorizontal),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
title()
content()
buttons()
}
}
}
)
}

View file

@ -0,0 +1,21 @@
package com.vanced.manager.ui.components.layout
import androidx.compose.foundation.layout.Arrangement
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.unit.Dp
import androidx.compose.ui.unit.dp
@Composable
fun ManagerLazyColumn(
itemSpacing: Dp = 0.dp,
content: LazyListScope.() -> Unit
) {
LazyColumn(
contentPadding = PaddingValues(12.dp),
verticalArrangement = Arrangement.spacedBy(itemSpacing),
content = content
)
}

View file

@ -0,0 +1,31 @@
package com.vanced.manager.ui.components.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.utils.defaultContentPaddingVertical
@Composable
fun ManagerScrollableColumn(
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

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

View file

@ -0,0 +1,27 @@
package com.vanced.manager.ui.components.layout
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxWidth
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>,
content: @Composable (T) -> Unit
) {
val state = rememberLazyListState()
LazyRow(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
state = state
) {
items(items) { item ->
content(item)
}
}
}

View file

@ -0,0 +1,17 @@
package com.vanced.manager.ui.components.lifecycle
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import com.vanced.manager.R
@Composable
fun managerString(
@StringRes stringId: Int?
) = stringResource(id = stringId ?: R.string.dummy_placeholder_text)
@Composable
fun managerString(
@StringRes stringId: Int?,
vararg formatArgs: Any
) = stringResource(id = stringId ?: R.string.dummy_placeholder_text, *formatArgs)

View file

@ -0,0 +1,54 @@
package com.vanced.manager.ui.components.list
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Checkbox
import androidx.compose.material.CheckboxDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
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.unit.dp
import androidx.compose.ui.unit.sp
import com.vanced.manager.ui.components.color.managerTextColor
import com.vanced.manager.ui.theme.managerAccentColor
@Composable
fun CheckboxItem(
text: String,
isChecked: Boolean,
onCheck: (Boolean) -> Unit = {}
) {
fun toggle() {
onCheck(isChecked)
}
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { toggle() }
.padding(horizontal = 8.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = isChecked,
onCheckedChange = {
toggle()
},
colors = CheckboxDefaults.colors(
MaterialTheme.colors.managerAccentColor,
Color.LightGray
)
)
Text(
modifier = Modifier.padding(start = 12.dp),
text = text,
color = managerTextColor(),
fontSize = 18.sp
)
}
}

View file

@ -0,0 +1,57 @@
package com.vanced.manager.ui.components.list
import androidx.compose.foundation.layout.*
import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha
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
@Composable
fun ManagerListItem(
modifier: Modifier = Modifier,
title: @Composable () -> Unit,
description: @Composable (() -> Unit)? = null,
icon: @Composable (() -> Unit)? = null,
trailing: @Composable (() -> Unit)? = null
) {
Row(modifier = modifier) {
if (icon != null) {
Box(
modifier = Modifier.align(Alignment.CenterVertically)
) {
icon()
}
}
Column(
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,
)
.align(Alignment.CenterVertically)
) {
title()
if (description != null) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
description()
}
}
}
if (trailing != null) {
Box(
modifier = Modifier
.size(56.dp)
.align(Alignment.CenterVertically),
contentAlignment = Alignment.Center,
) {
trailing()
}
}
}
}

View file

@ -0,0 +1,53 @@
package com.vanced.manager.ui.components.list
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButton
import androidx.compose.material.RadioButtonDefaults
import androidx.compose.material.Text
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.unit.dp
import androidx.compose.ui.unit.sp
import com.vanced.manager.ui.components.color.managerTextColor
import com.vanced.manager.ui.theme.managerAccentColor
@Composable
fun <T> RadiobuttonItem(
text: String,
tag: T,
selected: Boolean,
onSelect: (tag: T) -> Unit
) {
val onClick = {
onSelect(tag)
}
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(horizontal = 8.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = selected,
onClick = onClick,
colors = RadioButtonDefaults.colors(
MaterialTheme.colors.managerAccentColor,
Color.LightGray
)
)
Text(
modifier = Modifier.padding(start = 12.dp),
text = text,
color = managerTextColor(),
fontSize = 18.sp
)
}
}

View file

@ -0,0 +1,21 @@
package com.vanced.manager.ui.components.menu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import com.vanced.manager.ui.components.text.ManagerText
@Composable
fun ManagerDropdownMenuItem(
title: String,
onClick: () -> Unit
) {
DropdownMenuItem(
onClick = onClick,
) {
ManagerText(
text = title,
textStyle = MaterialTheme.typography.body1
)
}
}

View file

@ -0,0 +1,18 @@
package com.vanced.manager.ui.components.placeholder
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

@ -0,0 +1,68 @@
package com.vanced.manager.ui.components.preference
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import com.vanced.manager.ui.components.checkbox.ManagerCheckbox
import com.vanced.manager.ui.preferences.ManagerPreference
import kotlinx.coroutines.launch
@Composable
fun CheckboxPreference(
@StringRes preferenceTitle: Int,
@StringRes preferenceDescription: Int? = null,
preference: ManagerPreference<Boolean>,
onCheckedChange: (isChecked: Boolean) -> Unit = {}
) {
val isChecked by preference
val coroutineScope = rememberCoroutineScope()
val onClick: () -> Unit = {
coroutineScope.launch {
preference.save(!isChecked)
onCheckedChange(isChecked)
}
}
Preference(
preferenceTitleId = preferenceTitle,
preferenceDescriptionId = preferenceDescription,
onClick = onClick,
trailing = {
ManagerCheckbox(
isChecked = isChecked,
onCheckedChange = { onClick() }
)
}
)
}
@Composable
fun CheckboxPreference(
preferenceTitle: String,
preferenceDescription: String? = null,
preference: ManagerPreference<Boolean>,
onCheckedChange: (isChecked: Boolean) -> Unit = {}
) {
val isChecked by preference
val coroutineScope = rememberCoroutineScope()
val onClick: () -> Unit = {
coroutineScope.launch {
preference.save(!isChecked)
onCheckedChange(isChecked)
}
}
Preference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
onClick = onClick,
trailing = {
ManagerCheckbox(
isChecked = isChecked,
onCheckedChange = { onClick() }
)
}
)
}

View file

@ -0,0 +1,93 @@
package com.vanced.manager.ui.components.preference
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.vanced.manager.ui.components.dialog.ManagerDialog
@Composable
fun DialogPreference(
@StringRes preferenceTitleId: Int,
@StringRes preferenceDescriptionId: Int? = null,
trailing: @Composable () -> Unit = {},
buttons: @Composable ColumnScope.(isShown: MutableState<Boolean>) -> Unit,
content: @Composable ColumnScope.() -> Unit
) {
val isShown = remember { mutableStateOf(false) }
Preference(
preferenceTitleId = preferenceTitleId,
preferenceDescriptionId = preferenceDescriptionId,
trailing = trailing
) {
isShown.value = true
}
if (isShown.value) {
ManagerDialog(
titleId = preferenceTitleId,
onDismissRequest = {
isShown.value = false
},
buttons = { buttons(isShown) },
content = content
)
}
}
@Composable
fun DialogPreference(
@StringRes preferenceTitleId: Int,
preferenceDescription: String? = null,
trailing: @Composable () -> Unit = {},
buttons: @Composable ColumnScope.(isShown: MutableState<Boolean>) -> Unit,
content: @Composable ColumnScope.() -> Unit
) {
val isShown = remember { mutableStateOf(false) }
Preference(
preferenceTitleId = preferenceTitleId,
preferenceDescription = preferenceDescription,
trailing = trailing
) {
isShown.value = true
}
if (isShown.value) {
ManagerDialog(
titleId = preferenceTitleId,
onDismissRequest = {
isShown.value = false
},
buttons = { buttons(isShown) },
content = content
)
}
}
@Composable
fun DialogPreference(
preferenceTitle: String,
preferenceDescription: String? = null,
trailing: @Composable () -> Unit = {},
buttons: @Composable ColumnScope.(isShown: MutableState<Boolean>) -> Unit,
content: @Composable ColumnScope.() -> Unit
) {
val isShown = remember { mutableStateOf(false) }
Preference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing
) {
isShown.value = true
}
if (isShown.value) {
ManagerDialog(
title = preferenceTitle,
onDismissRequest = {
isShown.value = false
},
buttons = { buttons(isShown) },
content = content
)
}
}

View file

@ -0,0 +1,98 @@
package com.vanced.manager.ui.components.preference
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.material.LocalContentColor
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.components.color.managerAnimatedColor
import com.vanced.manager.ui.components.list.ManagerListItem
import com.vanced.manager.ui.components.text.ManagerText
import com.vanced.manager.ui.utils.defaultContentPaddingHorizontal
@Composable
fun Preference(
@StringRes preferenceTitleId: Int,
@StringRes preferenceDescriptionId: Int? = null,
trailing: @Composable () -> Unit = {},
onClick: () -> Unit
) {
Preference(
preferenceTitle = { ManagerText(stringId = preferenceTitleId) },
preferenceDescription = if (preferenceDescriptionId != null) {{
ManagerText(stringId = preferenceDescriptionId)
}} else null,
trailing = trailing,
onClick = onClick
)
}
@Composable
fun Preference(
@StringRes preferenceTitleId: Int,
preferenceDescription: String? = null,
trailing: @Composable () -> Unit = {},
onClick: () -> Unit
) {
Preference(
preferenceTitle = { ManagerText(stringId = preferenceTitleId) },
preferenceDescription = if (preferenceDescription != null) {{
ManagerText(text = preferenceDescription)
}} else null,
trailing = trailing,
onClick = onClick
)
}
@Composable
fun Preference(
preferenceTitle: String,
preferenceDescription: String? = null,
trailing: @Composable () -> Unit = {},
onClick: () -> Unit
) {
Preference(
preferenceTitle = { ManagerText(text = preferenceTitle) },
preferenceDescription = if (preferenceDescription != null) {{
ManagerText(text = preferenceDescription)
}} else null,
trailing = trailing,
onClick = onClick
)
}
@Composable
fun Preference(
preferenceTitle: @Composable () -> Unit,
preferenceDescription: @Composable (() -> Unit)? = null,
trailing: @Composable () -> Unit = {},
onClick: () -> Unit
) {
val color = managerAnimatedColor(color = MaterialTheme.colors.onSurface)
ManagerListItem(
modifier = Modifier
.clickable(onClick = onClick)
.padding(horizontal = defaultContentPaddingHorizontal),
title = {
CompositionLocalProvider(
LocalContentColor provides color,
LocalTextStyle provides MaterialTheme.typography.h6
) {
preferenceTitle()
}
},
description = if (preferenceDescription != null) {{
CompositionLocalProvider(
LocalContentColor provides color,
LocalTextStyle provides MaterialTheme.typography.subtitle1
) {
preferenceDescription()
}
}} else null,
trailing = trailing,
)
}

View file

@ -0,0 +1,66 @@
package com.vanced.manager.ui.components.preference
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import com.vanced.manager.R
import com.vanced.manager.ui.components.*
import com.vanced.manager.ui.components.button.ManagerThemedButton
import com.vanced.manager.ui.components.button.ManagerThemedTextButton
import com.vanced.manager.ui.components.list.CheckboxItem
import com.vanced.manager.ui.preferences.*
import kotlinx.coroutines.launch
@Composable
fun CheckboxDialogPreference(
@StringRes preferenceTitle: Int,
@StringRes preferenceDescription: Int? = null,
preference: ManagerPreference<Set<String>>,
trailing: @Composable () -> Unit = {},
buttons: List<CheckboxPreference>,
onSave: (checkedButtons: List<String>) -> Unit = {}
) {
val selectedButtons = remember { preference.value.value.toMutableStateList() }
val coroutineScope = rememberCoroutineScope()
DialogPreference(
preferenceTitleId = preferenceTitle,
preferenceDescriptionId = preferenceDescription,
trailing = trailing,
buttons = { isShown ->
ManagerThemedTextButton(
stringId = R.string.dialog_button_save,
modifier = Modifier.fillMaxWidth(),
onClick = {
coroutineScope.launch {
preference.save(selectedButtons.toSet())
onSave(selectedButtons)
isShown.value = false
}
}
)
}
) {
LazyColumn(
modifier = Modifier
) {
items(buttons) { button ->
val (title, key) = button
CheckboxItem(
text = title,
isChecked = selectedButtons.contains(key),
onCheck = { isChecked ->
if (isChecked) {
selectedButtons.add(key)
} else {
selectedButtons.remove(key)
}
}
)
}
}
}
}

View file

@ -0,0 +1,63 @@
package com.vanced.manager.ui.components.preference
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.R
import com.vanced.manager.ui.components.button.ManagerThemedTextButton
import com.vanced.manager.ui.components.list.RadiobuttonItem
import com.vanced.manager.ui.preferences.ManagerPreference
import com.vanced.manager.ui.preferences.RadioButtonPreference
import kotlinx.coroutines.launch
@Composable
fun RadiobuttonDialogPreference(
@StringRes preferenceTitle: Int,
preference: ManagerPreference<String>,
trailing: @Composable () -> Unit = {},
buttons: List<RadioButtonPreference>,
onSave: (newPref: String?) -> Unit = {}
) {
val coroutineScope = rememberCoroutineScope()
var currentSelection by remember { mutableStateOf(preference.value.value) }
DialogPreference(
preferenceTitleId = preferenceTitle,
preferenceDescription = currentSelection,
trailing = trailing,
buttons = { isShown ->
ManagerThemedTextButton(
stringId = R.string.dialog_button_save,
modifier = Modifier.fillMaxWidth(),
onClick = {
coroutineScope.launch {
preference.save(currentSelection)
onSave(currentSelection)
isShown.value = false
}
}
)
}
) {
LazyColumn(
modifier = Modifier.heightIn(max = 400.dp)
) {
items(buttons) { button ->
val (title, key) = button
RadiobuttonItem(
text = title,
tag = key,
selected = currentSelection == key,
onSelect = {
currentSelection = it
}
)
}
}
}
}

View file

@ -0,0 +1,40 @@
package com.vanced.manager.ui.components.text
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import com.vanced.manager.ui.components.lifecycle.managerString
@Composable
fun ManagerText(
vararg formatArgs: Any,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
textStyle: TextStyle = LocalTextStyle.current,
stringId: Int?,
) {
ManagerText(
modifier = modifier,
color = color,
textStyle = textStyle,
text = managerString(stringId, *formatArgs),
)
}
@Composable
fun ManagerText(
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
textStyle: TextStyle = LocalTextStyle.current,
text: String,
) {
Text(
modifier = modifier,
text = text,
color = color,
style = textStyle
)
}

View file

@ -1,226 +0,0 @@
package com.vanced.manager.ui.composables
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DeleteForever
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Launch
import androidx.compose.material.icons.outlined.Info
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.glide.rememberGlidePainter
import com.vanced.manager.R
import com.vanced.manager.domain.model.App
import com.vanced.manager.downloader.base.BaseDownloader
import com.vanced.manager.ui.theme.managerAccentColor
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AppCard(
app: App,
installationOptions: List<InstallationOption>? = null
) {
val showDownloadDialog = remember { mutableStateOf(false) }
val showAppInfo = remember { mutableStateOf(false) }
val showInstallationOptions = remember { mutableStateOf(false) }
val icon = rememberGlidePainter(
request = app.iconUrl ?: "",
requestBuilder = {
placeholder(R.drawable.ic_app_icon_placeholder)
},
fadeIn = true
)
ManagerCard {
Column {
AppInfoCard(
appName = app.name,
icon = icon
)
AppActionCard(
appInstalledVersion = app.installedVersion,
appRemoteVersion = app.remoteVersion,
showDownloadDialog = showDownloadDialog,
showAppInfo = showAppInfo,
showInstallationOptions = showInstallationOptions
)
if (installationOptions != null) {
AnimatedVisibility(
modifier = Modifier.fillMaxWidth(),
visible = showInstallationOptions.value
) {
installationOptions.forEach {
it.Content()
}
}
}
}
}
if (app.name != null && app.downloader != null) {
AppDownloadDialog(
app = app.name,
downloader = app.downloader,
showDialog = showDownloadDialog
)
}
if (app.name != null && app.changelog != null) {
ChangeLogDialog(
appName = app.name,
changelog = app.changelog,
icon = icon,
show = showAppInfo
)
}
}
@Composable
fun AppInfoCard(
appName: String?,
icon: Painter
) {
ManagerListItem(
title = appName ?: "",
icon = {
Image(
painter = icon,
contentDescription = "",
modifier = Modifier.size(48.dp, 48.dp),
)
},
topPadding = 8.dp,
bottomPadding = 8.dp
)
}
@Composable
fun AppActionCard(
appRemoteVersion: String?,
appInstalledVersion: String?,
showDownloadDialog: MutableState<Boolean>,
showAppInfo: MutableState<Boolean>,
showInstallationOptions: MutableState<Boolean>
) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colors.managerAccentColor) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 4.dp)
.background(managerAccentColor().copy(alpha = 0.15f)),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.weight(1f)
.wrapContentWidth(Alignment.Start)
) {
Text(
text = "Latest: ${appRemoteVersion ?: "Unavailable"}",
fontSize = 12.sp
)
Text(
text = "Installed: ${appInstalledVersion ?: "Unavailable"}",
fontSize = 12.sp
)
}
Row(
modifier = Modifier
.weight(1f)
.padding(start = 4.dp)
.wrapContentWidth(Alignment.End)
) {
IconButton(icon = Icons.Outlined.Info, contentDescription = "App Info") {
showAppInfo.value = true
}
IconButton(icon = Icons.Default.DeleteForever, contentDescription = "Uninstall") {}
IconButton(icon = Icons.Default.Launch, contentDescription = "Launch") {}
IconButton(icon = Icons.Default.Download, contentDescription = "Install") {
showInstallationOptions.value = true
}
}
}
}
}
@Composable
fun AppDownloadDialog(
app: String,
downloader: BaseDownloader,
showDialog: MutableState<Boolean>
) {
val coroutineScope = rememberCoroutineScope()
val rememberProgress = remember { downloader.downloadProgress }
val rememberFile = remember { downloader.downloadFile }
val rememberInstalling = remember { downloader.installing }
val showProgress = remember { mutableStateOf(false) }
ManagerDialog(title = app, isShown = showDialog, buttons = {
AppDownloadDialogButtons(
showProgress = showProgress,
showDialog = showDialog,
downloader = downloader,
coroutineScope = coroutineScope
)
}) {
AppDownloadDialogProgress(
progress = rememberProgress,
file = rememberFile,
showProgress = showProgress.value,
installing = rememberInstalling
)
}
}
@Composable
fun ChangeLogDialog(
appName: String,
changelog: String,
icon: Painter,
show: MutableState<Boolean>
) {
if (show.value) {
ManagerDialog(
title = "About $appName",
isShown = show,
buttons = {
ManagerThemedButton(
modifier = Modifier.fillMaxWidth(),
onClick = { show.value = false }
) {
Text(text = "Close")
}
}
) {
Image(
painter = icon,
contentDescription = null,
modifier = Modifier
.size(64.dp)
.align(Alignment.CenterHorizontally)
)
HeaderView(
modifier = Modifier.padding(horizontal = 8.dp),
headerName = "Changelog",
headerPadding = 0.dp
) {
Text(
modifier = Modifier.padding(top = 4.dp),
text = changelog,
fontSize = 14.sp
)
}
}
}
}

View file

@ -1,105 +0,0 @@
package com.vanced.manager.ui.composables
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.inset
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import androidx.core.graphics.ColorUtils
//TODO
@Composable
fun HSLColorPicker(
modifier: Modifier = Modifier
) {
val hslColors = floatArrayOf(0f, 1f, 0.5f)
val hueColors = getColors(hslColors, length = 360, index = 0) { it }
val saturationColors = getColors(hslColors, length = 11, index = 1) { it / 10 }
val lightnessColors = getColors(hslColors, length = 11, index = 2) { it / 10 }
val hueCircle by remember { mutableStateOf(Offset(0f, 0f)) }
val saturationCircle by remember { mutableStateOf(Offset(0f, 0f)) }
val lightnessCircle by remember { mutableStateOf(Offset(0f, 0f)) }
Canvas(modifier = modifier
.size(250.dp, 250.dp)
.pointerInput(Unit) {
detectDragGestures { change: PointerInputChange, dragAmount: Offset ->
val (changeX, changeY) = change.position
}
}
) {
colorArc(
brush = Brush.sweepGradient(hueColors),
startAngle = 0f,
sweepAngle = 360f,
circleOffset = hueCircle
)
inset(
inset = 60f
) {
colorArc(
brush = Brush.linearGradient(
colors = saturationColors,
start = Offset.Infinite,
end = Offset.Zero
),
startAngle = 100f,
sweepAngle = 155f,
circleOffset = saturationCircle
)
colorArc(
brush = Brush.linearGradient(lightnessColors),
startAngle = 280f,
sweepAngle = 155f,
circleOffset = lightnessCircle
)
}
}
}
fun DrawScope.colorArc(
brush: Brush,
startAngle: Float,
sweepAngle: Float,
circleOffset: Offset
) {
drawArc(
brush = brush,
startAngle = startAngle,
sweepAngle = sweepAngle,
useCenter = false,
style = Stroke(width = 15f)
)
inset(circleOffset.x, circleOffset.y) {
drawCircle(
brush = brush,
radius = 50f
)
}
}
fun getColors(
hslColors: FloatArray,
length: Int,
index: Int,
calculate: (Float) -> Float
): List<Color> {
val colorsCopy = hslColors.copyOf()
return List(length) {
colorsCopy[index] = calculate(it.toFloat())
Color(ColorUtils.HSLToColor(colorsCopy))
}
}

View file

@ -1,2 +0,0 @@
package com.vanced.manager.ui.composables

View file

@ -1,111 +0,0 @@
package com.vanced.manager.ui.composables
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.Icon
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowForwardIos
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.vanced.manager.downloader.base.BaseDownloader
import dev.burnoo.compose.rememberpreference.rememberStringPreference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
data class InstallationOptionKey(
val keyName: String,
val keyDefaultValue: String = "",
val keyInitialValue: String = keyDefaultValue
)
data class InstallationOption(
val title: String,
val descriptionKey: InstallationOptionKey,
val dialog: @Composable (show: MutableState<Boolean>, preference: MutableState<String>) -> Unit
) {
@Composable
fun Content() {
val showDialog = remember { mutableStateOf(false) }
val preference = rememberStringPreference(
keyName = descriptionKey.keyName,
defaultValue = descriptionKey.keyDefaultValue,
initialValue = descriptionKey.keyInitialValue
)
ManagerListItem(
modifier = Modifier.clickable {
showDialog.value = true
},
title = title,
description = preference.value,
trailing = {
Icon(imageVector = Icons.Default.ArrowForwardIos, contentDescription = null)
}
)
if (showDialog.value) {
dialog(showDialog, preference)
}
}
}
@Composable
fun AppDownloadDialogButtons(
showProgress: MutableState<Boolean>,
showDialog: MutableState<Boolean>,
downloader: BaseDownloader,
coroutineScope: CoroutineScope
) {
when(showProgress.value) {
true -> ManagerThemedButton(onClick = {
downloader.cancelDownload()
showDialog.value = false
showProgress.value = false
}) {
Text(text = "Cancel")
}
false -> ManagerThemedButton(onClick = {
coroutineScope.launch {
showProgress.value = true
downloader.download()
}
}) {
Text(text = "Download")
}
}
}
@Composable
fun AppDownloadDialogProgress(
progress: Float,
file: String,
showProgress: Boolean,
installing: Boolean
) {
if (showProgress) {
when (installing) {
true -> LinearProgressIndicator(color = managerAccentColor())
false -> LinearProgressIndicator(
progress = progress,
color = managerAccentColor()
)
}
Row {
Text(
modifier = Modifier
.weight(1f)
.wrapContentWidth(Alignment.Start),
text = "Downloading $file"
)
Text(
modifier = Modifier
.weight(1f)
.wrapContentWidth(Alignment.End),
text = "$progress"
)
}
}
}

View file

@ -1,140 +0,0 @@
package com.vanced.manager.ui.composables
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp
import com.vanced.manager.ui.theme.managerAccentColor
@ExperimentalStdlibApi
@Composable
fun HeaderCard(
headerName: String,
headerPadding: Dp = 11.dp,
content: @Composable ColumnScope.() -> Unit
) {
ManagerCard(
modifier = Modifier
.fillMaxWidth()
) {
Column {
Spacer(modifier = Modifier.size(height = 4.dp, width = 0.dp))
HeaderView(
headerName = headerName,
content = content,
headerPadding = headerPadding
)
}
}
}
@Composable
fun HeaderView(
modifier: Modifier = Modifier,
headerName: String,
headerPadding: Dp = 11.dp,
content: @Composable ColumnScope.() -> Unit
) {
Column(
modifier = modifier
) {
Header(headerName = headerName, headerPadding = headerPadding)
content()
}
}
@OptIn(ExperimentalStdlibApi::class)
@Composable
fun Header(
headerName: String,
headerPadding: Dp = 11.dp,
) {
Text(
headerName.uppercase(),
letterSpacing = 0.15.em,
color = MaterialTheme.colors.managerAccentColor,
modifier = Modifier.padding(horizontal = headerPadding),
fontWeight = FontWeight.Bold,
fontSize = 13.sp
)
}
@Composable
fun IconButton(
icon: ImageVector,
contentDescription: String,
onClick: () -> Unit
) {
val interactionSource = remember { MutableInteractionSource() }
val buttonSize = 36.dp
val enabled = true
Box(
modifier = Modifier
.clickable(
onClick = onClick,
enabled = enabled,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple(bounded = false, radius = 18.dp)
)
.size(buttonSize),
contentAlignment = Alignment.Center
) {
val contentAlpha = if (enabled) LocalContentAlpha.current else ContentAlpha.disabled
CompositionLocalProvider(LocalContentAlpha provides contentAlpha) {
Icon(
imageVector = icon,
contentDescription = contentDescription
)
}
}
}
@Composable
fun RadiobuttonItem(
currentSelection: MutableState<String?>,
text: String,
preferenceValue: String,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
currentSelection.value = preferenceValue
}
.padding(horizontal = 8.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = preferenceValue == currentSelection.value,
onClick = {
currentSelection.value = preferenceValue
},
colors = RadioButtonDefaults.colors(
MaterialTheme.colors.managerAccentColor,
Color.LightGray
)
)
Text(
modifier = Modifier.padding(start = 4.dp),
text = text,
color = managerTextColor(),
fontSize = 18.sp
)
}
}

View file

@ -1,80 +0,0 @@
package com.vanced.manager.ui.composables
import android.net.Uri
import androidx.annotation.DrawableRes
import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.LocalContentColor
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
@Composable
fun LinkCard(
@DrawableRes icon: Int,
title: String,
link: String
) {
val context = LocalContext.current
val customTabs = remember { CustomTabsIntent.Builder().build() }
ManagerCard(
modifier = Modifier
.clip(RoundedCornerShape(12.dp))
.clickable {
customTabs.launchUrl(context, Uri.parse(link))
}
.size(width = 120.dp, height = 100.dp)
) {
CompositionLocalProvider(LocalContentColor provides managerTextColor()) {
Column(
modifier = Modifier.padding(all = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
modifier = Modifier.size(36.dp),
painter = painterResource(id = icon),
contentDescription = null,
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = title,
textAlign = TextAlign.Center
)
}
}
}
}
@Composable
fun <T> ScrollableLinkRow(
items: List<T>,
content: @Composable (T) -> Unit
) {
val state = rememberLazyListState()
LazyRow(
modifier = Modifier.fillMaxWidth(),
state = state
) {
itemsIndexed(items) { index, item ->
content(item)
if (index < items.size - 1) {
Spacer(modifier = Modifier.width(8.dp))
}
}
}
}

View file

@ -1,229 +0,0 @@
package com.vanced.manager.ui.composables
import androidx.annotation.StringRes
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import com.vanced.manager.ui.theme.cardColor
import com.vanced.manager.ui.theme.managerAccentColor
import java.util.*
@Composable
fun managerShape() = RoundedCornerShape(12.dp)
@Composable
fun ManagerCard(
modifier: Modifier = Modifier,
shape: Shape = managerShape(),
content: @Composable () -> Unit,
) {
Card(
modifier = modifier,
shape = shape,
backgroundColor = managerCardColor(),
elevation = 0.dp,
content = content
)
}
@Composable
fun ManagerThemedButton(
modifier: Modifier = Modifier,
onClick: () -> Unit,
content: @Composable RowScope.() -> Unit
) {
val accentColor = managerAccentColor()
Button(
modifier = modifier,
onClick = onClick,
shape = managerShape(),
colors = ButtonDefaults.buttonColors(
backgroundColor = managerAccentColor()
),
elevation = ButtonDefaults.elevation(0.dp)
) {
CompositionLocalProvider(LocalContentColor provides if (accentColor.luminance() > 0.7) Color.Black else Color.White) {
content()
}
}
}
@Composable
fun ManagerDialog(
title: String,
isShown: MutableState<Boolean>,
buttons: @Composable ColumnScope.() -> Unit,
content: @Composable ColumnScope.() -> Unit,
) {
Dialog(
onDismissRequest = { isShown.value = false },
content = {
ManagerCard {
Column(
modifier = Modifier.padding(all = 8.dp)
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = title,
letterSpacing = 2.sp,
fontWeight = FontWeight.Bold,
color = managerTextColor().copy(alpha = 0.8f),
fontSize = 24.sp,
textAlign = TextAlign.Center
)
Spacer(Modifier.height(8.dp))
content()
Spacer(Modifier.height(8.dp))
buttons()
}
}
}
)
}
@Composable
fun ManagerSurface(
content: @Composable () -> Unit
) {
Surface(
modifier = Modifier.fillMaxSize(),
color = managerSurfaceColor(),
content = content
)
}
@Composable
fun ManagerLazyColumn(
content: LazyListScope.() -> Unit
) {
LazyColumn(
contentPadding = PaddingValues(12.dp),
content = content
)
}
@Composable
fun ManagerScrollableColumn(
content: @Composable ColumnScope.() -> Unit
) {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.verticalScroll(scrollState)
.padding(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
content = content
)
}
@Composable
fun ManagerDropdownMenuItem(
isMenuExpanded: MutableState<Boolean>,
title: String,
onClick: () -> Unit
) {
DropdownMenuItem(
onClick = {
isMenuExpanded.value = false
onClick()
},
) {
Text(text = title)
}
}
@Composable
fun animateManagerColor(
color: Color
): State<Color> {
return animateColorAsState(
targetValue = color,
animationSpec = tween(500)
)
}
@Composable
fun HomeHeaderView(
modifier: Modifier = Modifier,
headerName: String,
content: @Composable () -> Unit
) {
HeaderView(
modifier = modifier,
headerName = headerName,
headerPadding = 4.dp
) {
ManagerHomeHeaderSeparator()
content()
}
}
@Composable
fun managerAnimatedColor(
color: Color
): Color {
val animColor by animateColorAsState(
targetValue = color,
animationSpec = tween(500)
)
return animColor
}
@Composable
fun managerAnimatedText(
@StringRes stringId: Int
): String {
var text by remember { mutableStateOf("") }
CompositionLocalProvider(
LocalConfiguration provides LocalConfiguration.current.apply {
setLocale(Locale("ru"))
}
) {
text = stringResource(id = stringId)
}
return text
}
@Composable
fun ManagerCardSeparator() {
Spacer(modifier = Modifier.height(8.dp))
}
@Composable
fun ManagerHomeHeaderSeparator() {
Spacer(modifier = Modifier.height(4.dp))
}
@Composable
fun managerTextColor(): Color = managerAnimatedColor(color = MaterialTheme.colors.onSurface)
@Composable
fun managerSurfaceColor(): Color = managerAnimatedColor(color = MaterialTheme.colors.surface)
@Composable
fun managerCardColor(): Color = managerAnimatedColor(color = MaterialTheme.colors.cardColor)
@Composable
fun managerAccentColor(): Color = MaterialTheme.colors.managerAccentColor
@Composable
fun animatedManagerAccentColor(): Color = managerAnimatedColor(color = MaterialTheme.colors.managerAccentColor)

View file

@ -1,95 +0,0 @@
package com.vanced.manager.ui.composables
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
const val defaultPadding = 12
@Composable
fun ManagerListItem(
modifier: Modifier = Modifier,
title: String,
description: String? = null,
startPadding: Dp = defaultPadding.dp,
endPadding: Dp = defaultPadding.dp,
topPadding: Dp = defaultPadding.dp,
bottomPadding: Dp = defaultPadding.dp,
icon: @Composable (() -> Unit)? = null,
trailing: @Composable (() -> Unit)? = null
) {
val typography = MaterialTheme.typography
val textColor by animateManagerColor(color = MaterialTheme.colors.onBackground)
Box(
modifier = modifier
) {
Row(
modifier = Modifier
.padding(top = topPadding, bottom = bottomPadding)
) {
if (icon != null) {
Box(
modifier = Modifier
.padding(start = startPadding)
.align(Alignment.CenterVertically)
) {
icon()
}
}
Column(
modifier = Modifier
.padding(
start = startPadding,
end = endPadding
)
.weight(1f)
.align(Alignment.CenterVertically)
) {
if (description == null) {
Spacer(modifier = Modifier.size(6.dp))
}
Text(
text = title,
fontSize = 16.sp,
modifier = Modifier.fillMaxWidth(),
style = typography.subtitle1,
color = textColor
)
if (description != null) {
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.medium,
LocalContentColor provides textColor
) {
Text(
text = description,
fontSize = 12.sp,
modifier = Modifier.fillMaxWidth()
)
}
}
if (description == null) {
Spacer(modifier = Modifier.size(6.dp))
}
}
if (trailing != null) {
Box(
modifier = Modifier
.padding(end = endPadding)
.align(Alignment.CenterVertically)
) {
CompositionLocalProvider(LocalContentColor provides textColor) {
trailing()
}
}
}
}
}
}

View file

@ -1,148 +0,0 @@
package com.vanced.manager.ui.composables
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import com.vanced.manager.ui.preferences.RadioButtonPreference
@Composable
fun SwitchPreference(
preferenceTitle: String,
preferenceDescription: String?,
preferenceKey: String,
defaultValue: Boolean = true,
onCheckedChange: (isChecked: Boolean) -> Unit = {}
) {
val prefs = PreferenceManager.getDefaultSharedPreferences(LocalContext.current)
var isChecked by remember { mutableStateOf(prefs.getBoolean(preferenceKey, defaultValue)) }
fun savePreference() {
isChecked = !isChecked
prefs.edit {
putBoolean(preferenceKey, isChecked)
}
onCheckedChange(isChecked)
}
Preference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
onClick = { savePreference() },
trailing = {
Switch(
checked = isChecked,
onCheckedChange = { savePreference() },
colors = SwitchDefaults.colors(
checkedThumbColor = managerAccentColor(),
checkedTrackColor = managerAccentColor(),
uncheckedThumbColor = Color.Gray,
uncheckedTrackColor = Color.Gray
)
)
}
)
}
@Composable
fun Preference(
preferenceTitle: String,
preferenceDescription: String? = null,
trailing: @Composable (() -> Unit)? = null,
onClick: () -> Unit
) {
ManagerListItem(
modifier = Modifier.clickable(onClick = onClick),
title = preferenceTitle,
description = preferenceDescription,
trailing = trailing,
bottomPadding = 4.dp,
topPadding = 4.dp
)
}
@Composable
fun DialogPreference(
preferenceTitle: String,
preferenceDescription: String? = null,
trailing: @Composable (() -> Unit)? = null,
buttons: @Composable ColumnScope.(isShown: MutableState<Boolean>) -> Unit,
content: @Composable ColumnScope.() -> Unit
) {
val isShown = remember { mutableStateOf(false) }
Preference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing
) {
isShown.value = true
}
if (isShown.value) {
ManagerDialog(
title = preferenceTitle,
isShown = isShown,
buttons = { buttons(isShown) },
content = content
)
}
}
@Composable
fun DialogRadioButtonPreference(
preferenceTitle: String,
preferenceKey: String,
defaultValue: String,
preferenceDescription: String? = null,
trailing: @Composable (() -> Unit)? = null,
buttons: List<RadioButtonPreference>,
onSave: (newPref: String?) -> Unit = {}
) {
val prefs = PreferenceManager.getDefaultSharedPreferences(LocalContext.current)
val currentSelection = remember { mutableStateOf(prefs.getString(preferenceKey, defaultValue)) }
DialogPreference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing,
buttons = { isShown ->
ManagerThemedButton(
modifier = Modifier.fillMaxWidth(),
onClick = {
prefs.edit {
putString(preferenceKey, currentSelection.value)
}
onSave(currentSelection.value)
isShown.value = false
}
) {
Text(text = "Save")
}
}
) {
LazyColumn(
modifier = Modifier
.weight(
weight = 1f,
fill = false
)
) {
items(buttons) { button ->
val (title, key) = button
RadiobuttonItem(
currentSelection = currentSelection,
text = title,
preferenceValue = key
)
}
}
}
}

View file

@ -1,193 +1,205 @@
package com.vanced.manager.ui.layouts
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Circle
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vanced.manager.ui.composables.HeaderCard
import com.vanced.manager.ui.composables.ManagerCard
import com.vanced.manager.ui.composables.ManagerLazyColumn
import com.vanced.manager.ui.composables.managerTextColor
import com.vanced.manager.ui.theme.ComposeTestTheme
import com.vanced.manager.ui.theme.vancedBlue
import com.vanced.manager.ui.theme.vancedRed
import androidx.compose.ui.util.fastForEach
import com.vanced.manager.R
import com.vanced.manager.ui.components.card.ManagerLinkCard
import com.vanced.manager.ui.components.card.ManagerThemedCard
import com.vanced.manager.ui.components.layout.ManagerScrollableColumn
import com.vanced.manager.ui.components.layout.ScrollableItemRow
import com.vanced.manager.ui.components.lifecycle.managerString
import com.vanced.manager.ui.components.list.ManagerListItem
import com.vanced.manager.ui.components.text.ManagerText
import com.vanced.manager.ui.widgets.layout.CategoryLayout
data class Person(
val name: String,
val contribution: String
)
data class Credit(
val creditName: String,
val persons: List<String>
@StringRes val nameId: Int,
val persons: List<Person>
)
data class Source(
val sourceLink: String
@StringRes val nameId: Int,
@DrawableRes val iconId: Int,
val link: String
)
private val credits = listOf(
Credit(
creditName = "Vanced Team",
nameId = R.string.about_category_credits_vanced_team,
persons = listOf(
"xfileFIN",
"ZaneZam",
"Laura Almeida",
"KevinX8"
Person(
name = "xfileFIN",
contribution = "Mods, Theming, Support"
),
Person(
name = "Laura",
contribution = "Theming, Support"
),
Person(
name = "ZaneZam",
contribution = "Publishing, Support"
),
Person(
name = "KevinX8",
contribution = "Overlord, Support"
)
)
),
Credit(
creditName = "Manager Team",
nameId = R.string.about_category_credits_manager_devs,
persons = listOf(
"Xinto",
"Koopah",
"Logan"
Person(
name = "Xinto",
contribution = "Manager Core"
),
Person(
name = "Koopah",
contribution = "Root installer"
),
Person(
name = "Logan",
contribution = "UI"
),
Person(
name = "HaliksaR",
contribution = "Refactoring, UI"
),
)
),
Credit(
creditName = "Other Contributors",
nameId = R.string.about_category_credits_other,
persons = listOf(
"bhatVikrant",
"bawm",
"AioiLight",
"HaliksaR"
Person(
name = "bhatVikrant",
contribution = "Website"
),
Person(
name = "bawm",
contribution = "Sponsorblock"
),
Person(
name = "cane",
contribution = "Sponsorblock"
),
)
)
)
private val sources = listOf(
Source(
""
nameId = R.string.about_sources_source_code,
iconId = R.drawable.ic_github,
link = "https://github.com/YTVanced/VancedManager"
),
Source(
""
nameId = R.string.about_sources_license,
iconId = R.drawable.ic_round_assignment_24,
link = "https://raw.githubusercontent.com/YTVanced/VancedManager/dev/LICENSE"
)
)
@ExperimentalStdlibApi
@Composable
@Preview(
showSystemUi = true,
uiMode = UI_MODE_NIGHT_YES
)
fun AboutDarkMode() {
ComposeTestTheme {
AboutLayout()
}
}
@ExperimentalStdlibApi
@Composable
@Preview(
showSystemUi = true
)
fun AboutLightMode() {
MaterialTheme {
AboutLayout()
}
}
@ExperimentalStdlibApi
@Composable
fun AboutLayout() {
ManagerLazyColumn {
item {
ManagerCard {
Column(
modifier = Modifier
.background(
Brush.horizontalGradient(
colors = listOf(
vancedBlue,
vancedRed
),
)
)
) {
Spacer(modifier = Modifier.size(width = 0.dp, height = 8.dp))
Text(
text = "Vanced Manager",
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
fontSize = 30.sp,
color = Color.White
)
Text(
text = buildAnnotatedString {
append("Re")
withStyle(style = SpanStyle(Color(0xFFBBB529))) {
append("@Compose")
ManagerScrollableColumn(
itemSpacing = 12.dp
) {
AboutManagerCard()
credits.fastForEach { credit ->
CategoryLayout(
categoryNameId = credit.nameId,
categoryNameSpacing = 4.dp
) {
Column {
credit.persons.fastForEach { person ->
ManagerListItem(
title = {
ManagerText(
text = person.name,
textStyle = MaterialTheme.typography.h6
)
},
description = {
ManagerText(
text = person.contribution,
textStyle = MaterialTheme.typography.subtitle1
)
}
append("d")
},
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
fontSize = 16.sp,
color = Color.White
)
Spacer(modifier = Modifier.size(width = 0.dp, height = 8.dp))
)
}
}
}
}
items(credits) { credit ->
Spacer(modifier = Modifier.size(width = 0.dp, height = 12.dp))
CreditsCard(creditName = credit.creditName, persons = credit.persons)
}
item {
Spacer(modifier = Modifier.size(width = 0.dp, height = 12.dp))
HeaderCard(
headerName = "Sources"
) {
Row(modifier = Modifier.fillMaxWidth()) {
sources.forEach { _ ->
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Circle, contentDescription = null, modifier = Modifier.size(36.dp))
}
}
}
Spacer(modifier = Modifier.size(8.dp))
CategoryLayout(categoryNameId = R.string.about_category_sources) {
ScrollableItemRow(items = sources) { source ->
ManagerLinkCard(
title = managerString(source.nameId),
icon = source.iconId,
link = source.link
)
}
}
}
}
@Composable
@ExperimentalStdlibApi
fun CreditsCard(
creditName: String,
persons: List<String>
) {
HeaderCard(
headerName = creditName
) {
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.medium,
LocalContentColor provides managerTextColor()
fun AboutManagerCard() {
ManagerThemedCard {
Column(
modifier = Modifier
// .clip(managerShape())
// .background(
// Brush.horizontalGradient(
// colors = listOf(
// vancedBlue,
// vancedRed
// )
// )
// )
) {
Text(
text = persons.joinToString("\n"),
text = "Vanced Manager",
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp),
fontSize = 13.sp
.padding(top = 8.dp),
fontSize = 30.sp,
color = Color.White
)
Text(
text = buildAnnotatedString {
append("Re")
withStyle(style = SpanStyle(Color(0xFFBBB529))) {
append("@Compose")
}
append("d")
},
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp),
fontSize = 16.sp,
color = Color.White
)
}
Spacer(modifier = Modifier.size(width = 0.dp, height = 2.dp))
}
}

View file

@ -1,28 +1,31 @@
package com.vanced.manager.ui.layouts
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import androidx.compose.ui.util.fastForEach
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.SwipeRefreshIndicator
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.vanced.manager.R
import com.vanced.manager.domain.model.App
import com.vanced.manager.ui.composables.*
import com.vanced.manager.ui.components.*
import com.vanced.manager.ui.components.card.ManagerLinkCard
import com.vanced.manager.ui.components.color.managerAccentColor
import com.vanced.manager.ui.components.layout.ManagerScrollableColumn
import com.vanced.manager.ui.components.layout.ScrollableItemRow
import com.vanced.manager.ui.utils.defaultContentPaddingVertical
import com.vanced.manager.ui.viewmodel.HomeViewModel
import com.vanced.manager.ui.widgets.home.apps.card.AppCard
import com.vanced.manager.ui.widgets.layout.CategoryLayout
import com.vanced.manager.util.socialMedia
import com.vanced.manager.util.sponsors
import org.koin.androidx.compose.getViewModel
@ExperimentalAnimationApi
@Composable
@Preview
fun HomeLayout() {
@ -41,39 +44,38 @@ fun HomeLayout() {
)
}
) {
ManagerScrollableColumn {
HomeHeaderView(headerName = "Apps") {
viewModel.apps.fastForEachIndexed { index, app ->
val rememberedApp by app.observeAsState(initial = App())
AppCard(rememberedApp)
if (index != viewModel.apps.size - 1) {
Spacer(modifier = Modifier.size(height = 8.dp, width = 0.dp))
ManagerScrollableColumn(
contentPaddingVertical = defaultContentPaddingVertical,
itemSpacing = 18.dp
) {
CategoryLayout(
categoryNameId = R.string.home_category_apps,
contentPaddingHorizontal = 0.dp
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
viewModel.apps.fastForEach { app ->
val observedApp by app.observeAsState(initial = App())
AppCard(observedApp, isFetching)
}
}
}
ManagerCardSeparator()
HomeHeaderView(
modifier = Modifier.fillMaxWidth(),
headerName = "Support Us"
) {
ScrollableLinkRow(items = sponsors) { sponsor ->
LinkCard(
icon = R.drawable.ic_android_black_24dp,
CategoryLayout(categoryNameId = R.string.home_category_support_us) {
ScrollableItemRow(items = sponsors) { sponsor ->
ManagerLinkCard(
icon = sponsor.icon,
title = sponsor.title,
link = "https://m.youtube.com"
link = sponsor.link
)
}
}
ManagerCardSeparator()
HomeHeaderView(
modifier = Modifier.fillMaxWidth(),
headerName = "Social media"
) {
ScrollableLinkRow(items = socialMedia) { socialMedia ->
LinkCard(
icon = R.drawable.ic_android_black_24dp,
CategoryLayout(categoryNameId = R.string.home_category_social_media) {
ScrollableItemRow(items = socialMedia) { socialMedia ->
ManagerLinkCard(
icon = socialMedia.icon,
title = socialMedia.title,
link = "https://m.youtube.com"
link = socialMedia.link
)
}
}

View file

@ -10,9 +10,9 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Share
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.composables.ManagerLazyColumn
import com.vanced.manager.ui.composables.managerAccentColor
import com.vanced.manager.ui.composables.managerSurfaceColor
import com.vanced.manager.ui.components.color.managerAccentColor
import com.vanced.manager.ui.components.color.managerSurfaceColor
import com.vanced.manager.ui.components.layout.ManagerLazyColumn
import com.vanced.manager.util.logs
@Composable

View file

@ -1,13 +1,23 @@
package com.vanced.manager.ui.layouts
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.ui.composables.*
import androidx.datastore.preferences.core.booleanPreferencesKey
import com.vanced.manager.R
import com.vanced.manager.ui.components.*
import com.vanced.manager.ui.components.layout.ManagerScrollableColumn
import com.vanced.manager.ui.components.preference.CheckboxPreference
import com.vanced.manager.ui.components.preference.Preference
import com.vanced.manager.ui.components.preference.RadiobuttonDialogPreference
import com.vanced.manager.ui.preferences.RadioButtonPreference
import com.vanced.manager.ui.theme.managerTheme
import com.vanced.manager.ui.preferences.holder.managerVariantPref
import com.vanced.manager.ui.preferences.holder.useCustomTabsPref
import com.vanced.manager.ui.preferences.managerBooleanPreference
import com.vanced.manager.ui.utils.defaultContentPaddingVertical
import com.vanced.manager.ui.widgets.layout.CategoryLayout
import com.vanced.manager.ui.widgets.settings.SettingsAccentColorItem
import com.vanced.manager.ui.widgets.settings.ThemeSettingsItem
data class NotificationPrefModel(
val app: String,
@ -29,89 +39,66 @@ private val notificationApps = arrayOf(
)
)
@ExperimentalStdlibApi
@ExperimentalMaterialApi
@Composable
fun SettingsLayout() {
var showDialog by remember { mutableStateOf(false) }
ManagerScrollableColumn {
HeaderCard(headerName = "Behaviour") {
SwitchPreference(
preferenceTitle = "Use Custom Tabs",
preferenceDescription = "Links will open in chrome custom tabs",
preferenceKey = "use_custom_tabs"
)
notificationApps.forEach {
with (it) {
SwitchPreference(
preferenceTitle = "$app Push Notifications",
preferenceDescription = "Receive push notifications when an update for $app is released",
preferenceKey = "${prefKey}_notifications"
)
ManagerScrollableColumn(
contentPaddingVertical = defaultContentPaddingVertical,
itemSpacing = 12.dp
) {
CategoryLayout(
categoryNameId = R.string.settings_category_behaviour,
contentPaddingHorizontal = 0.dp,
categoryNameSpacing = 4.dp
) {
Column {
CheckboxPreference(
preferenceTitle = R.string.settings_preference_use_custom_tabs_title,
preferenceDescription = R.string.settings_preference_use_custom_tabs_summary,
preference = useCustomTabsPref
)
notificationApps.forEach {
with(it) {
CheckboxPreference(
preferenceTitle = "$app Push Notifications",
preferenceDescription = "Receive push notifications when an update for $app is released",
preference = managerBooleanPreference(
key = booleanPreferencesKey("${prefKey}_notifications"),
defaultValue = true
)
)
}
}
}
Preference(
preferenceTitle = "Variant",
preferenceDescription = "nonroot",
onClick = {}
)
Preference(
preferenceTitle = "Clear downloaded files",
onClick = {}
)
}
Spacer(modifier = Modifier.size(12.dp))
HeaderCard(headerName = "Appearance") {
Preference(
preferenceTitle = "Accent Color",
onClick = {
showDialog = true
}
)
DialogRadioButtonPreference(
preferenceTitle = "Theme",
preferenceKey = "manager_theme",
defaultValue = "Light",
buttons = listOf(
RadioButtonPreference(
title = "Light Theme",
preferenceValue = "Light"
),
RadioButtonPreference(
title = "Dark Theme",
preferenceValue = "Dark"
),
RadioButtonPreference(
title = "System Default",
preferenceValue = "System Default"
RadiobuttonDialogPreference(
preferenceTitle = R.string.settings_preference_variant_title,
preference = managerVariantPref,
buttons = listOf(
RadioButtonPreference(
title = "nonroot",
key = "nonroot"
),
RadioButtonPreference(
title = "root",
key = "root"
),
)
)
) {
managerTheme = it
Preference(
preferenceTitleId = R.string.settings_preference_clear_files_title,
preferenceDescriptionId = null,
onClick = {}
)
}
}
CategoryLayout(
categoryNameId = R.string.settings_category_appearance,
contentPaddingHorizontal = 0.dp,
categoryNameSpacing = 4.dp
) {
Column {
SettingsAccentColorItem()
ThemeSettingsItem()
}
}
}
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
text = {
HSLColorPicker()
// AndroidView(
// factory = {
// HSLColorPicker(it)
// },
// update = { view ->
// view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 800)
// view.setColorSelectionListener(object : SimpleColorSelectionListener() {
// override fun onColorSelected(color: Int) {
// accentColorInt = color
// }
// })
// }
// )
},
buttons = {}
)
}
}

View file

@ -0,0 +1,6 @@
package com.vanced.manager.ui.preferences
data class CheckboxPreference(
val title: String,
val key: String,
)

View file

@ -0,0 +1,69 @@
package com.vanced.manager.ui.preferences
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import kotlin.reflect.KProperty
class ManagerPreference<T>(
val key: Preferences.Key<T>,
val defaultValue: T,
) : KoinComponent {
private val dataStore: DataStore<Preferences> by inject()
private val _value = mutableStateOf(defaultValue)
val value: State<T> = _value
@Composable
fun rememberValue() = remember { value }
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value.value
suspend fun save(newValue: T) {
_value.value = newValue
dataStore.edit {
it[key] = value.value
}
}
//It's Chewsday innit - © Bri'ish ppl
init {
dataStore.data.map {
_value.value = it[key] ?: defaultValue
}
}
}
fun managerStringPreference(
key: Preferences.Key<String>,
defaultValue: String = ""
) = ManagerPreference(key, defaultValue)
fun managerStringSetPreference(
key: Preferences.Key<Set<String>>,
defaultValue: Set<String> = setOf()
) = ManagerPreference(key, defaultValue)
fun managerBooleanPreference(
key: Preferences.Key<Boolean>,
defaultValue: Boolean = false
) = ManagerPreference(key, defaultValue)
fun managerIntPreference(
key: Preferences.Key<Int>,
defaultValue: Int = 0
) = ManagerPreference(key, defaultValue)
fun managerLongPreference(
key: Preferences.Key<Long>,
defaultValue: Long = 0
) = ManagerPreference(key, defaultValue)

View file

@ -2,5 +2,5 @@ package com.vanced.manager.ui.preferences
data class RadioButtonPreference(
val title: String,
val preferenceValue: String
val key: String
)

View file

@ -0,0 +1,6 @@
package com.vanced.manager.ui.preferences.holder
const val MANAGER_VARIANT_DEFAULT_VALUE = "nonroot"
const val VANCED_ENABLED_DEFAULT_VALUE = true
const val MUSIC_ENABLED_DEFAULT_VALUE = true

View file

@ -0,0 +1,23 @@
package com.vanced.manager.ui.preferences.holder
import com.vanced.manager.ui.preferences.managerBooleanPreference
import com.vanced.manager.ui.preferences.managerLongPreference
import com.vanced.manager.ui.preferences.managerStringPreference
import com.vanced.manager.ui.preferences.managerStringSetPreference
import com.vanced.manager.ui.theme.defAccentColor
val useCustomTabsPref = managerBooleanPreference(useCustomTabsKey)
val managerVariantPref = managerStringPreference(managerVariantKey, MANAGER_VARIANT_DEFAULT_VALUE)
val managerThemePref = managerStringPreference(managerThemeKey, "System Default")
val managerAccentColorPref = managerLongPreference(managerAccentColorKey, defAccentColor)
val vancedThemePref = managerStringPreference(vancedThemeKey, "Dark")
val vancedVersionPref = managerStringPreference(vancedVersionKey, "latest")
val vancedLanguagesPref = managerStringSetPreference(vancedLanguageKey, setOf("en"))
val musicVersionPref = managerStringPreference(musicVersionKey, "latest")
val vancedEnabled = managerBooleanPreference(vancedEnabledKey, VANCED_ENABLED_DEFAULT_VALUE)
val musicEnabled = managerBooleanPreference(musicEnabledKey, MUSIC_ENABLED_DEFAULT_VALUE)

View file

@ -0,0 +1,19 @@
package com.vanced.manager.ui.preferences.holder
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
val useCustomTabsKey = booleanPreferencesKey(USE_CUSTOM_TABS_KEY)
val managerVariantKey = stringPreferencesKey(MANAGER_VARIANT_KEY)
val managerThemeKey = stringPreferencesKey("manager_theme")
val managerAccentColorKey = longPreferencesKey("manager_accent_color")
val vancedThemeKey = stringPreferencesKey(APP_VANCED_THEME_KEY)
val vancedVersionKey = stringPreferencesKey(APP_VANCED_VERSION_KEY)
val vancedLanguageKey = stringSetPreferencesKey(APP_VANCED_LANGUAGE_KEY)
val musicVersionKey = stringPreferencesKey(APP_MUSIC_VERSION_KEY)
val vancedEnabledKey = booleanPreferencesKey(VANCED_ENABLED_KEY)
val musicEnabledKey = booleanPreferencesKey(MUSIC_ENABLED_KEY)

View file

@ -0,0 +1,13 @@
package com.vanced.manager.ui.preferences.holder
const val USE_CUSTOM_TABS_KEY = "use_custom_tabs"
const val MANAGER_VARIANT_KEY = "manager_variant"
const val APP_VANCED_THEME_KEY = "app_vanced_theme"
const val APP_VANCED_VERSION_KEY = "app_vanced_version"
const val APP_VANCED_LANGUAGE_KEY = "app_vanced_language"
const val APP_MUSIC_VERSION_KEY = "app_music_version"
const val VANCED_ENABLED_KEY = "manager_vanced_enabled"
const val MUSIC_ENABLED_KEY = "manager_music_enabled"

View file

@ -1,8 +1,48 @@
package com.vanced.manager.ui.screens
sealed class Screen(val route: String, val displayName: String) {
object Home : Screen("home", "Manager")
object Settings : Screen("settings", "Settings")
object About : Screen("about", "About")
object Logs : Screen("logs", "Logs")
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import com.vanced.manager.R
import com.vanced.manager.ui.layouts.AboutLayout
import com.vanced.manager.ui.layouts.HomeLayout
import com.vanced.manager.ui.layouts.LogLayout
import com.vanced.manager.ui.layouts.SettingsLayout
sealed class Screen(
val route: String,
@StringRes val displayName: Int,
val content: @Composable () -> Unit
) {
object Home : Screen(
route = "home",
displayName = R.string.toolbar_home,
content = {
HomeLayout()
}
)
object Settings : Screen(
route = "settings",
displayName = R.string.toolbar_settings,
content = {
SettingsLayout()
}
)
object About : Screen(
route = "about",
displayName = R.string.toolbar_about,
content = {
AboutLayout()
}
)
object Logs : Screen(
route = "logs",
displayName = R.string.toolbar_logs,
content = {
LogLayout()
}
)
}

View file

@ -5,7 +5,7 @@ import androidx.compose.material.Shapes
import androidx.compose.ui.unit.dp
val shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(12.dp),
large = RoundedCornerShape(8.dp)
)

View file

@ -6,12 +6,11 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import com.vanced.manager.ui.preferences.holder.managerAccentColorPref
import com.vanced.manager.ui.preferences.holder.managerThemePref
var managerTheme by mutableStateOf<String?>("")
const val defAccentColor = 0xFF0477E1
private val DarkColorPalette = darkColors(
primary = purple200,
@ -28,10 +27,10 @@ private val LightColorPalette = lightColors(
val Colors.cardColor: Color get() = if (isLight) Color(0xFFF7F7F7) else Color(0xFF191919)
val Colors.managerAccentColor: Color get() = Color(0xFF2E73FF)
val Colors.managerAccentColor: Color get() = Color(managerAccentColorPref.value.value)
@Composable
fun isDark(): Boolean = when (managerTheme) {
fun isDark(): Boolean = when (managerThemePref.value.value) {
"Dark" -> true
"Light" -> false
"System Default" -> isSystemInDarkTheme()
@ -39,7 +38,7 @@ fun isDark(): Boolean = when (managerTheme) {
}
@Composable
fun ComposeTestTheme(
fun ManagerTheme(
content: @Composable () -> Unit
) {
val colors = if (isDark()) DarkColorPalette else LightColorPalette

View file

@ -2,27 +2,53 @@ package com.vanced.manager.ui.theme
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.vanced.manager.R
private val light = Font(R.font.inter_light, FontWeight.Light)
private val regular = Font(R.font.inter_regular, FontWeight.Normal)
private val medium = Font(R.font.inter_medium, FontWeight.Medium)
private val semibold = Font(R.font.inter_semibold, FontWeight.SemiBold)
private val bold = Font(R.font.inter_bold, FontWeight.Bold)
private val interFontFamily = FontFamily(light, regular, medium, semibold, bold)
// Set of Material typography styles to start with
val typography = Typography(
body1 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
/* Other default text styles to override
button = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W500,
fontSize = 14.sp
defaultFontFamily = interFontFamily,
h1 = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
),
caption = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
h2 = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold
),
h5 = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
),
h6 = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Bold
),
body1 = TextStyle(
fontWeight = FontWeight.Medium,
fontSize = 12.sp
),
body2 = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 10.sp,
),
subtitle1 = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 12.sp,
),
button = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Medium
)
*/
)

View file

@ -0,0 +1,6 @@
package com.vanced.manager.ui.utils
import androidx.compose.ui.unit.dp
val defaultContentPaddingHorizontal = 16.dp
val defaultContentPaddingVertical = 12.dp

View file

@ -7,9 +7,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
import com.vanced.manager.domain.model.App
import com.vanced.manager.repository.JsonRepository
import com.vanced.manager.ui.preferences.holder.managerVariantPref
import com.vanced.manager.ui.preferences.holder.musicEnabled
import com.vanced.manager.ui.preferences.holder.vancedEnabled
import kotlinx.coroutines.launch
class HomeViewModel(
@ -45,26 +47,10 @@ class HomeViewModel(
}
init {
val prefs = getDefaultSharedPreferences(context)
val variant = prefs.getString("manager_variant", "nonroot")
val vancedEnabled = prefs.getBoolean("manager_vanced_enabled", true)
val musicEnabled = prefs.getBoolean("manager_music_enabled", true)
apps.apply {
if (vancedEnabled) {
add(vanced)
}
if (musicEnabled) {
add(music)
}
if (variant == "nonroot") {
add(microg)
}
if (vancedEnabled.value.value) add(vanced)
if (musicEnabled.value.value) add(music)
if (managerVariantPref.value.value == "nonroot") add(microg)
}
fetch()

View file

@ -0,0 +1,72 @@
package com.vanced.manager.ui.widgets.home.apps.card
import androidx.compose.foundation.layout.*
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.runtime.Composable
import androidx.compose.runtime.MutableState
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.ui.components.button.IconButton
import com.vanced.manager.ui.components.color.ThemedCardContentColorProvider
import com.vanced.manager.ui.components.text.ManagerText
import com.vanced.manager.ui.utils.defaultContentPaddingHorizontal
import com.vanced.manager.ui.widgets.text.AppVersionText
@Composable
fun AppActionCard(
appRemoteVersion: String?,
appInstalledVersion: String?,
showDownloadDialog: MutableState<Boolean>,
showAppInfo: MutableState<Boolean>,
showInstallationOptions: MutableState<Boolean>,
hasInstallationOptions: Boolean
) {
ThemedCardContentColorProvider {
Row(
modifier = Modifier.padding(horizontal = defaultContentPaddingHorizontal, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.weight(1f)
.wrapContentWidth(Alignment.Start),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
ManagerText(stringId = R.string.app_versions)
AppVersionText(
stringId = R.string.app_version_latest,
version = appRemoteVersion
)
AppVersionText(
stringId = R.string.app_version_installed,
version = appInstalledVersion
)
}
Row(
modifier = Modifier
.weight(1f)
.padding(start = 4.dp)
.wrapContentWidth(Alignment.End)
) {
IconButton(icon = Icons.Outlined.Info, contentDescription = "App Info") {
showAppInfo.value = true
}
IconButton(icon = Icons.Rounded.DeleteForever, contentDescription = "Uninstall") {}
IconButton(icon = Icons.Rounded.Launch, contentDescription = "Launch") {}
IconButton(icon = Icons.Rounded.Download, contentDescription = "Install") {
if (hasInstallationOptions) {
showInstallationOptions.value = true
} else {
showDownloadDialog.value = true
}
}
}
}
}
}

View file

@ -0,0 +1,81 @@
package com.vanced.manager.ui.widgets.home.apps.card
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.google.accompanist.glide.rememberGlidePainter
import com.vanced.manager.domain.model.App
import com.vanced.manager.ui.components.button.ManagerThemedButton
import com.vanced.manager.ui.components.card.ManagerThemedCard
import com.vanced.manager.ui.widgets.home.apps.dialog.AppChangelogDialog
import com.vanced.manager.ui.widgets.home.apps.dialog.AppDownloadDialog
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AppCard(
app: App,
fetching: Boolean
) {
val showDownloadDialog = remember { mutableStateOf(false) }
val showAppInfo = remember { mutableStateOf(false) }
val showInstallationOptions = remember { mutableStateOf(false) }
val icon = rememberGlidePainter(
request = app.iconUrl ?: "",
fadeIn = true
)
val hasInstallationOption = app.installationOptions != null
ManagerThemedCard {
Column {
AppInfoCard(
appName = app.name ?: "",
icon = icon,
fetching = fetching
)
AppActionCard(
appInstalledVersion = app.installedVersion,
appRemoteVersion = app.remoteVersion,
showDownloadDialog = showDownloadDialog,
showAppInfo = showAppInfo,
showInstallationOptions = showInstallationOptions,
hasInstallationOptions = hasInstallationOption
)
if (hasInstallationOption) {
AnimatedVisibility(
modifier = Modifier.fillMaxWidth(),
visible = showInstallationOptions.value
) {
app.installationOptions?.forEach {
it.item()
}
ManagerThemedButton(onClick = {
showDownloadDialog.value = true
}) {
}
}
}
}
}
if (app.name != null && app.downloader != null) {
AppDownloadDialog(
app = app.name,
downloader = app.downloader,
showDialog = showDownloadDialog
)
}
if (app.name != null && app.changelog != null) {
AppChangelogDialog(
appName = app.name,
changelog = app.changelog,
showDialog = showAppInfo
)
}
}

View file

@ -0,0 +1,48 @@
package com.vanced.manager.ui.widgets.home.apps.card
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.google.accompanist.imageloading.ImageLoadState
import com.google.accompanist.imageloading.LoadPainter
import com.vanced.manager.ui.components.card.ManagerCard
import com.vanced.manager.ui.components.list.ManagerListItem
import com.vanced.manager.ui.components.placeholder.managerPlaceholder
import com.vanced.manager.ui.components.text.ManagerText
import com.vanced.manager.ui.utils.defaultContentPaddingHorizontal
@Composable
fun AppInfoCard(
appName: String,
icon: LoadPainter<Any>,
fetching: Boolean
) {
ManagerCard {
ManagerListItem(
modifier = Modifier.padding(
horizontal = defaultContentPaddingHorizontal,
vertical = 12.dp
),
title = {
ManagerText(
modifier = Modifier.managerPlaceholder(fetching),
text = appName,
textStyle = MaterialTheme.typography.h5
)
},
icon = {
Image(
painter = icon,
contentDescription = "",
modifier = Modifier
.size(48.dp, 48.dp)
.managerPlaceholder(icon.loadState is ImageLoadState.Loading)
)
}
)
}
}

View file

@ -0,0 +1,42 @@
package com.vanced.manager.ui.widgets.home.apps.dialog
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vanced.manager.R
import com.vanced.manager.ui.components.button.ManagerThemedTextButton
import com.vanced.manager.ui.components.dialog.ManagerDialog
import com.vanced.manager.ui.components.lifecycle.managerString
import com.vanced.manager.ui.components.text.ManagerText
@Composable
fun AppChangelogDialog(
appName: String,
changelog: String,
showDialog: MutableState<Boolean>
) {
if (showDialog.value) {
ManagerDialog(
title = managerString(R.string.app_info_title, appName),
onDismissRequest = { showDialog.value = false },
buttons = {
ManagerThemedTextButton(
modifier = Modifier.fillMaxWidth(),
stringId = R.string.dialog_button_close
) {
showDialog.value = false
}
}
) {
ManagerText(
modifier = Modifier.padding(top = 4.dp),
text = changelog,
textStyle = MaterialTheme.typography.subtitle1
)
}
}
}

View file

@ -0,0 +1,53 @@
package com.vanced.manager.ui.widgets.home.apps.dialog
import androidx.compose.runtime.*
import com.vanced.manager.downloader.base.BaseDownloader
import com.vanced.manager.ui.components.dialog.ManagerDialog
import com.vanced.manager.ui.widgets.home.download.AppDownloadDialogButtons
import com.vanced.manager.ui.widgets.home.download.AppDownloadDialogProgress
import kotlinx.coroutines.launch
@Composable
fun AppDownloadDialog(
app: String,
downloader: BaseDownloader,
showDialog: MutableState<Boolean>
) {
val coroutineScope = rememberCoroutineScope()
val rememberProgress = remember { downloader.downloadProgress }
val rememberFile = remember { downloader.downloadFile }
val rememberInstalling = remember { downloader.installing }
val showProgress = remember { mutableStateOf(false) }
if (showDialog.value) {
ManagerDialog(
title = app,
onDismissRequest = { showDialog.value = false },
buttons = {
AppDownloadDialogButtons(
showProgress = showProgress,
onDownloadClick = {
coroutineScope.launch {
showProgress.value = true
downloader.download()
}
},
onCancelClick = {
downloader.cancelDownload()
showDialog.value = false
showProgress.value = false
}
)
}
) {
AppDownloadDialogProgress(
progress = rememberProgress,
file = rememberFile,
showProgress = showProgress.value,
installing = rememberInstalling
)
}
}
}

View file

@ -0,0 +1,28 @@
package com.vanced.manager.ui.widgets.home.download
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Modifier
import com.vanced.manager.R
import com.vanced.manager.ui.components.button.ManagerThemedTextButton
@Composable
fun AppDownloadDialogButtons(
showProgress: MutableState<Boolean>,
onDownloadClick: () -> Unit,
onCancelClick: () -> Unit,
) {
when (showProgress.value) {
true -> ManagerThemedTextButton(
stringId = R.string.dialog_button_cancel,
modifier = Modifier.fillMaxWidth(),
onClick = onCancelClick
)
false -> ManagerThemedTextButton(
stringId = R.string.app_download_dialog_confirm,
modifier = Modifier.fillMaxWidth(),
onClick = onDownloadClick
)
}
}

View file

@ -0,0 +1,42 @@
package com.vanced.manager.ui.widgets.home.download
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.components.color.managerAccentColor
@Composable
fun AppDownloadDialogProgress(
progress: Float,
file: String,
showProgress: Boolean,
installing: Boolean
) {
if (showProgress) {
when (installing) {
true -> LinearProgressIndicator(color = managerAccentColor())
false -> LinearProgressIndicator(
progress = progress,
color = managerAccentColor()
)
}
Row {
Text(
modifier = Modifier
.weight(1f)
.wrapContentWidth(Alignment.Start),
text = "Downloading $file"
)
Text(
modifier = Modifier
.weight(1f)
.wrapContentWidth(Alignment.End),
text = "$progress"
)
}
}
}

View file

@ -0,0 +1,20 @@
package com.vanced.manager.ui.widgets.home.installation
import androidx.annotation.StringRes
import com.vanced.manager.ui.components.preference.CheckboxDialogPreference
import com.vanced.manager.ui.preferences.CheckboxPreference
import com.vanced.manager.ui.preferences.ManagerPreference
data class CheckboxInstallationOption(
@StringRes val title: Int,
val preference: ManagerPreference<Set<String>>,
val buttons: List<CheckboxPreference>
) : InstallationOption(
item = {
CheckboxDialogPreference(
preferenceTitle = title,
preference = preference,
buttons = buttons
)
}
)

View file

@ -0,0 +1,7 @@
package com.vanced.manager.ui.widgets.home.installation
import androidx.compose.runtime.Composable
open class InstallationOption(
val item: @Composable () -> Unit
)

View file

@ -0,0 +1,20 @@
package com.vanced.manager.ui.widgets.home.installation
import androidx.annotation.StringRes
import com.vanced.manager.ui.components.preference.RadiobuttonDialogPreference
import com.vanced.manager.ui.preferences.ManagerPreference
import com.vanced.manager.ui.preferences.RadioButtonPreference
data class RadiobuttonInstallationOption(
@StringRes val title: Int,
val preference: ManagerPreference<String>,
val buttons: List<RadioButtonPreference>
) : InstallationOption(
item = {
RadiobuttonDialogPreference(
preferenceTitle = title,
preference = preference,
buttons = buttons
)
}
)

View file

@ -0,0 +1,30 @@
package com.vanced.manager.ui.widgets.layout
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import com.vanced.manager.ui.utils.defaultContentPaddingHorizontal
import com.vanced.manager.ui.utils.defaultContentPaddingVertical
import com.vanced.manager.ui.widgets.text.CategoryTitleText
@Composable
fun CategoryLayout(
@StringRes categoryNameId: Int,
contentPaddingHorizontal: Dp = defaultContentPaddingHorizontal,
categoryNameSpacing: Dp = defaultContentPaddingVertical,
content: @Composable () -> Unit,
) {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(categoryNameSpacing),
) {
CategoryTitleText(stringId = categoryNameId)
Box(
modifier = Modifier.padding(horizontal = contentPaddingHorizontal)
) {
content()
}
}
}

View file

@ -0,0 +1,23 @@
package com.vanced.manager.ui.widgets.settings
import androidx.compose.runtime.*
import com.vanced.manager.R
import com.vanced.manager.ui.components.color.ManagerColorPicker
import com.vanced.manager.ui.components.preference.DialogPreference
import com.vanced.manager.ui.preferences.holder.managerAccentColorPref
@Composable
fun SettingsAccentColorItem() {
var localAccentColor by remember { mutableStateOf(managerAccentColorPref.value.value) }
DialogPreference(
preferenceTitleId = R.string.settings_preference_accent_color_title,
preferenceDescription = localAccentColor.toString(),
buttons = {
}
) {
ManagerColorPicker {
localAccentColor = it
}
}
}

View file

@ -0,0 +1,29 @@
package com.vanced.manager.ui.widgets.settings
import androidx.compose.runtime.Composable
import com.vanced.manager.R
import com.vanced.manager.ui.components.preference.RadiobuttonDialogPreference
import com.vanced.manager.ui.preferences.RadioButtonPreference
import com.vanced.manager.ui.preferences.holder.managerThemePref
@Composable
fun ThemeSettingsItem() {
RadiobuttonDialogPreference(
preferenceTitle = R.string.settings_preference_theme_title,
preference = managerThemePref,
buttons = listOf(
RadioButtonPreference(
title = "Light Theme",
key = "Light"
),
RadioButtonPreference(
title = "Dark Theme",
key = "Dark"
),
RadioButtonPreference(
title = "System Default",
key = "System Default"
)
)
)
}

View file

@ -0,0 +1,20 @@
package com.vanced.manager.ui.widgets.text
import androidx.annotation.StringRes
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import com.vanced.manager.R
import com.vanced.manager.ui.components.lifecycle.managerString
import com.vanced.manager.ui.components.text.ManagerText
@Composable
fun AppVersionText(
@StringRes stringId: Int,
version: String?
) {
ManagerText(
version ?: managerString(stringId = R.string.app_content_unavailable),
stringId = stringId,
textStyle = MaterialTheme.typography.body2,
)
}

View file

@ -0,0 +1,20 @@
package com.vanced.manager.ui.widgets.text
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.vanced.manager.ui.components.text.ManagerText
import com.vanced.manager.ui.utils.defaultContentPaddingHorizontal
@Composable
fun CategoryTitleText(
@StringRes stringId: Int
) {
ManagerText(
modifier = Modifier.padding(start = defaultContentPaddingHorizontal),
stringId = stringId,
textStyle = MaterialTheme.typography.h2
)
}

View file

@ -0,0 +1,15 @@
package com.vanced.manager.ui.widgets.text
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import com.vanced.manager.ui.components.text.ManagerText
@Composable
fun ToolbarTitleText(
stringId: Int?
) {
ManagerText(
stringId = stringId,
textStyle = MaterialTheme.typography.h1
)
}

View file

@ -1,16 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="33.408dp"
android:height="34.294dp"
android:viewportWidth="33.408"
android:viewportHeight="34.294">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:strokeWidth="1"
android:pathData="M16.7,0.5A36.932,36.932 0,0 0,0.5 4.292c0,5.689 -0.057,19.767 16.2,29.42 16.261,-9.653 16.2,-23.789 16.2,-29.42A36.932,36.932 0,0 0,16.7 0.5Z"
android:fillColor="#00000000"
android:strokeColor="?colorLinkImage"
android:fillType="evenOdd"/>
<path
android:pathData="M16.13,22.678l9.826,-13.216a1.223,1.223 0,0 0,-1.724 0.172h0l-8.159,8.5 -3.1,-3.735c-1.494,-1.724 -3.448,-0.4 -3.907,-0.057L16.13,22.678"
android:fillColor="?colorLinkImage"
android:fillType="evenOdd"/>
android:fillColor="#FF000000"
android:pathData="M11.7,2.1c3.6,0.1 6.7,0.7 9.7,2.1c0.3,0.1 0.4,0.3 0.4,0.6c0,3.2 -0.5,6.3 -1.9,9.2c-1.7,3.4 -4.3,6 -7.5,7.9c-0.1,0.1 -0.3,0.1 -0.4,0c-4.8,-3 -8.1,-7.1 -9.1,-12.7c-0.3,-1.5 -0.2,-3 -0.4,-4.5c0,-0.3 0.1,-0.4 0.3,-0.5c2,-1 4.2,-1.6 6.4,-1.9C10.1,2.2 11.1,2.1 11.7,2.1zM7.6,10.5c1.4,1.7 2.8,3.3 4.2,5c2,-2.7 3.9,-5.3 5.8,-7.9c-0.5,-0.3 -0.7,-0.2 -1.2,0.2c-1.5,1.6 -3,3.1 -4.4,4.7c-0.1,0.1 -0.2,0.2 -0.3,0.3c-0.6,-0.7 -1.1,-1.4 -1.7,-2.1C9.3,9.8 8.4,9.8 7.6,10.5z"/>
</vector>

View file

@ -1,14 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="37.319dp"
android:height="43.642dp"
android:viewportWidth="37.319"
android:viewportHeight="43.642">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:strokeWidth="1"
android:pathData="M36.796,14.32l-1.329,-3.587 0.93,-2.059a0.707,0.707 0,0 0,-0.133 -0.8L33.807,5.352a4.063,4.063 0,0 0,-4.185 -1l-0.664,0.266L25.171,0.502l-6.51,0.066L12.216,0.635 8.43,4.754l-0.664,-0.266a3.933,3.933 0,0 0,-4.185 1L1.055,8.009A0.637,0.637 0,0 0,0.924 8.602l0.93,2.126L0.524,14.32l4.717,17.8a7.7,7.7 0,0 0,2.857 4.185l9.3,6.311a2.1,2.1 0,0 0,2.591 0l9.3,-6.311a7.162,7.162 0,0 0,2.857 -4.185l3.853,-14.615Z"
android:fillColor="#00000000"
android:strokeColor="?colorLinkImage"/>
<path
android:pathData="M19.723,26.942a3.239,3.239 0,0 0,-0.731 -0.266h-0.465a2.38,2.38 0,0 0,-0.731 0.266l-1.129,0.465 -1.329,0.6L13.209,29.135a0.441,0.441 0,0 0,-0.266 0.332,0.473 0.473,0 0,0 0.2,0.4l1.86,1.262c0.4,0.266 0.8,0.6 1.129,0.864l0.531,0.465 1.063,0.93 0.465,0.465a0.807,0.807 0,0 0,1.063 0l2.126,-1.86 1.129,-0.864 1.86,-1.329a0.418,0.418 0,0 0,0.2 -0.531c-0.066,-0.133 -0.133,-0.2 -0.266,-0.2L22.181,28.004l-1.329,-0.6ZM32.744,15.117l0.066,-0.2a2.491,2.491 0,0 0,-0.066 -0.731,7.572 7.572,0 0,0 -0.664,-1.262l-1.2,-1.727 -0.864,-1.129 -1.594,-1.993a3.8,3.8 0,0 0,-0.465 -0.531h0l-0.731,0.133L23.709,8.341a5.815,5.815 0,0 1,-1.063 -0.133l-1.927,-0.6 -1.4,-0.531a3.612,3.612 0,0 0,-1.2 0l-1.4,0.4 -1.927,0.6a1.894,1.894 0,0 1,-1.063 0.133l-3.521,-0.664 -0.731,-0.133h0a3.327,3.327 0,0 0,-0.465 0.531l-1.594,1.993c-0.266,0.4 -0.6,0.731 -0.864,1.129l-1.2,1.86 -0.531,0.93A2.926,2.926 0,0 0,4.709 14.918l0.066,0.2a0.507,0.507 0,0 0,0.133 0.332l0.93,1.063 4.185,4.451a1.288,1.288 0,0 1,0.2 1.2l-0.731,1.661a1.831,1.831 0,0 0,0 1.329l0.133,0.4a3.193,3.193 0,0 0,1.129 1.528l0.664,0.531a1.116,1.116 0,0 0,1.2 0.133l2.325,-1.129a6.239,6.239 0,0 0,1.2 -0.8l1.86,-1.661a0.82,0.82 0,0 0,0.266 -0.531,0.754 0.754,0 0,0 -0.2,-0.531l-4.185,-2.857a0.833,0.833 0,0 1,-0.266 -1.063l1.661,-3.056a1.5,1.5 0,0 0,0.066 -1.2,1.959 1.959,0 0,0 -0.93,-0.864l-5.115,-1.927c-0.4,-0.133 -0.332,-0.332 0.066,-0.332l2.989,-0.332a4.974,4.974 0,0 1,1.4 0.133l2.591,0.731a0.777,0.777 0,0 1,0.531 0.864l-1,5.58a4.386,4.386 0,0 0,-0.066 0.93,1.252 1.252,0 0,0 0.8,0.4l1.594,0.332a4.908,4.908 0,0 0,1.4 0l1.462,-0.332a1.252,1.252 0,0 0,0.8 -0.4,2.133 2.133,0 0,0 -0.066,-0.93L20.719,13.124a0.762,0.762 0,0 1,0.531 -0.864l2.591,-0.731a4.974,4.974 0,0 1,1.4 -0.133l2.989,0.266c0.4,0.066 0.4,0.2 0.066,0.332l-5.115,1.927a1.687,1.687 0,0 0,-0.93 0.864,1.487 1.487,0 0,0 0.066,1.2l1.661,3.056A0.851,0.851 0,0 1,23.709 20.104l-4.185,2.857a0.807,0.807 0,0 0,0 1.063h0l1.86,1.661a4.7,4.7 0,0 0,1.2 0.8L24.909,27.604a1.271,1.271 0,0 0,1.2 -0.133l0.664,-0.531a3.685,3.685 0,0 0,1.129 -1.528l0.066,-0.266a2.261,2.261 0,0 0,0 -1.329l-0.664,-1.594a1.288,1.288 0,0 1,0.2 -1.2l4.185,-4.451 0.93,-1.063a0.712,0.712 0,0 1,0.133 -0.4Z"
android:fillColor="?colorLinkImage"/>
android:fillColor="#FF000000"
android:pathData="M5.4,2.7c0.3,0 0.6,0.1 1,0.1c0,0 0.1,0 0.1,0c0.6,-0.7 1.2,-1.4 1.8,-2.1c0.1,-0.1 0.2,-0.1 0.3,-0.1c2.2,0 4.4,0 6.7,0c0.3,0 0.4,0.1 0.6,0.3c0.4,0.4 0.7,0.8 1.1,1.3c0.2,0.2 0.4,0.5 0.6,0.7c0,0 0.1,0.1 0.2,0c0.5,-0.1 1,-0.2 1.5,-0.1c0.4,0.1 0.8,0.2 1.2,0.5c0.3,0.4 0.7,0.7 1,1c0.1,0.1 0.2,0.2 0.4,0.4c0.1,0.1 0.1,0.1 0,0.2c-0.2,0.4 -0.3,0.7 -0.5,1.1c0,0.1 0,0.2 0,0.3c0.2,0.6 0.4,1.1 0.6,1.7c0,0.1 0,0.2 0,0.3c-0.1,0.4 -0.2,0.9 -0.3,1.3c-0.5,1.8 -0.9,3.5 -1.4,5.3c-0.3,1 -0.5,2 -0.8,3c-0.1,0.5 -0.3,0.9 -0.6,1.3c-0.3,0.3 -0.6,0.7 -1,0.9c-0.6,0.4 -1.2,0.8 -1.8,1.2c-0.5,0.3 -1,0.7 -1.5,1c-0.5,0.3 -1,0.7 -1.5,1c-0.2,0.1 -0.4,0.2 -0.6,0.3c-0.2,0.1 -0.5,0.1 -0.7,0c-0.2,-0.1 -0.4,-0.2 -0.5,-0.3c-0.8,-0.6 -1.7,-1.1 -2.5,-1.7c-0.5,-0.3 -1,-0.6 -1.4,-1c-0.4,-0.3 -0.8,-0.6 -1.2,-0.8c-0.7,-0.5 -1.1,-1.3 -1.4,-2.2c-0.1,-0.4 -0.2,-0.8 -0.3,-1.2c-0.3,-1.2 -0.6,-2.3 -1,-3.5c-0.2,-0.7 -0.4,-1.4 -0.5,-2.1C2.6,9.9 2.4,9 2.1,8.2c0,-0.1 0,-0.2 0,-0.3c0.2,-0.6 0.4,-1.1 0.6,-1.6c0,-0.1 0,-0.2 0,-0.3C2.6,5.6 2.4,5.2 2.3,4.9c0,-0.1 0,-0.1 0,-0.2c0.5,-0.5 1,-1 1.5,-1.5c0.3,-0.3 0.6,-0.3 1,-0.4C5,2.7 5.1,2.7 5.4,2.7zM6.7,6.7C6.7,6.7 6.7,6.7 6.7,6.7C6.8,6.7 6.9,6.6 7,6.6c0.3,0 0.5,-0.1 0.8,-0.1c0.4,0 0.9,-0.1 1.3,0c0.5,0.1 1.1,0.3 1.6,0.4C11,7.1 11.1,7.2 11,7.5c-0.1,0.7 -0.3,1.4 -0.4,2.1c-0.1,0.4 -0.1,0.7 -0.2,1.1c0,0.2 0.1,0.3 0.3,0.4c0.3,0.1 0.6,0.1 1,0.2c0.2,0 0.4,0 0.5,0c0.4,-0.1 0.8,-0.1 1.1,-0.2c0.3,-0.1 0.3,-0.2 0.3,-0.4c-0.1,-0.5 -0.2,-1 -0.2,-1.5c-0.1,-0.6 -0.2,-1.2 -0.3,-1.7c0,-0.3 0,-0.4 0.3,-0.5c0.2,-0.1 0.4,-0.1 0.6,-0.2c0.6,-0.2 1.1,-0.3 1.7,-0.3c0.5,0 0.9,0.1 1.4,0.1c0.1,0 0.2,0 0.2,0.1c0,0 0,0 0,0c0,0 -0.1,0 -0.1,0.1c-1,0.4 -1.9,0.7 -2.9,1.1C14,8 13.8,8.4 13.9,8.7c0.1,0.4 0.3,0.7 0.4,1c0.2,0.3 0.4,0.6 0.5,0.9c0.1,0.3 0.1,0.4 -0.2,0.6c-0.4,0.3 -0.8,0.5 -1.2,0.8c-0.3,0.2 -0.7,0.4 -1,0.7c-0.2,0.2 -0.2,0.3 0,0.5c0.2,0.2 0.4,0.4 0.6,0.6c0.7,0.6 1.5,1 2.3,1.4c0.3,0.1 0.5,0.1 0.7,-0.1c0.3,-0.2 0.6,-0.5 0.8,-0.8c0.2,-0.4 0.3,-0.8 0.2,-1.2c-0.1,-0.3 -0.2,-0.5 -0.3,-0.8c-0.1,-0.3 -0.1,-0.6 0.2,-0.9c0.1,-0.1 0.3,-0.2 0.4,-0.4c0.7,-0.7 1.3,-1.4 2,-2.1c0.4,-0.4 0.5,-0.9 0.2,-1.4c-0.3,-0.5 -0.6,-0.9 -1,-1.4c-0.4,-0.6 -0.8,-1.1 -1.3,-1.7c-0.1,-0.1 -0.1,-0.2 -0.3,-0.2c-0.6,0.1 -1.2,0.2 -1.8,0.3c-0.4,0.1 -0.8,0.1 -1.2,0c-0.3,-0.1 -0.6,-0.2 -0.9,-0.3c-0.6,-0.2 -1.2,-0.3 -1.8,-0.1c-0.4,0.1 -0.7,0.2 -1.1,0.4C9.9,4.6 9.6,4.7 9.3,4.7C8.6,4.6 7.9,4.4 7.1,4.3c-0.1,0 -0.2,0 -0.3,0.1C6.4,4.9 6,5.4 5.6,5.9C5.2,6.5 4.9,7.1 4.5,7.6c-0.3,0.5 -0.1,1 0.2,1.3c0.5,0.6 1.1,1.1 1.6,1.7c0.3,0.3 0.6,0.6 0.8,0.9c0.2,0.2 0.3,0.5 0.2,0.8c0,0.1 -0.1,0.2 -0.1,0.3C7.1,12.8 7,13 7,13.3c-0.1,0.3 0,0.7 0.2,1c0.2,0.3 0.4,0.6 0.7,0.8c0.3,0.2 0.6,0.3 1,0.1c0.4,-0.2 0.7,-0.3 1.1,-0.5c0.7,-0.3 1.2,-0.8 1.7,-1.4c0.2,-0.3 0.2,-0.4 0,-0.6c0,0 -0.1,-0.1 -0.1,-0.1c-0.3,-0.2 -0.6,-0.4 -0.9,-0.6c-0.4,-0.3 -0.8,-0.5 -1.2,-0.8c-0.3,-0.2 -0.3,-0.3 -0.2,-0.7c0.1,-0.3 0.3,-0.6 0.5,-0.9c0.2,-0.3 0.3,-0.6 0.4,-0.9c0.1,-0.2 0.1,-0.4 -0.1,-0.6C9.9,8 9.8,7.9 9.7,7.9C8.8,7.6 8,7.2 7.1,6.9C7,6.9 6.8,6.8 6.7,6.7zM12.1,14.8c-0.1,0 -0.1,0 -0.1,0c-0.2,0.1 -0.5,0.1 -0.7,0.2c-0.7,0.3 -1.4,0.7 -2.2,1.1c-0.2,0.1 -0.2,0.3 0,0.4c0,0 0.1,0.1 0.1,0.1c0.5,0.4 1,0.7 1.5,1.1c0.4,0.3 0.8,0.6 1.2,0.9c0.3,0.2 0.4,0.2 0.6,0c0.2,-0.2 0.4,-0.3 0.6,-0.5c0.7,-0.5 1.4,-1.1 2.1,-1.6c0.3,-0.2 0.2,-0.3 0,-0.5c-0.3,-0.2 -0.6,-0.3 -0.9,-0.5c-0.5,-0.2 -1,-0.5 -1.6,-0.7C12.4,14.9 12.2,14.9 12.1,14.8z"/>
</vector>

View file

@ -1,18 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="38dp"
android:height="38dp"
android:viewportWidth="35"
android:viewportHeight="35">
<path
android:fillColor="#FF000000"
android:pathData="M27.1,1.1H6.7c-2.3,0 -4,1.8 -4,4v20.7c0,2.3 1.8,4 4,4h18.5L25,29l1.3,1.2l4.9,4.2V5.2C31.2,2.9 29.4,1.1 27.1,1.1zM30.2,32.6l-3.3,-2.9L25.1,28l-2,-1.8l0.8,2.8H6.6c-1.7,0 -3.1,-1.4 -3.1,-3.1V5.2c0,-1.7 1.4,-3.1 3.1,-3.1H27c1.7,0 3.1,1.4 3.1,3.1L30.2,32.6L30.2,32.6z"/>
<path
android:fillColor="#FF000000"
android:pathData="M24.5,10.4l-0.1,-0.2l-0.2,-0.1c-2.3,-1.7 -4.4,-1.8 -4.9,-1.8h-0.4L18.3,9c-0.5,-0.1 -0.9,-0.1 -1.4,-0.1S16,8.9 15.5,9l-0.3,-0.4l-0.3,-0.4h-0.4c-0.4,0 -2.5,0.1 -4.8,1.8l-0.3,0.3C9.3,10.5 7,14.5 7,19.6V20l0.1,0.2c0.1,0.1 1.6,2.6 5.5,2.8h0.5l0.3,-0.4c0,0 0.6,-0.7 1,-1.3c0.1,0 1.9,0.3 2.6,0.3s2.3,-0.2 2.4,-0.3c0.5,0.6 1,1.2 1,1.2l0.3,0.4h0.5c3.9,-0.1 5.4,-2.6 5.5,-2.8l0.1,-0.2v-0.3C26.9,14.6 24.6,10.5 24.5,10.4zM21.2,22c0,0 -0.6,-0.7 -1,-1.2c1.9,-0.6 2.6,-1.7 2.7,-1.8c-0.7,0.4 -1.2,0.7 -1.8,0.9c-0.8,0.3 -1.5,0.6 -2.2,0.7c-1.5,0.3 -2.8,0.2 -4,0c-0.8,-0.2 -1.6,-0.4 -2.3,-0.7c-0.2,0 -1.6,-0.8 -1.6,-0.9c0.1,0.1 0.8,1.2 2.6,1.8c-0.5,0.6 -1,1.2 -1,1.2c-3.4,-0.1 -4.7,-2.4 -4.7,-2.4c0,-4.9 2.2,-8.9 2.2,-8.9c2.2,-1.7 4.3,-1.6 4.3,-1.6l0.2,0.2L15,9.9c0,0 2.3,-0.2 3.4,0c0.1,0 0.4,0.1 0.5,0.1l0.4,-0.5l0.2,-0.3c0,0 2.1,0 4.3,1.6c0,0 2.2,4 2.2,8.9C25.9,19.7 24.6,21.8 21.2,22z"/>
<path
android:fillColor="#FF000000"
android:pathData="M14.1,16.6m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
<path
android:fillColor="#FF000000"
android:pathData="M19.7,16.6m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
<vector
android:height="18.591549dp"
android:viewportHeight="55"
android:viewportWidth="71"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#FF000000"
android:pathData="M60.1045,4.8978C55.5792,2.8214 50.7265,1.2916 45.6527,0.4154C45.5603,0.3985 45.468,0.4408 45.4204,0.5253C44.7963,1.6353 44.105,3.0834 43.6209,4.2216C38.1637,3.4046 32.7345,3.4046 27.3892,4.2216C26.905,3.0581 26.1886,1.6353 25.5617,0.5253C25.5141,0.4436 25.4218,0.4013 25.3294,0.4154C20.2584,1.2888 15.4057,2.8186 10.8776,4.8978C10.8384,4.9147 10.8048,4.9429 10.7825,4.9795C1.578,18.7309 -0.9436,32.1443 0.2934,45.3914C0.299,45.4562 0.3354,45.5182 0.3858,45.5576C6.4587,50.0174 12.3413,52.7249 18.1147,54.5195C18.2071,54.5477 18.305,54.5139 18.3638,54.4378C19.7295,52.5728 20.9469,50.6063 21.9907,48.5383C22.0523,48.4172 21.9935,48.2735 21.8676,48.2256C19.9366,47.4931 18.0979,46.6 16.3292,45.5858C16.1893,45.5041 16.1781,45.304 16.3068,45.2082C16.679,44.9293 17.0513,44.6391 17.4067,44.3461C17.471,44.2926 17.5606,44.2813 17.6362,44.3151C29.2558,49.6202 41.8354,49.6202 53.3179,44.3151C53.3935,44.2785 53.4831,44.2898 53.5502,44.3433C53.9057,44.6363 54.2779,44.9293 54.6529,45.2082C54.7816,45.304 54.7732,45.5041 54.6333,45.5858C52.8646,46.6197 51.0259,47.4931 49.0921,48.2228C48.9662,48.2707 48.9102,48.4172 48.9718,48.5383C50.038,50.6034 51.2554,52.5699 52.5959,54.435C52.6519,54.5139 52.7526,54.5477 52.845,54.5195C58.6464,52.7249 64.529,50.0174 70.6019,45.5576C70.6551,45.5182 70.6887,45.459 70.6943,45.3942C72.1747,30.0791 68.2147,16.7757 60.1968,4.9823C60.1772,4.9429 60.1437,4.9147 60.1045,4.8978ZM23.7259,37.3253C20.2276,37.3253 17.3451,34.1136 17.3451,30.1693C17.3451,26.225 20.1717,23.0133 23.7259,23.0133C27.308,23.0133 30.1626,26.2532 30.1066,30.1693C30.1066,34.1136 27.28,37.3253 23.7259,37.3253ZM47.3178,37.3253C43.8196,37.3253 40.9371,34.1136 40.9371,30.1693C40.9371,26.225 43.7636,23.0133 47.3178,23.0133C50.9,23.0133 53.7545,26.2532 53.6986,30.1693C53.6986,34.1136 50.9,37.3253 47.3178,37.3253Z"/>
</vector>

View file

@ -1,6 +1,9 @@
<vector android:height="36.508373dp" android:viewportHeight="35.5"
android:viewportWidth="36.367" android:width="37.4dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M18.183,0.5a17.687,17.687 0,0 0,-5.59 34.466c0.879,0.163 1.2,-0.38 1.2,-0.858 0,-0.423 -0.011,-1.531 -0.022,-3.007 -4.917,1.064 -5.96,-2.366 -5.96,-2.366a4.67,4.67 0,0 0,-1.965 -2.584c-1.607,-1.1 0.119,-1.075 0.119,-1.075a3.758,3.758 0,0 1,2.714 1.824,3.758 3.758,0 0,0 5.145,1.465 3.843,3.843 0,0 1,1.118 -2.366c-3.93,-0.445 -8.055,-1.965 -8.055,-8.739a6.8,6.8 0,0 1,1.824 -4.744,6.321 6.321,0 0,1 0.174,-4.679s1.487,-0.478 4.863,1.813a16.727,16.727 0,0 1,8.858 0c3.376,-2.29 4.863,-1.813 4.863,-1.813a6.321,6.321 0,0 1,0.174 4.679,6.836 6.836,0 0,1 1.813,4.744c0,6.8 -4.136,8.283 -8.076,8.728a4.243,4.243 0,0 1,1.2 3.278c0,2.366 -0.022,4.266 -0.022,4.852 0,0.478 0.315,1.02 1.216,0.847a17.689,17.689 0,0 0,-5.6 -34.466Z"
android:strokeColor="#000000" android:strokeWidth="1"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,0c-6.626,0 -12,5.373 -12,12 0,5.302 3.438,9.8 8.207,11.387 0.599,0.111 0.793,-0.261 0.793,-0.577v-2.234c-3.338,0.726 -4.033,-1.416 -4.033,-1.416 -0.546,-1.387 -1.333,-1.756 -1.333,-1.756 -1.089,-0.745 0.083,-0.729 0.083,-0.729 1.205,0.084 1.839,1.237 1.839,1.237 1.07,1.834 2.807,1.304 3.492,0.997 0.107,-0.775 0.418,-1.305 0.762,-1.604 -2.665,-0.305 -5.467,-1.334 -5.467,-5.931 0,-1.311 0.469,-2.381 1.236,-3.221 -0.124,-0.303 -0.535,-1.524 0.117,-3.176 0,0 1.008,-0.322 3.301,1.23 0.957,-0.266 1.983,-0.399 3.003,-0.404 1.02,0.005 2.047,0.138 3.006,0.404 2.291,-1.552 3.297,-1.23 3.297,-1.23 0.653,1.653 0.242,2.874 0.118,3.176 0.77,0.84 1.235,1.911 1.235,3.221 0,4.609 -2.807,5.624 -5.479,5.921 0.43,0.372 0.823,1.102 0.823,2.222v3.293c0,0.319 0.192,0.694 0.801,0.576 4.765,-1.589 8.199,-6.086 8.199,-11.386 0,-6.627 -5.373,-12 -12,-12z"/>
</vector>

View file

@ -1,15 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="33.543dp"
android:height="33.543dp"
android:viewportWidth="33.543"
android:viewportHeight="33.543">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M24.522,1.086a7.945,7.945 0,0 1,7.935 7.935v15.5a7.945,7.945 0,0 1,-7.935 7.935h-15.5a7.945,7.945 0,0 1,-7.935 -7.935v-15.5a7.945,7.945 0,0 1,7.935 -7.935h15.5m0,-1.086h-15.5a9.02,9.02 0,0 0,-9.021 9.021v15.5a9.02,9.02 0,0 0,9.021 9.021h15.5a9.02,9.02 0,0 0,9.021 -9.021v-15.5a9.02,9.02 0,0 0,-9.021 -9.021Z"
android:fillColor="#000000"/>
<path
android:pathData="M16.674,9.282a7.686,7.686 0,1 1,-7.686 7.686,7.7 7.7,0 0,1 7.686,-7.686m0,-1.086a8.771,8.771 0,1 0,8.771 8.771,8.775 8.775,0 0,0 -8.771,-8.771Z"
android:fillColor="#000000"/>
<path
android:pathData="M25.879,7.794m-1.541,0a1.541,1.541 0,1 1,3.082 0a1.541,1.541 0,1 1,-3.082 0"
android:fillColor="#000000"/>
android:fillColor="#FF000000"
android:pathData="M12,2.163c3.204,0 3.584,0.012 4.85,0.07 3.252,0.148 4.771,1.691 4.919,4.919 0.058,1.265 0.069,1.645 0.069,4.849 0,3.205 -0.012,3.584 -0.069,4.849 -0.149,3.225 -1.664,4.771 -4.919,4.919 -1.266,0.058 -1.644,0.07 -4.85,0.07 -3.204,0 -3.584,-0.012 -4.849,-0.07 -3.26,-0.149 -4.771,-1.699 -4.919,-4.92 -0.058,-1.265 -0.07,-1.644 -0.07,-4.849 0,-3.204 0.013,-3.583 0.07,-4.849 0.149,-3.227 1.664,-4.771 4.919,-4.919 1.266,-0.057 1.645,-0.069 4.849,-0.069zM12,0c-3.259,0 -3.667,0.014 -4.947,0.072 -4.358,0.2 -6.78,2.618 -6.98,6.98 -0.059,1.281 -0.073,1.689 -0.073,4.948 0,3.259 0.014,3.668 0.072,4.948 0.2,4.358 2.618,6.78 6.98,6.98 1.281,0.058 1.689,0.072 4.948,0.072 3.259,0 3.668,-0.014 4.948,-0.072 4.354,-0.2 6.782,-2.618 6.979,-6.98 0.059,-1.28 0.073,-1.689 0.073,-4.948 0,-3.259 -0.014,-3.667 -0.072,-4.947 -0.196,-4.354 -2.617,-6.78 -6.979,-6.98 -1.281,-0.059 -1.69,-0.073 -4.949,-0.073zM12,5.838c-3.403,0 -6.162,2.759 -6.162,6.162s2.759,6.163 6.162,6.163 6.162,-2.759 6.162,-6.163c0,-3.403 -2.759,-6.162 -6.162,-6.162zM12,16c-2.209,0 -4,-1.79 -4,-4 0,-2.209 1.791,-4 4,-4s4,1.791 4,4c0,2.21 -1.791,4 -4,4zM18.406,4.155c-0.796,0 -1.441,0.645 -1.441,1.44s0.645,1.44 1.441,1.44c0.795,0 1.439,-0.645 1.439,-1.44s-0.644,-1.44 -1.439,-1.44z"/>
</vector>

View file

@ -1,21 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="38dp"
android:height="38dp"
android:viewportWidth="35"
android:viewportHeight="35">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M17.5,0.4c-9.4,0 -17,7.6 -17,17c0,9.4 7.6,17 17,17c9.4,0 17,-7.6 17,-17C34.5,8 26.9,0.4 17.5,0.4zM17.5,33.4c-8.8,0 -16,-7.2 -16,-16c0,-8.9 7.2,-16 16,-16c8.9,0 16,7.2 16,16C33.5,26.2 26.3,33.4 17.5,33.4z"/>
<path
android:fillColor="#FF000000"
android:pathData="M27.8,17.2c0,-1.7 -1.4,-3.1 -3.1,-3.1c-0.5,0 -1,0.1 -1.5,0.4c-1.2,-0.6 -2.6,-1.1 -4.1,-1.3l0.5,-1.6l1.6,0.4c0.3,1.1 1.4,1.9 2.6,1.9c1.5,0 2.7,-1.2 2.7,-2.7c0,-1.5 -1.2,-2.7 -2.7,-2.7c-0.8,0 -1.5,0.4 -2,0.9l-2.7,-0.6l-0.2,0h-0.1c-0.6,0 -1.1,0.4 -1.2,0.9l-1.1,3.4c-1.8,0.1 -3.5,0.6 -4.9,1.4c-0.4,-0.2 -0.9,-0.4 -1.4,-0.4c-1.7,0 -3.1,1.4 -3.1,3.1c0,0.8 0.4,1.6 1,2.2v0.2c0,3.6 4.2,6.5 9.4,6.5c5.1,0 9.3,-2.9 9.3,-6.5c0,-0.1 0,-0.1 0,-0.2C27.5,18.9 27.8,18.1 27.8,17.2zM25.8,19.6c0,3 -3.8,5.5 -8.3,5.5c-4.6,0 -8.4,-2.5 -8.4,-5.5c0,-0.2 0,-0.4 0.1,-0.7c-0.6,-0.4 -1,-1 -1,-1.8c0,-1.1 0.9,-2.1 2.1,-2.1c0.5,0 1,0.2 1.4,0.6c1.5,-1 3.5,-1.6 5.7,-1.6l1.3,-4c0,-0.2 0.2,-0.2 0.3,-0.2l3.4,0.8c0.2,-0.6 0.9,-1.1 1.6,-1.1c0.9,0 1.7,0.8 1.7,1.7S24.8,13 23.9,13c-0.9,0 -1.7,-0.8 -1.7,-1.7v0l-3.1,-0.7l-1.1,3.6c2.1,0.1 4,0.7 5.4,1.6c0.4,-0.4 0.9,-0.6 1.4,-0.6c1.1,0 2.1,0.9 2.1,2.1c0,0.8 -0.4,1.5 -1.1,1.8C25.8,19.2 25.8,19.4 25.8,19.6z"/>
<path
android:fillColor="#FF000000"
android:pathData="M20.5,22.3c-0.6,0.6 -1.6,0.9 -3,0.9h0c-1.4,0 -2.3,-0.3 -3,-0.9c-0.1,-0.1 -0.1,-0.3 0,-0.4c0.1,-0.1 0.3,-0.1 0.4,0c0.5,0.5 1.3,0.8 2.6,0.8h0c1.2,0 2.1,-0.2 2.6,-0.8c0.1,-0.1 0.3,-0.1 0.4,0C20.6,22 20.6,22.1 20.5,22.3z"/>
<path
android:fillColor="#FF000000"
android:pathData="M15.9,18.6c0,0.7 -0.5,1.2 -1.2,1.2c-0.7,0 -1.2,-0.5 -1.2,-1.2c0,-0.7 0.5,-1.2 1.2,-1.2C15.3,17.4 15.9,18 15.9,18.6z"/>
<path
android:fillColor="#FF000000"
android:pathData="M21.6,18.6c0,0.7 -0.5,1.2 -1.2,1.2s-1.2,-0.5 -1.2,-1.2c0,-0.7 0.6,-1.2 1.2,-1.2S21.6,18 21.6,18.6z"/>
android:pathData="M14.238,15.348c0.085,0.084 0.085,0.221 0,0.306 -0.465,0.462 -1.194,0.687 -2.231,0.687l-0.008,-0.002 -0.008,0.002c-1.036,0 -1.766,-0.225 -2.231,-0.688 -0.085,-0.084 -0.085,-0.221 0,-0.305 0.084,-0.084 0.222,-0.084 0.307,0 0.379,0.377 1.008,0.561 1.924,0.561l0.008,0.002 0.008,-0.002c0.915,0 1.544,-0.184 1.924,-0.561 0.085,-0.084 0.223,-0.084 0.307,0zM10.798,12.93c0,-0.507 -0.414,-0.919 -0.922,-0.919 -0.509,0 -0.923,0.412 -0.923,0.919 0,0.506 0.414,0.918 0.923,0.918 0.508,0.001 0.922,-0.411 0.922,-0.918zM24,12c0,6.627 -5.373,12 -12,12s-12,-5.373 -12,-12 5.373,-12 12,-12 12,5.373 12,12zM19,11.871c0,-0.851 -0.695,-1.543 -1.55,-1.543 -0.417,0 -0.795,0.167 -1.074,0.435 -1.056,-0.695 -2.485,-1.137 -4.066,-1.194l0.865,-2.724 2.343,0.549 -0.003,0.034c0,0.696 0.569,1.262 1.268,1.262 0.699,0 1.267,-0.566 1.267,-1.262s-0.568,-1.262 -1.267,-1.262c-0.537,0 -0.994,0.335 -1.179,0.804l-2.525,-0.592c-0.11,-0.027 -0.223,0.037 -0.257,0.145l-0.965,3.038c-1.656,0.02 -3.155,0.466 -4.258,1.181 -0.277,-0.255 -0.644,-0.415 -1.05,-0.415 -0.854,0.001 -1.549,0.693 -1.549,1.544 0,0.566 0.311,1.056 0.768,1.325 -0.03,0.164 -0.05,0.331 -0.05,0.5 0,2.281 2.805,4.137 6.253,4.137s6.253,-1.856 6.253,-4.137c0,-0.16 -0.017,-0.317 -0.044,-0.472 0.486,-0.261 0.82,-0.766 0.82,-1.353zM14.128,12.012c-0.509,0 -0.922,0.412 -0.922,0.919 0,0.506 0.414,0.918 0.922,0.918s0.922,-0.412 0.922,-0.918c0,-0.507 -0.413,-0.919 -0.922,-0.919z"/>
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1s-2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM13,17L8,17c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h5c0.55,0 1,0.45 1,1s-0.45,1 -1,1zM16,13L8,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h8c0.55,0 1,0.45 1,1s-0.45,1 -1,1zM16,9L8,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h8c0.55,0 1,0.45 1,1s-0.45,1 -1,1z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M18.3,5.71L18.3,5.71c-0.39,-0.39 -1.02,-0.39 -1.41,0L12,10.59L7.11,5.7c-0.39,-0.39 -1.02,-0.39 -1.41,0l0,0c-0.39,0.39 -0.39,1.02 0,1.41L10.59,12L5.7,16.89c-0.39,0.39 -0.39,1.02 0,1.41l0,0c0.39,0.39 1.02,0.39 1.41,0L12,13.41l4.89,4.89c0.39,0.39 1.02,0.39 1.41,0l0,0c0.39,-0.39 0.39,-1.02 0,-1.41L13.41,12l4.89,-4.89C18.68,6.73 18.68,6.09 18.3,5.71z"/>
</vector>

Some files were not shown because too many files have changed in this diff Show more