feat: routine housekeeping
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
b68267c1e7
commit
9e003897a5
|
|
@ -33,6 +33,7 @@ import chat.revolt.screens.about.AboutScreen
|
|||
import chat.revolt.screens.about.AttributionScreen
|
||||
import chat.revolt.screens.chat.ChatRouterScreen
|
||||
import chat.revolt.screens.chat.dialogs.FeedbackDialog
|
||||
import chat.revolt.screens.labs.LabsRootScreen
|
||||
import chat.revolt.screens.login.LoginGreetingScreen
|
||||
import chat.revolt.screens.login.LoginScreen
|
||||
import chat.revolt.screens.login.MfaScreen
|
||||
|
|
@ -159,6 +160,8 @@ fun AppEntrypoint(windowSizeClass: WindowSizeClass) {
|
|||
|
||||
composable("about") { AboutScreen(navController) }
|
||||
composable("about/oss") { AttributionScreen(navController) }
|
||||
|
||||
composable("labs") { LabsRootScreen(navController) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@ import android.content.Context
|
|||
import android.graphics.RuntimeShader
|
||||
import android.os.Build
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush as AndroidBrush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ShaderBrush
|
||||
import org.intellij.lang.annotations.Language
|
||||
import androidx.compose.ui.graphics.Brush as AndroidBrush
|
||||
|
||||
object SpecialUsers {
|
||||
val JENNIFER = "01F1WKM5TK2V6KCZWR6DGBJDTZ"
|
||||
|
||||
val PLATFORM_MODERATION_USER = "01FC17E1WTM2BGE4F3ARN3FDAF"
|
||||
|
||||
val TRUSTED_MODERATION_BOTS = listOf(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.internals.SpecialUsers
|
||||
|
||||
annotation class FeatureFlag(val name: String)
|
||||
annotation class Treatment(val description: String)
|
||||
|
|
@ -19,6 +20,14 @@ sealed class ClosedBetaAccessControlVariates {
|
|||
data object Unrestricted : ClosedBetaAccessControlVariates()
|
||||
}
|
||||
|
||||
@FeatureFlag("LabsAccessControl")
|
||||
sealed class LabsAccessControlVariates {
|
||||
@Treatment(
|
||||
"Restrict access to Labs to users that meet certain or all criteria (implementation-specific)"
|
||||
)
|
||||
data class Restricted(val predicate: () -> Boolean) : LabsAccessControlVariates()
|
||||
}
|
||||
|
||||
object FeatureFlags {
|
||||
@FeatureFlag("ClosedBetaAccessControl")
|
||||
var closedBetaAccessControl by mutableStateOf<ClosedBetaAccessControlVariates>(
|
||||
|
|
@ -26,4 +35,11 @@ object FeatureFlags {
|
|||
RevoltAPI.channelCache.containsKey("01H7X2KRB0CA4QDSMB4N7WGERF")
|
||||
}
|
||||
)
|
||||
|
||||
@FeatureFlag("LabsAccessControl")
|
||||
var labsAccessControl by mutableStateOf<LabsAccessControlVariates>(
|
||||
LabsAccessControlVariates.Restricted {
|
||||
RevoltAPI.selfId == SpecialUsers.JENNIFER
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
package chat.revolt.screens.labs
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
|
||||
enum class LabsHomeScreenTab {
|
||||
Home,
|
||||
Mockups,
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun LabsHomeScreen(navController: NavController) {
|
||||
val currentTab = rememberSaveable { mutableStateOf(LabsHomeScreenTab.Home) }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(),
|
||||
title = {
|
||||
Text("Labs")
|
||||
}
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
NavigationBar {
|
||||
NavigationBarItem(
|
||||
selected = currentTab.value == LabsHomeScreenTab.Home,
|
||||
onClick = { currentTab.value = LabsHomeScreenTab.Home },
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Home,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
label = {
|
||||
Text("Home")
|
||||
}
|
||||
)
|
||||
NavigationBarItem(
|
||||
selected = currentTab.value == LabsHomeScreenTab.Mockups,
|
||||
onClick = { currentTab.value = LabsHomeScreenTab.Mockups },
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Menu,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
label = {
|
||||
Text("UI Mockups")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Box(Modifier.padding(it)) {
|
||||
when (currentTab.value) {
|
||||
LabsHomeScreenTab.Home -> {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
"Hey, this is kinda secret 🤫",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(
|
||||
"Remember, everything you see here can be broken and is not guaranteed to work.",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(
|
||||
"Don't tell anyone about anything either, okay?",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LabsHomeScreenTab.Mockups -> {
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text("Call Screen")
|
||||
},
|
||||
modifier = Modifier.clickable {
|
||||
navController.navigate("mockups/call")
|
||||
}
|
||||
)
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package chat.revolt.screens.labs
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import chat.revolt.api.settings.FeatureFlags
|
||||
import chat.revolt.api.settings.LabsAccessControlVariates
|
||||
import chat.revolt.screens.labs.ui.mockups.CallScreenMockup
|
||||
|
||||
annotation class LabsFeature
|
||||
|
||||
@Composable
|
||||
fun LabsGuard(onTurnBack: () -> Unit = {}, content: @Composable () -> Unit) {
|
||||
if (FeatureFlags.labsAccessControl is LabsAccessControlVariates.Restricted &&
|
||||
(FeatureFlags.labsAccessControl as LabsAccessControlVariates.Restricted).predicate().not()
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { onTurnBack() },
|
||||
confirmButton = {
|
||||
TextButton(onClick = { onTurnBack() }) {
|
||||
Text("Turn back")
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text("You don't have access to Labs.")
|
||||
},
|
||||
text = {
|
||||
Text("Labs is where we test new features. However, these features may be unstable and may not work as expected. Hence, access to Labs is restricted.")
|
||||
}
|
||||
)
|
||||
} else {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LabsRootScreen(topNav: NavController) {
|
||||
val labsNav = rememberNavController()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
LabsGuard(
|
||||
onTurnBack = {
|
||||
topNav.popBackStack()
|
||||
}
|
||||
) {
|
||||
NavHost(
|
||||
navController = labsNav,
|
||||
startDestination = "home",
|
||||
) {
|
||||
composable("home") {
|
||||
LabsHomeScreen(labsNav)
|
||||
}
|
||||
|
||||
composable("mockups/call") {
|
||||
CallScreenMockup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,262 @@
|
|||
package chat.revolt.screens.labs.ui.mockups
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.EaseInOutExpo
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.schemas.ChannelType
|
||||
import chat.revolt.components.screens.chat.ChannelIcon
|
||||
import chat.revolt.screens.labs.LabsFeature
|
||||
|
||||
@LabsFeature
|
||||
@Composable
|
||||
fun CallScreenMockup() {
|
||||
var showOptions by remember { mutableStateOf(false) }
|
||||
var pushToTalk by remember { mutableStateOf(false) }
|
||||
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val pushToTalkIsHeld by interactionSource.collectIsPressedAsState()
|
||||
|
||||
val pttBackground by animateColorAsState(
|
||||
targetValue = if (pushToTalkIsHeld) {
|
||||
MaterialTheme.colorScheme.primaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.secondaryContainer
|
||||
},
|
||||
animationSpec = tween(200, easing = EaseInOutExpo),
|
||||
label = "pttBackground"
|
||||
)
|
||||
val pttText by animateColorAsState(
|
||||
targetValue = if (pushToTalkIsHeld) {
|
||||
MaterialTheme.colorScheme.onPrimaryContainer
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSecondaryContainer
|
||||
},
|
||||
animationSpec = tween(200, easing = EaseInOutExpo),
|
||||
label = "pttText"
|
||||
)
|
||||
|
||||
if (showOptions) {
|
||||
Dialog(
|
||||
onDismissRequest = { showOptions = false }
|
||||
) {
|
||||
BoxWithConstraints {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.large)
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.padding(24.dp)
|
||||
.width(maxWidth * 0.85f)
|
||||
.heightIn(max = maxHeight * 0.85f)
|
||||
) {
|
||||
Row {
|
||||
Checkbox(
|
||||
checked = pushToTalk,
|
||||
onCheckedChange = { pushToTalk = it },
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Push to talk",
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.safeDrawingPadding()
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = 4.dp, horizontal = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.height(48.dp)
|
||||
.width(12.dp)
|
||||
)
|
||||
|
||||
ChannelIcon(
|
||||
channelType = ChannelType.VoiceChannel,
|
||||
modifier = Modifier.alpha(0.6f)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "Voice Channel",
|
||||
fontWeight = FontWeight.Medium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight,
|
||||
contentDescription = stringResource(R.string.menu),
|
||||
modifier = Modifier
|
||||
.size(18.dp)
|
||||
.alpha(0.4f)
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = {
|
||||
showOptions = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowDown,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
) {
|
||||
}
|
||||
if (pushToTalk) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
TextButton(
|
||||
onClick = {},
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_headphones_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Galaxy Buds Live",
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Button(
|
||||
onClick = {},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = pttBackground,
|
||||
contentColor = pttText
|
||||
),
|
||||
interactionSource = interactionSource,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_gesture_tap_button_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Hold to talk",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
if (!pushToTalk) {
|
||||
Button(
|
||||
onClick = {}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_microphone_off_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = MaterialTheme.colorScheme.errorContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onErrorContainer
|
||||
),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
) {
|
||||
Text(
|
||||
text = "Leave call",
|
||||
)
|
||||
}
|
||||
|
||||
if (!pushToTalk) {
|
||||
Button(
|
||||
onClick = {}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_headphones_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.safeDrawingPadding
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowForward
|
||||
import androidx.compose.material.icons.filled.Build
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.DateRange
|
||||
|
|
@ -27,7 +28,9 @@ import androidx.navigation.NavController
|
|||
import chat.revolt.BuildConfig
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.settings.FeatureFlags
|
||||
import chat.revolt.api.settings.GlobalState
|
||||
import chat.revolt.api.settings.LabsAccessControlVariates
|
||||
import chat.revolt.components.generic.PageHeader
|
||||
import chat.revolt.components.generic.SheetClickable
|
||||
import chat.revolt.components.screens.settings.SelfUserOverview
|
||||
|
|
@ -189,6 +192,25 @@ fun SettingsScreen(
|
|||
}
|
||||
}
|
||||
|
||||
if (FeatureFlags.labsAccessControl is LabsAccessControlVariates.Restricted &&
|
||||
(FeatureFlags.labsAccessControl as LabsAccessControlVariates.Restricted).predicate()
|
||||
) {
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowForward,
|
||||
contentDescription = null,
|
||||
modifier = modifier
|
||||
)
|
||||
},
|
||||
label = { textStyle ->
|
||||
Text(text = "Labs", style = textStyle)
|
||||
},
|
||||
) {
|
||||
navController.navigate("labs")
|
||||
}
|
||||
}
|
||||
|
||||
SheetClickable(
|
||||
icon = { modifier ->
|
||||
Icon(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M13 5C15.21 5 17 6.79 17 9C17 10.5 16.2 11.77 15 12.46V11.24C15.61 10.69 16 9.89 16 9C16 7.34 14.66 6 13 6S10 7.34 10 9C10 9.89 10.39 10.69 11 11.24V12.46C9.8 11.77 9 10.5 9 9C9 6.79 10.79 5 13 5M20 20.5C19.97 21.32 19.32 21.97 18.5 22H13C12.62 22 12.26 21.85 12 21.57L8 17.37L8.74 16.6C8.93 16.39 9.2 16.28 9.5 16.28H9.7L12 18V9C12 8.45 12.45 8 13 8S14 8.45 14 9V13.47L15.21 13.6L19.15 15.79C19.68 16.03 20 16.56 20 17.14V20.5M20 2H4C2.9 2 2 2.9 2 4V12C2 13.11 2.9 14 4 14H8V12L4 12L4 4H20L20 12H18V14H20V13.96L20.04 14C21.13 14 22 13.09 22 12V4C22 2.9 21.11 2 20 2Z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M12,1C7,1 3,5 3,10V17A3,3 0 0,0 6,20H9V12H5V10A7,7 0 0,1 12,3A7,7 0 0,1 19,10V12H15V20H18A3,3 0 0,0 21,17V10C21,5 16.97,1 12,1Z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M12,1A9,9 0 0,1 21,10V17C21,17.62 20.81,18.19 20.5,18.67L15,13.18V12H19V10A7,7 0 0,0 12,3C10,3 8.23,3.82 6.96,5.14L5.55,3.72C7.18,2.04 9.47,1 12,1M2.78,3.5L20.5,21.22L19.23,22.5L16.73,20H15V18.27L9,12.27V20H6A3,3 0 0,1 3,17V10C3,8.89 3.2,7.82 3.57,6.84L1.5,4.77L2.78,3.5M5.17,8.44C5.06,8.94 5,9.46 5,10V12H8.73L5.17,8.44Z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M19,11C19,12.19 18.66,13.3 18.1,14.28L16.87,13.05C17.14,12.43 17.3,11.74 17.3,11H19M15,11.16L9,5.18V5A3,3 0 0,1 12,2A3,3 0 0,1 15,5V11L15,11.16M4.27,3L21,19.73L19.73,21L15.54,16.81C14.77,17.27 13.91,17.58 13,17.72V21H11V17.72C7.72,17.23 5,14.41 5,11H6.7C6.7,14 9.24,16.1 12,16.1C12.81,16.1 13.6,15.91 14.31,15.58L12.65,13.92L12,14A3,3 0 0,1 9,11V10.28L3,4.27L4.27,3Z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M12,9C10.4,9 8.85,9.25 7.4,9.72V12.82C7.4,13.22 7.17,13.56 6.84,13.72C5.86,14.21 4.97,14.84 4.17,15.57C4,15.75 3.75,15.86 3.5,15.86C3.2,15.86 2.95,15.74 2.77,15.56L0.29,13.08C0.11,12.9 0,12.65 0,12.38C0,12.1 0.11,11.85 0.29,11.67C3.34,8.77 7.46,7 12,7C16.54,7 20.66,8.77 23.71,11.67C23.89,11.85 24,12.1 24,12.38C24,12.65 23.89,12.9 23.71,13.08L21.23,15.56C21.05,15.74 20.8,15.86 20.5,15.86C20.25,15.86 20,15.75 19.82,15.57C19.03,14.84 18.14,14.21 17.16,13.72C16.83,13.56 16.6,13.22 16.6,12.82V9.72C15.15,9.25 13.6,9 12,9Z" />
|
||||
</vector>
|
||||
Loading…
Reference in New Issue