Add location tap feature
This commit is contained in:
		
							parent
							
								
									6e56313639
								
							
						
					
					
						commit
						81259cabc9
					
				
					 5 changed files with 199 additions and 50 deletions
				
			
		| 
						 | 
				
			
			@ -41,6 +41,7 @@ fun FantasyMap(
 | 
			
		|||
    item: State<LocationDetailUio>,
 | 
			
		||||
    selectedItem: State<Int>,
 | 
			
		||||
    onMarquee: (MarqueeUio) -> Unit,
 | 
			
		||||
    onTap: (Offset) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val colorScheme = MaterialTheme.lexicon.colorScheme
 | 
			
		||||
    val animatedScale = animateFloatAsState(targetValue = state.scale, label = "ScaleAnimation")
 | 
			
		||||
| 
						 | 
				
			
			@ -84,47 +85,63 @@ fun FantasyMap(
 | 
			
		|||
                .onSizeChanged { state.imageSize = it }
 | 
			
		||||
                .drawWithContent {
 | 
			
		||||
                    drawContent()
 | 
			
		||||
                    item.value.marquees.forEachIndexed { index, item ->
 | 
			
		||||
                        if (item.position != Offset.Unspecified) {
 | 
			
		||||
                            drawCircle(
 | 
			
		||||
                                color = colorScheme.shadow,
 | 
			
		||||
                                radius = 12.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                style = Stroke(
 | 
			
		||||
                                    width = 2.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                ),
 | 
			
		||||
                                center = Offset(
 | 
			
		||||
                                    x = size.width * item.position.x,
 | 
			
		||||
                                    y = size.height * item.position.y + 2.dp.toPx() / animatedScale.value,
 | 
			
		||||
 | 
			
		||||
                    if (state.freeHand.not()) {
 | 
			
		||||
                        item.value.marquees.forEachIndexed { index, item ->
 | 
			
		||||
                            if (item.position != Offset.Unspecified) {
 | 
			
		||||
                                drawCircle(
 | 
			
		||||
                                    color = colorScheme.shadow,
 | 
			
		||||
                                    radius = 12.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    style = Stroke(
 | 
			
		||||
                                        width = 2.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    center = Offset(
 | 
			
		||||
                                        x = size.width * item.position.x,
 | 
			
		||||
                                        y = size.height * item.position.y + 2.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    )
 | 
			
		||||
                                )
 | 
			
		||||
                            )
 | 
			
		||||
                            drawCircle(
 | 
			
		||||
                                color = when (selectedItem.value) {
 | 
			
		||||
                                    index -> colorScheme.base.primary
 | 
			
		||||
                                    else -> Color.White
 | 
			
		||||
                                },
 | 
			
		||||
                                radius = 12.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                style = Stroke(
 | 
			
		||||
                                    width = 2.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                ),
 | 
			
		||||
                                center = Offset(
 | 
			
		||||
                                    x = size.width * item.position.x,
 | 
			
		||||
                                    y = size.height * item.position.y,
 | 
			
		||||
                                drawCircle(
 | 
			
		||||
                                    color = when (selectedItem.value) {
 | 
			
		||||
                                        index -> colorScheme.base.primary
 | 
			
		||||
                                        else -> Color.White
 | 
			
		||||
                                    },
 | 
			
		||||
                                    radius = 12.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    style = Stroke(
 | 
			
		||||
                                        width = 2.dp.toPx() / animatedScale.value,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    center = Offset(
 | 
			
		||||
                                        x = size.width * item.position.x,
 | 
			
		||||
                                        y = size.height * item.position.y,
 | 
			
		||||
                                    )
 | 
			
		||||
                                )
 | 
			
		||||
                            )
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .pointerInput("DetectTapGestures") {
 | 
			
		||||
                    detectTapGestures(
 | 
			
		||||
                        onTap = { tap ->
 | 
			
		||||
                            val marquee = item.value.marquees
 | 
			
		||||
                                .asReversed()
 | 
			
		||||
                                .firstOrNull { item ->
 | 
			
		||||
                                    val radius = 24.dp.toPx() / animatedScale.value
 | 
			
		||||
                                    (size.width * item.position.x).let { tap.x in (it - radius)..(it + radius) } &&
 | 
			
		||||
                                            (size.height * item.position.y).let { tap.y in (it - radius)..(it + radius) }
 | 
			
		||||
                                }
 | 
			
		||||
                            marquee?.let(onMarquee)
 | 
			
		||||
                            if (state.freeHand) {
 | 
			
		||||
                                onTap(
 | 
			
		||||
                                    Offset(
 | 
			
		||||
                                        x = tap.x / size.width,
 | 
			
		||||
                                        y = tap.y / size.height,
 | 
			
		||||
                                    )
 | 
			
		||||
                                )
 | 
			
		||||
                            } else {
 | 
			
		||||
                                val marquee = item.value.marquees
 | 
			
		||||
                                    .asReversed()
 | 
			
		||||
                                    .firstOrNull { item ->
 | 
			
		||||
                                        if (item.position != Offset.Unspecified) {
 | 
			
		||||
                                            val radius = 24.dp.toPx() / animatedScale.value
 | 
			
		||||
                                            (size.width * item.position.x).let { tap.x in (it - radius)..(it + radius) } &&
 | 
			
		||||
                                                    (size.height * item.position.y).let { tap.y in (it - radius)..(it + radius) }
 | 
			
		||||
                                        } else {
 | 
			
		||||
                                            false
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                marquee?.let(onMarquee)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    )
 | 
			
		||||
                },
 | 
			
		||||
| 
						 | 
				
			
			@ -174,6 +191,9 @@ class FantasyMapState(
 | 
			
		|||
    private val _imageSize: MutableState<IntSize> = mutableStateOf(IntSize.Zero)
 | 
			
		||||
    var imageSize: IntSize by _imageSize
 | 
			
		||||
 | 
			
		||||
    private val _freeHand: MutableState<Boolean> = mutableStateOf(false)
 | 
			
		||||
    val freeHand: Boolean by _freeHand
 | 
			
		||||
 | 
			
		||||
    @Stable
 | 
			
		||||
    fun scale(
 | 
			
		||||
        scale: Float,
 | 
			
		||||
| 
						 | 
				
			
			@ -193,6 +213,10 @@ class FantasyMapState(
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun toggleFreeHand(toggle: Boolean) {
 | 
			
		||||
        _freeHand.value = toggle
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Stable
 | 
			
		||||
    fun computeMarqueeOffset(
 | 
			
		||||
        origin: Offset,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,11 +22,14 @@ import androidx.compose.foundation.rememberScrollState
 | 
			
		|||
import androidx.compose.foundation.verticalScroll
 | 
			
		||||
import androidx.compose.material3.ExperimentalMaterial3Api
 | 
			
		||||
import androidx.compose.material3.FilledIconButton
 | 
			
		||||
import androidx.compose.material3.FilledIconToggleButton
 | 
			
		||||
import androidx.compose.material3.FilledTonalIconButton
 | 
			
		||||
import androidx.compose.material3.Icon
 | 
			
		||||
import androidx.compose.material3.IconButton
 | 
			
		||||
import androidx.compose.material3.IconButtonDefaults
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Scaffold
 | 
			
		||||
import androidx.compose.material3.SnackbarDuration
 | 
			
		||||
import androidx.compose.material3.Surface
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.material3.TopAppBar
 | 
			
		||||
| 
						 | 
				
			
			@ -48,15 +51,19 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
 | 
			
		|||
import androidx.compose.ui.layout.ContentScale
 | 
			
		||||
import androidx.compose.ui.res.painterResource
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.tooling.preview.Preview
 | 
			
		||||
import androidx.compose.ui.unit.IntOffset
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import androidx.hilt.navigation.compose.hiltViewModel
 | 
			
		||||
import com.pixelized.rplexicon.LocalSnack
 | 
			
		||||
import com.pixelized.rplexicon.R
 | 
			
		||||
import com.pixelized.rplexicon.ui.composable.Handle
 | 
			
		||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
 | 
			
		||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
			
		||||
import com.pixelized.rplexicon.utilitary.rememberTextSize
 | 
			
		||||
import com.skydoves.landscapist.ImageOptions
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.coroutines.runBlocking
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
| 
						 | 
				
			
			@ -73,11 +80,16 @@ data class LocationDetailUio(
 | 
			
		|||
fun LocationDetail(
 | 
			
		||||
    viewModel: LocationDetailViewModel = hiltViewModel()
 | 
			
		||||
) {
 | 
			
		||||
    val screen = LocalScreenNavHost.current
 | 
			
		||||
    val snack = LocalSnack.current
 | 
			
		||||
 | 
			
		||||
    val scope = rememberCoroutineScope()
 | 
			
		||||
    val scroll = rememberScrollState()
 | 
			
		||||
    val pager = rememberPagerState()
 | 
			
		||||
    val fantasy = rememberFantasyMapState()
 | 
			
		||||
    val screen = LocalScreenNavHost.current
 | 
			
		||||
 | 
			
		||||
    val ok = stringResource(id = android.R.string.ok)
 | 
			
		||||
    val job = remember { mutableStateOf<Job?>(null) }
 | 
			
		||||
 | 
			
		||||
    val connection = remember {
 | 
			
		||||
        object : NestedScrollConnection {
 | 
			
		||||
| 
						 | 
				
			
			@ -117,6 +129,19 @@ fun LocationDetail(
 | 
			
		|||
                    pager.animateScrollToPage(page = index)
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            onMapTap = {
 | 
			
		||||
                job.value?.cancel()
 | 
			
		||||
                job.value = scope.launch {
 | 
			
		||||
                    snack.showSnackbar(
 | 
			
		||||
                        message = "x:${it.x}, y:${it.y}",
 | 
			
		||||
                        actionLabel = ok,
 | 
			
		||||
                        duration = SnackbarDuration.Indefinite,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            onTouch = {
 | 
			
		||||
                fantasy.toggleFreeHand(it)
 | 
			
		||||
            },
 | 
			
		||||
            onCenter = {
 | 
			
		||||
                fantasy.scale(scale = 1f)
 | 
			
		||||
                fantasy.pan(offset = Offset.Zero)
 | 
			
		||||
| 
						 | 
				
			
			@ -176,10 +201,26 @@ private fun LocationContent(
 | 
			
		|||
    selectedIndex: State<Int>,
 | 
			
		||||
    onBack: () -> Unit,
 | 
			
		||||
    onMarquee: (MarqueeUio) -> Unit,
 | 
			
		||||
    onMapTap: (Offset) -> Unit,
 | 
			
		||||
    onTouch: (Boolean) -> Unit,
 | 
			
		||||
    onCenter: () -> Unit,
 | 
			
		||||
    onZoomIn: () -> Unit,
 | 
			
		||||
    onZoomOut: () -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val itemNameSize = rememberTextSize(style = MaterialTheme.typography.headlineSmall)
 | 
			
		||||
 | 
			
		||||
    val filledIconButtonColors = IconButtonDefaults.filledIconButtonColors(
 | 
			
		||||
        containerColor = MaterialTheme.colorScheme.surface,
 | 
			
		||||
        contentColor = MaterialTheme.colorScheme.onSurface,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    val filledIconToggleButtonColors = IconButtonDefaults.filledIconToggleButtonColors(
 | 
			
		||||
        containerColor = MaterialTheme.colorScheme.surface,
 | 
			
		||||
        contentColor = MaterialTheme.colorScheme.onSurface,
 | 
			
		||||
        checkedContainerColor = MaterialTheme.colorScheme.primary,
 | 
			
		||||
        checkedContentColor = MaterialTheme.colorScheme.onPrimary,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    Scaffold(
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
        topBar = {
 | 
			
		||||
| 
						 | 
				
			
			@ -226,19 +267,31 @@ private fun LocationContent(
 | 
			
		|||
                            item = item,
 | 
			
		||||
                            selectedItem = selectedIndex,
 | 
			
		||||
                            onMarquee = onMarquee,
 | 
			
		||||
                            onTap = onMapTap,
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
                        FilledIconToggleButton(
 | 
			
		||||
                            modifier = Modifier
 | 
			
		||||
                                .align(alignment = Alignment.TopEnd)
 | 
			
		||||
                                .padding(all = 16.dp),
 | 
			
		||||
                            checked = fantasyMapState.freeHand,
 | 
			
		||||
                            onCheckedChange = onTouch,
 | 
			
		||||
                            colors = filledIconToggleButtonColors,
 | 
			
		||||
                        ) {
 | 
			
		||||
                            Icon(
 | 
			
		||||
                                painter = painterResource(id = R.drawable.ic_baseline_touch_app_24),
 | 
			
		||||
                                contentDescription = null
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        Column(
 | 
			
		||||
                            modifier = Modifier
 | 
			
		||||
                                .align(alignment = Alignment.BottomEnd)
 | 
			
		||||
                                .padding(all = 16.dp),
 | 
			
		||||
                        ) {
 | 
			
		||||
                            val colors = IconButtonDefaults.filledIconButtonColors(
 | 
			
		||||
                                containerColor = MaterialTheme.colorScheme.surface,
 | 
			
		||||
                                contentColor = MaterialTheme.colorScheme.onSurface,
 | 
			
		||||
                            )
 | 
			
		||||
                            FilledIconButton(
 | 
			
		||||
                                onClick = onZoomOut,
 | 
			
		||||
                                colors = colors,
 | 
			
		||||
                                colors = filledIconButtonColors,
 | 
			
		||||
                            ) {
 | 
			
		||||
                                Icon(
 | 
			
		||||
                                    painter = painterResource(id = R.drawable.ic_baseline_remove_24),
 | 
			
		||||
| 
						 | 
				
			
			@ -247,7 +300,7 @@ private fun LocationContent(
 | 
			
		|||
                            }
 | 
			
		||||
                            FilledIconButton(
 | 
			
		||||
                                onClick = onZoomIn,
 | 
			
		||||
                                colors = colors,
 | 
			
		||||
                                colors = filledIconButtonColors,
 | 
			
		||||
                            ) {
 | 
			
		||||
                                Icon(
 | 
			
		||||
                                    painter = painterResource(id = R.drawable.ic_baseline_add_24),
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +309,7 @@ private fun LocationContent(
 | 
			
		|||
                            }
 | 
			
		||||
                            FilledIconButton(
 | 
			
		||||
                                onClick = onCenter,
 | 
			
		||||
                                colors = colors,
 | 
			
		||||
                                colors = filledIconButtonColors,
 | 
			
		||||
                            ) {
 | 
			
		||||
                                Icon(
 | 
			
		||||
                                    painter = painterResource(id = R.drawable.ic_baseline_zoom_in_map_24),
 | 
			
		||||
| 
						 | 
				
			
			@ -273,6 +326,16 @@ private fun LocationContent(
 | 
			
		|||
                        .padding(top = 16.dp)
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                Text(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .align(alignment = Alignment.CenterHorizontally)
 | 
			
		||||
                        .padding(horizontal = 16.dp)
 | 
			
		||||
                        .padding(top = 16.dp),
 | 
			
		||||
                    textAlign = TextAlign.Center,
 | 
			
		||||
                    style = MaterialTheme.typography.headlineSmall,
 | 
			
		||||
                    text = item.value.name,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                HorizontalPager(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .fillMaxWidth()
 | 
			
		||||
| 
						 | 
				
			
			@ -287,7 +350,7 @@ private fun LocationContent(
 | 
			
		|||
                        MarqueeItem(
 | 
			
		||||
                            modifier = Modifier
 | 
			
		||||
                                .fillMaxWidth()
 | 
			
		||||
                                .height(this@constraint.maxHeight - 32.dp),
 | 
			
		||||
                                .height(this@constraint.maxHeight - 32.dp - itemNameSize.height),
 | 
			
		||||
                            marquee = marquee,
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -351,6 +414,8 @@ private fun LocationPreview() {
 | 
			
		|||
                selectedIndex = remember { mutableStateOf(0) },
 | 
			
		||||
                onBack = { },
 | 
			
		||||
                onMarquee = { },
 | 
			
		||||
                onMapTap = { },
 | 
			
		||||
                onTouch = { },
 | 
			
		||||
                onCenter = { },
 | 
			
		||||
                onZoomIn = { },
 | 
			
		||||
                onZoomOut = { },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,9 +4,11 @@ import androidx.compose.foundation.layout.Arrangement
 | 
			
		|||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.foundation.rememberScrollState
 | 
			
		||||
import androidx.compose.foundation.verticalScroll
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Surface
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.Stable
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +41,7 @@ fun MarqueeItem(
 | 
			
		|||
            textAlign = TextAlign.Center,
 | 
			
		||||
            overflow = TextOverflow.Ellipsis,
 | 
			
		||||
            maxLines = 1,
 | 
			
		||||
            style = MaterialTheme.typography.headlineSmall,
 | 
			
		||||
            style = MaterialTheme.typography.titleMedium,
 | 
			
		||||
            text = marquee.name,
 | 
			
		||||
        )
 | 
			
		||||
        marquee.description?.let {
 | 
			
		||||
| 
						 | 
				
			
			@ -56,13 +58,15 @@ fun MarqueeItem(
 | 
			
		|||
@Preview
 | 
			
		||||
private fun MarqueeItemPreview() {
 | 
			
		||||
    LexiconTheme {
 | 
			
		||||
        MarqueeItem(
 | 
			
		||||
            modifier = Modifier.fillMaxSize(),
 | 
			
		||||
            marquee = MarqueeUio(
 | 
			
		||||
                name = "Name",
 | 
			
		||||
                position = Offset.Zero,
 | 
			
		||||
                description = "description",
 | 
			
		||||
        Surface {
 | 
			
		||||
            MarqueeItem(
 | 
			
		||||
                modifier = Modifier.padding(all = 16.dp),
 | 
			
		||||
                marquee = MarqueeUio(
 | 
			
		||||
                    name = "Name",
 | 
			
		||||
                    position = Offset.Zero,
 | 
			
		||||
                    description = "description",
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
package com.pixelized.rplexicon.utilitary
 | 
			
		||||
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.Immutable
 | 
			
		||||
import androidx.compose.runtime.Stable
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.ui.platform.LocalDensity
 | 
			
		||||
import androidx.compose.ui.text.TextStyle
 | 
			
		||||
import androidx.compose.ui.unit.Dp
 | 
			
		||||
import androidx.compose.ui.unit.TextUnit
 | 
			
		||||
 | 
			
		||||
private const val PT_TO_PX_RATIO = 4f / 3f // pt to px ratio
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * TextMeasurement class
 | 
			
		||||
 *
 | 
			
		||||
 * Currently it is impossible to set a *minLine* value on a Text in Compose.
 | 
			
		||||
 * This class is a work around that compute the require Height for a [TextStyle].
 | 
			
		||||
 *
 | 
			
		||||
 * google issue tracker :
 | 
			
		||||
 * https://issuetracker.google.com/issues/122476634
 | 
			
		||||
 *
 | 
			
		||||
 * based on :
 | 
			
		||||
 * https://stackoverflow.com/questions/66394624/specify-minimal-lines-for-text-in-jetpack-compose
 | 
			
		||||
 */
 | 
			
		||||
@Stable
 | 
			
		||||
@Immutable
 | 
			
		||||
data class TextMeasurement(
 | 
			
		||||
    val height: Dp,
 | 
			
		||||
    val line: TextUnit,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun rememberTextSize(
 | 
			
		||||
    style: TextStyle,
 | 
			
		||||
): TextMeasurement {
 | 
			
		||||
    val density = LocalDensity.current
 | 
			
		||||
    return remember(density, style) {
 | 
			
		||||
        val line = if (style.lineHeight != TextUnit.Unspecified) {
 | 
			
		||||
            style.lineHeight
 | 
			
		||||
        } else {
 | 
			
		||||
            style.fontSize * PT_TO_PX_RATIO
 | 
			
		||||
        }
 | 
			
		||||
        val height = with(density) { line.toDp() }
 | 
			
		||||
 | 
			
		||||
        TextMeasurement(
 | 
			
		||||
            height = height,
 | 
			
		||||
            line = line,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/ic_baseline_touch_app_24.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/ic_baseline_touch_app_24.xml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
<vector android:height="24dp" android:tint="#000000"
 | 
			
		||||
    android:viewportHeight="24" android:viewportWidth="24"
 | 
			
		||||
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <path android:fillColor="@android:color/white" android:pathData="M9,11.24V7.5C9,6.12 10.12,5 11.5,5S14,6.12 14,7.5v3.74c1.21,-0.81 2,-2.18 2,-3.74C16,5.01 13.99,3 11.5,3S7,5.01 7,7.5C7,9.06 7.79,10.43 9,11.24zM18.84,15.87l-4.54,-2.26c-0.17,-0.07 -0.35,-0.11 -0.54,-0.11H13v-6C13,6.67 12.33,6 11.5,6S10,6.67 10,7.5v10.74c-3.6,-0.76 -3.54,-0.75 -3.67,-0.75c-0.31,0 -0.59,0.13 -0.79,0.33l-0.79,0.8l4.94,4.94C9.96,23.83 10.34,24 10.75,24h6.79c0.75,0 1.33,-0.55 1.44,-1.28l0.75,-5.27c0.01,-0.07 0.02,-0.14 0.02,-0.2C19.75,16.63 19.37,16.09 18.84,15.87z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue