Navigation Image

This commit is contained in:
Dimitris
2025-11-25 15:14:34 +01:00
parent b1a9a2c7fe
commit ce382e304c
20 changed files with 437 additions and 244 deletions

View File

@@ -49,6 +49,9 @@ dependencies {
implementation(project(":common:data"))
implementation(libs.androidx.runtime.livedata)
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.material3)
implementation(libs.androidx.compose.ui.text)
androidTestImplementation(libs.androidx.junit)
testImplementation(libs.junit)
}

View File

@@ -0,0 +1,179 @@
package com.kouros.navigation.car
import android.R.attr.strokeWidth
import android.graphics.Rect
import android.location.Location
import androidx.car.app.CarContext
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.kouros.android.cars.carappservice.R
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.CameraState
import org.maplibre.compose.camera.rememberCameraState
import org.maplibre.compose.expressions.dsl.const
import org.maplibre.compose.layers.Anchor
import org.maplibre.compose.layers.FillLayer
import org.maplibre.compose.layers.LineLayer
import org.maplibre.compose.location.LocationPuckColors
import org.maplibre.compose.location.LocationPuckSizes
import org.maplibre.compose.sources.GeoJsonData
import org.maplibre.compose.sources.Source
import org.maplibre.compose.sources.rememberGeoJsonSource
import org.maplibre.spatialk.geojson.Position
@Composable
fun cameraState(position: CameraPosition?, tilt: Double, preview: Boolean): CameraState {
val padding = getPaddingValues(preview)
return rememberCameraState(
firstPosition =
CameraPosition(
target = Position(
latitude = position!!.target.latitude,
longitude = position.target.longitude
),
zoom = 15.0,
tilt = tilt,
padding = padding
)
)
}
@Composable
fun RouteLayer(routeData: String?, previewRoute: String?) {
if (routeData!!.isNotEmpty()) {
val routes =
rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
LineLayer(
id = "routes-casing",
source = routes,
color = const(Color.White),
width = const(16.dp),
)
LineLayer(
id = "routes",
source = routes,
color = const(Color.Blue),
width = const(14.dp),
)
}
if (previewRoute!!.isNotEmpty()) {
val routes =
rememberGeoJsonSource(GeoJsonData.JsonString(previewRoute))
LineLayer(
id = "routes-casing-pre",
source = routes,
color = const(Color.White),
width = const(6.dp),
)
LineLayer(
id = "routes-pre",
source = routes,
color = const(Color.Cyan),
width = const(4.dp),
)
}
}
@Composable
fun BuildingLayer(tiles: Source) {
Anchor.Replace("building-3d") {
FillLayer(
id = "remove-building",
visible = false,
source = tiles,
sourceLayer = "building"
)
}
}
@Composable
fun DrawImage(location: Location) {
val textMeasurer = rememberTextMeasurer()
val vector = ImageVector.vectorResource(id = R.drawable.assistant_navigation_48px)
val painter = rememberVectorPainter(image = vector)
val tint = remember { ColorFilter.tint(Color.Blue) }
Box(
modifier = Modifier
.padding(start = 450.dp - 30.dp, top = 350.dp - 30.dp)
.drawBehind {
with(painter) {
draw(
size = Size(60F, 60F),
colorFilter = tint
)
}
})
Box(
modifier = Modifier
.size(30.dp, 30.dp)
.padding(start = 650.dp, top = 350.dp)
.drawWithCache {
val measuredText =
textMeasurer.measure(
AnnotatedString("${(location.speed * 3.6).toInt()}"),
style = TextStyle(fontSize = 22.sp)
)
onDrawBehind {
drawCircle(
Color.LightGray, radius = 30.dp.toPx(), center = Offset(5f, 10f)
)
drawText(measuredText)
}
}
.fillMaxSize()
)
}
@Composable
fun Puck(cameraState: CameraState, location: Location) {
LocationPuck(
idPrefix = "user-location",
locationState = location,
cameraState = cameraState,
accuracyThreshold = 10f,
showBearing = false,
sizes = LocationPuckSizes(dotRadius = 10.dp),
colors = LocationPuckColors(
dotFillColorCurrentLocation = Color.Cyan,
accuracyStrokeColor = Color.Green
)
)
}
fun getPaddingValues(preView: Boolean): PaddingValues {
val padding = PaddingValues(start = 100.dp, top = 300.dp)
val prePadding = PaddingValues(start = 150.dp, bottom = 0.dp)
return if (preView) {
prePadding
} else {
padding
}
}

View File

@@ -24,6 +24,10 @@ import com.kouros.navigation.car.screen.RequestPermissionScreen
import com.kouros.navigation.car.screen.SearchScreen
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.ObjectBox
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class NavigationSession : Session() {
val uriScheme = "samples";
@@ -31,16 +35,21 @@ class NavigationSession : Session() {
val uriHost = "navigation";
lateinit var routeModel: RouteCarModel;
lateinit var navigationScreen: NavigationScreen
lateinit var surfaceRenderer: SurfaceRenderer
var locationIndex = 0
val test = true
lateinit var navigationScreen: NavigationScreen
lateinit var surfaceRenderer: SurfaceRenderer
var locationIndex = 100
val simulate = true
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
updateLocation(location)
}
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
private val mLifeCycleObserver: LifecycleObserver = object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
Log.i(TAG, "In onCreate()")
@@ -150,7 +159,7 @@ class NavigationSession : Session() {
updateLocation(location)
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
/* minTimeMs= */ 1000,
/* minTimeMs= */ 10,
/* minDistanceM= */ 0f,
mLocationListener
)
@@ -158,26 +167,31 @@ class NavigationSession : Session() {
fun updateLocation(location: Location?) {
if (location != null) {
if (test) {
test(location)
if (simulate) {
simulate(location)
} else {
update(location)
}
}
}
fun test(location: Location?) {
fun simulate(location: Location?) {
if (routeModel.isNavigating() && locationIndex < routeModel.route.waypoints.size) {
val loc = routeModel.route.waypoints[locationIndex]
val curLocation = Location(LocationManager.GPS_PROVIDER)
curLocation.longitude = loc[0] + 0.0003
curLocation.latitude = loc[1] + 0.0002
update(curLocation)
locationIndex += 1
if (locationIndex > routeModel.route.waypoints.size) {
val locationManager =
carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.removeUpdates(mLocationListener)
coroutineScope.launch {
delay(
100
)
val loc = routeModel.route.waypoints[locationIndex]
val curLocation = Location(LocationManager.GPS_PROVIDER)
curLocation.longitude = loc[0] + 0.0003
curLocation.latitude = loc[1] + 0.0002
update(curLocation)
locationIndex += 1
if (locationIndex > routeModel.route.waypoints.size) {
val locationManager =
carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.removeUpdates(mLocationListener)
}
}
} else {
update(location = location!!)

View File

@@ -1,6 +1,7 @@
package com.kouros.navigation.car
import android.app.Presentation
import android.content.res.Resources.getSystem
import android.graphics.Rect
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
@@ -11,14 +12,11 @@ import androidx.car.app.AppManager
import androidx.car.app.CarContext
import androidx.car.app.SurfaceCallback
import androidx.car.app.SurfaceContainer
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
@@ -33,29 +31,21 @@ import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
import com.kouros.navigation.utils.NavigationUtils.snapLocation
import com.kouros.navigation.utils.calculateZoom
import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.rememberCameraState
import org.maplibre.compose.expressions.dsl.const
import org.maplibre.compose.layers.Anchor
import org.maplibre.compose.layers.FillLayer
import org.maplibre.compose.layers.LineLayer
import org.maplibre.compose.location.LocationPuckColors
import org.maplibre.compose.location.LocationPuckSizes
import org.maplibre.compose.camera.CameraState
import org.maplibre.compose.map.MaplibreMap
import org.maplibre.compose.sources.GeoJsonData
import org.maplibre.compose.sources.Source
import org.maplibre.compose.sources.getBaseSource
import org.maplibre.compose.sources.rememberGeoJsonSource
import org.maplibre.compose.style.BaseStyle
import org.maplibre.spatialk.geojson.Position
import kotlin.math.absoluteValue
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
class SurfaceRenderer(
carContext: CarContext, lifecycle: Lifecycle,
private var carContext: CarContext, lifecycle: Lifecycle,
private var routeModel: RouteCarModel
) : DefaultLifecycleObserver {
private val mCarContext: CarContext = carContext
var lastLocation = Location(LocationManager.GPS_PROVIDER)
val cameraPosition = MutableLiveData(
CameraPosition(
@@ -63,6 +53,19 @@ class SurfaceRenderer(
target = Position(latitude = 48.1857475, longitude = 11.5793627)
)
)
var visibleArea = MutableLiveData(
Rect()
)
var stableArea = Rect()
var containerWidth = 0
var containerHeight = 0
var containerDpi = 1
var lastBearing = 0.0
val routeData = MutableLiveData("")
val previewRouteData = MutableLiveData("")
@@ -74,9 +77,6 @@ class SurfaceRenderer(
var panView = false
val tilt = 55.0
val padding = PaddingValues(start = 150.dp, top = 250.dp)
val prePadding = PaddingValues(start = 150.dp, bottom = 0.dp)
val mSurfaceCallback: SurfaceCallback = object : SurfaceCallback {
@@ -107,6 +107,18 @@ class SurfaceRenderer(
0
)
containerWidth = surfaceContainer.width
containerHeight = surfaceContainer.height
if (surfaceContainer.dpi != 0) {
containerDpi = surfaceContainer.dpi
}
println(surfaceContainer.toString())
println(containerDpi)
println(carContext.resources.displayMetrics.density)
println(carContext.resources.displayMetrics.densityDpi)
println(getSystem().displayMetrics.density)
println(getSystem().displayMetrics.densityDpi)
mapView = ComposeView(carContext).apply {
this.setViewTreeLifecycleOwner(lifecycleOwner)
this.setViewTreeSavedStateRegistryOwner(lifecycleOwner)
@@ -120,13 +132,15 @@ class SurfaceRenderer(
}
}
override fun onVisibleAreaChanged(visibleArea: Rect) {
override fun onVisibleAreaChanged(newVisibleArea: Rect) {
synchronized(this@SurfaceRenderer) {
visibleArea.value = newVisibleArea
}
}
override fun onStableAreaChanged(stableArea: Rect) {
override fun onStableAreaChanged(newStableArea: Rect) {
synchronized(this@SurfaceRenderer) {
stableArea = newStableArea
}
}
@@ -143,7 +157,6 @@ class SurfaceRenderer(
}
override fun onScroll(distanceX: Float, distanceY: Float) {
Log.i(TAG, "onScroll")
synchronized(this@SurfaceRenderer) {
}
}
@@ -162,49 +175,41 @@ class SurfaceRenderer(
val position: CameraPosition? by cameraPosition.observeAsState()
val route: String? by routeData.observeAsState()
val previewRoute: String? by previewRouteData.observeAsState()
val cameraState =
rememberCameraState(
firstPosition =
CameraPosition(
target = Position(
latitude = position!!.target.latitude,
longitude = position!!.target.longitude
),
zoom = 15.0,
tilt = tilt,
padding = getPaddingValues()
)
)
val cameraState = cameraState(position, tilt, preview)
MaplibreMap(
cameraState = cameraState,
baseStyle = BaseStyle.Uri(Constants.STYLE),
) {
getBaseSource(id = "openmaptiles")?.let { tiles ->
BuildingLayer(tiles)
if (!getBooleanKeyValue(context = carContext, SHOW_THREED_BUILDING)
&& Constants.STYLE.contains("liberty")) {
BuildingLayer(tiles)
}
RouteLayer(route, previewRoute)
}
LocationPuck(
idPrefix = "user-location",
locationState = lastLocation,
cameraState = cameraState,
accuracyThreshold = 10f,
sizes = LocationPuckSizes(dotRadius = 10.dp),
colors = LocationPuckColors(accuracyStrokeColor = Color.Green)
)
//Puck(cameraState, lastLocation)
}
ShowPosition(cameraState, position)
}
@Composable
fun ShowPosition(cameraState: CameraState, position: CameraPosition?) {
if (!preview) {
DrawImage(lastLocation)
val cameraDuration = duration(position)
LaunchedEffect(position) {
cameraState.animateTo(
finalPosition = CameraPosition(
bearing = position!!.bearing,
zoom = position!!.zoom,
target = position!!.target,
zoom = position.zoom,
target = position.target,
tilt = tilt,
padding = getPaddingValues()
padding = getPaddingValues(preview)
),
duration = 3.seconds
duration = cameraDuration
)
}
} else {
@@ -212,10 +217,10 @@ class SurfaceRenderer(
cameraState.animateTo(
finalPosition = CameraPosition(
bearing = 0.0,
zoom = 9.0,
zoom = 11.0,
target = Position(centerLocation.longitude, centerLocation.latitude),
tilt = 0.0,
padding = getPaddingValues()
padding = getPaddingValues(preview)
),
duration = 3.seconds
)
@@ -223,62 +228,23 @@ class SurfaceRenderer(
}
}
@Composable
fun BuildingLayer(tiles: Source) {
if (!getBooleanKeyValue(context = mCarContext, SHOW_THREED_BUILDING)) {
Anchor.Replace("building-3d") {
FillLayer(
id = "remove-building",
visible = false,
source = tiles,
sourceLayer = "building"
)
}
}
}
@Composable
fun RouteLayer(routeData: String?, previewRoute: String?) {
if (routeData!!.isNotEmpty()) {
val routes =
rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
LineLayer(
id = "routes-casing",
source = routes,
color = const(Color.White),
width = const(16.dp),
)
LineLayer(
id = "routes",
source = routes,
color = const(Color.Blue),
width = const(14.dp),
)
}
if (previewRoute!!.isNotEmpty()) {
val routes =
rememberGeoJsonSource(GeoJsonData.JsonString(previewRoute))
LineLayer(
id = "routes-casing-pre",
source = routes,
color = const(Color.White),
width = const(6.dp),
)
LineLayer(
id = "routes-pre",
source = routes,
color = const(Color.Cyan),
width = const(4.dp),
)
}
}
override fun onCreate(owner: LifecycleOwner) {
Log.i(TAG, "SurfaceRenderer created")
mCarContext.getCarService(AppManager::class.java)
carContext.getCarService(AppManager::class.java)
.setSurfaceCallback(mSurfaceCallback)
}
private fun duration(position: CameraPosition?): Duration {
val cameraDuration = if ((lastBearing - position!!.bearing).absoluteValue > 20.0) {
0.4.seconds
} else {
1.seconds
}
return cameraDuration
}
/** Handles the map zoom-in and zoom-out events. */
fun handleScale(zoomSign: Int) {
synchronized(this) {
@@ -304,32 +270,34 @@ class SurfaceRenderer(
var bearing: Double
if (routeModel.isNavigating()) {
snapedLocation = snapLocation(location, routeModel.route.maneuverLocations())
bearing = routeModel.currentStep().bearing
} else {
bearing = cameraPosition.value!!.bearing
if (lastLocation.latitude != snapedLocation.latitude) {
if (lastLocation.distanceTo(snapedLocation) > 5) {
bearing = lastLocation.bearingTo(snapedLocation).toDouble()
}
// stimmt nicht
//bearing = routeModel.currentStep().bearing
}
bearing = cameraPosition.value!!.bearing
if (lastLocation.latitude != snapedLocation.latitude) {
if (lastLocation.distanceTo(snapedLocation) > 5) {
bearing = lastLocation.bearingTo(snapedLocation).toDouble()
}
}
val zoom = if (!panView) {
calculateZoom(snapedLocation.speed.toDouble())
} else {
cameraPosition.value!!.zoom
}
lastBearing = cameraPosition.value!!.bearing
cameraPosition.postValue(
cameraPosition.value!!.copy(
bearing = bearing,
zoom = zoom,
padding = getPaddingValues(),
padding = getPaddingValues(preview),
target = Position(snapedLocation.longitude, snapedLocation.latitude),
)
)
lastLocation = snapedLocation
} else {
val bearing = 0.0
val zoom = 11.0
val zoom = 14.0
cameraPosition.postValue(
cameraPosition.value!!.copy(
bearing = bearing,
@@ -342,7 +310,6 @@ class SurfaceRenderer(
}
}
fun setRouteData() {
routeData.value = routeModel.route.routeGeoJson
preview = false
@@ -355,17 +322,10 @@ class SurfaceRenderer(
preview = true
}
fun getPaddingValues(): PaddingValues {
return if (preview) {
prePadding
} else {
padding
}
}
companion
object {
private const val TAG = "MapRenderer"
}
}

View File

@@ -63,7 +63,7 @@ class NavigationScreen(
)
actionStripBuilder.addAction(
Action.Builder()
.setIcon(routeModel.createCarIcon(carContext, R.drawable.ic_favorite_white_24dp))
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_24px))
.setOnClickListener {
screenManager.push(SettingsScreen(carContext))
}
@@ -91,8 +91,8 @@ class NavigationScreen(
.build()
)
.setOnClickListener {
surfaceRenderer.routeData.postValue("")
routeModel.stopNavigation()
surfaceRenderer.routeData.postValue("")
invalidate()
}
.build()

View File

@@ -21,7 +21,6 @@ import android.location.LocationManager
import android.os.CountDownTimer
import android.text.SpannableString
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
@@ -164,9 +163,8 @@ class RoutePreviewScreen(
)
.build()
val timer = object: CountDownTimer(10000, 10000) {
val timer = object: CountDownTimer(10000, 15000) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
onNavigate()
}
@@ -223,18 +221,17 @@ class RoutePreviewScreen(
fun getMapActionStrip(): ActionStrip {
return ActionStrip.Builder()
.addAction(
createToastAction(R.drawable.ic_zoom_in_24, R.string.zoomed_in_toast_msg)
createToastAction(R.drawable.ic_zoom_in_24)
)
.addAction(
createToastAction(R.drawable.ic_zoom_out_24, R.string.zoomed_out_toast_msg)
createToastAction(R.drawable.ic_zoom_out_24)
)
.addAction(Action.PAN)
.build()
}
private fun createToastAction(
@DrawableRes iconRes: Int,
@StringRes toastStringRes: Int
@DrawableRes iconRes: Int
): Action {
return Action.Builder()
.setOnClickListener { surfaceRenderer.handleScale(-1) }

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M321,668L480,596L639,668L644,663L480,266L316,663L321,668ZM480,880Q398,880 325,848.5Q252,817 197.5,762.5Q143,708 111.5,635Q80,562 80,480Q80,397 111.5,324Q143,251 197.5,197Q252,143 325,111.5Q398,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,562 848.5,635Q817,708 763,762.5Q709,817 636,848.5Q563,880 480,880ZM480,820Q622,820 721,720.5Q820,621 820,480Q820,338 721,239Q622,140 480,140Q339,140 239.5,239Q140,338 140,480Q140,621 239.5,720.5Q339,820 480,820ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M321,668L480,596L639,668L644,663L480,266L316,663L321,668ZM480,880Q398,880 325,848.5Q252,817 197.5,762.5Q143,708 111.5,635Q80,562 80,480Q80,397 111.5,324Q143,251 197.5,197Q252,143 325,111.5Q398,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,562 848.5,635Q817,708 763,762.5Q709,817 636,848.5Q563,880 480,880ZM480,820Q622,820 721,720.5Q820,621 820,480Q820,338 721,239Q622,140 480,140Q339,140 239.5,239Q140,338 140,480Q140,621 239.5,720.5Q339,820 480,820ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</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="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M370,880L354,752Q341,747 329.5,740Q318,733 307,725L188,775L78,585L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L78,375L188,185L307,235Q318,227 330,220Q342,213 354,208L370,80L590,80L606,208Q619,213 630.5,220Q642,227 653,235L772,185L882,375L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L881,585L771,775L653,725Q642,733 630,740Q618,747 606,752L590,880L370,880ZM440,800L519,800L533,694Q564,686 590.5,670.5Q617,655 639,633L738,674L777,606L691,541Q696,527 698,511.5Q700,496 700,480Q700,464 698,448.5Q696,433 691,419L777,354L738,286L639,328Q617,305 590.5,289.5Q564,274 533,266L520,160L441,160L427,266Q396,274 369.5,289.5Q343,305 321,327L222,286L183,354L269,418Q264,433 262,448Q260,463 260,480Q260,496 262,511Q264,526 269,541L183,606L222,674L321,632Q343,655 369.5,670.5Q396,686 427,694L440,800ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620ZM480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Z"/>
</vector>

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1E1D1D">
<ImageView
android:id="@+id/imageView"
android:layout_width="158dp"
android:layout_height="123dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="32dp"
android:src="@drawable/ic_launcher" />
</RelativeLayout>