Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
TeamPrinterV2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Aleksandr
TeamPrinterV2
Commits
4bf53d84
Commit
4bf53d84
authored
Jul 23, 2024
by
Aleksandr
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Template for Print job details
parent
33b43604
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
322 additions
and
121 deletions
+322
-121
app/src/main/java/com/isidroid/c23/Errors.kt
+2
-0
app/src/main/java/com/isidroid/c23/constant/Argument.kt
+10
-1
app/src/main/java/com/isidroid/c23/data/mapper/PrintJobListItemMapper.kt
+4
-0
app/src/main/java/com/isidroid/c23/domain/dto/PrintJobListItem.kt
+5
-1
app/src/main/java/com/isidroid/c23/domain/use_case/DetailsUseCase.kt
+28
-1
app/src/main/java/com/isidroid/c23/ui/screen/details/JobDetailsContract.kt
+5
-1
app/src/main/java/com/isidroid/c23/ui/screen/details/JobDetailsScreen.kt
+19
-42
app/src/main/java/com/isidroid/c23/ui/screen/details/JobDetailsViewModel.kt
+28
-10
app/src/main/java/com/isidroid/c23/ui/screen/details/component/DisplayMapRouteConfirmation.kt
+57
-0
app/src/main/java/com/isidroid/c23/ui/screen/details/component/v1.kt
+0
-37
app/src/main/java/com/isidroid/c23/ui/screen/details/component/v2.kt
+81
-22
app/src/main/java/com/isidroid/c23/ui/screen/map/MapScreen.kt
+1
-1
app/src/main/java/com/isidroid/c23/ui/screen/map/_components/RequestLocationPermissionComponent.kt
+2
-2
app/src/test/java/com/isidroid/c23/ExampleUnitTest.kt
+25
-0
library/location/src/google/java/com/isidroid/location/repository/LocationRepositoryImpl.kt
+14
-3
library/location/src/main/java/com/isidroid/location/ext/ExtLocation.kt
+39
-0
library/location/src/main/java/com/isidroid/location/repository/LocationRepository.kt
+2
-0
No files found.
app/src/main/java/com/isidroid/c23/Errors.kt
View file @
4bf53d84
...
...
@@ -2,3 +2,4 @@ package com.isidroid.c23
class
SpotHasNoPrintProfilesException
(
m
:
String
?
=
null
):
Throwable
(
m
)
class
JobNotFoundException
(
m
:
String
?
=
null
):
Throwable
(
m
)
class
NoLocationPermissionException
(
m
:
String
?
=
null
):
Throwable
(
m
)
\ No newline at end of file
app/src/main/java/com/isidroid/c23/constant/Argument.kt
View file @
4bf53d84
...
...
@@ -3,7 +3,15 @@ package com.isidroid.c23.constant
import
androidx.annotation.StringDef
@Retention
(
AnnotationRetention
.
SOURCE
)
@StringDef
(
Argument
.
URI
,
Argument
.
LATITUDE
,
Argument
.
LONGITUDE
,
Argument
.
SPOT_CODE
,
Argument
.
ID
,
Argument
.
INFO
,
Argument
.
MARKER
)
@StringDef
(
Argument
.
URI
,
Argument
.
LATITUDE
,
Argument
.
LONGITUDE
,
Argument
.
SPOT_CODE
,
Argument
.
ID
,
Argument
.
INFO
,
Argument
.
MARKER
,
)
annotation
class
Argument
{
companion
object
{
const
val
URI
=
"URI"
...
...
@@ -13,5 +21,6 @@ annotation class Argument {
const
val
ID
=
"ID"
const
val
INFO
=
"INFO"
const
val
MARKER
=
"MARKER"
const
val
DISTANCE
=
"DISTANCE"
}
}
app/src/main/java/com/isidroid/c23/data/mapper/PrintJobListItemMapper.kt
View file @
4bf53d84
...
...
@@ -15,6 +15,7 @@ fun PrintJob.createListItem(context: Context, richSpot: RichSpot?): PrintJobList
id
=
id
,
spotCode
=
richSpot
?.
spot
?.
code
,
spotName
=
richSpot
?.
spot
?.
name
?:
"Deleted spot"
,
spotAddress
=
richSpot
?.
spot
?.
address
?:
"Deleted spot"
,
cost
=
cost
,
paperInfo
=
printSize
.
printSizeName
,
isColor
=
profile
?.
grayscale
!=
true
,
...
...
@@ -25,5 +26,7 @@ fun PrintJob.createListItem(context: Context, richSpot: RichSpot?): PrintJobList
statusName
=
context
.
getString
(
getPrintJobStatus
(
status
)),
accessCode
=
accessCode
.
orEmpty
(),
createdAt
=
createdAt
,
profileName
=
profile
?.
name
,
profileCost
=
profile
?.
cost
)
}
\ No newline at end of file
app/src/main/java/com/isidroid/c23/domain/dto/PrintJobListItem.kt
View file @
4bf53d84
...
...
@@ -7,6 +7,7 @@ data class PrintJobListItem(
val
id
:
String
,
val
spotName
:
String
,
val
spotCode
:
String
?,
val
spotAddress
:
String
?,
val
cost
:
Float
,
val
paperInfo
:
String
,
val
isColor
:
Boolean
,
...
...
@@ -16,5 +17,7 @@ data class PrintJobListItem(
val
statusColor
:
Color
,
val
cover
:
String
?,
val
accessCode
:
String
,
val
createdAt
:
Date
val
createdAt
:
Date
,
val
profileName
:
String
?,
val
profileCost
:
Float
?
)
\ No newline at end of file
app/src/main/java/com/isidroid/c23/domain/use_case/DetailsUseCase.kt
View file @
4bf53d84
package
com.isidroid.c23.domain.use_case
import
android.Manifest
import
android.content.Context
import
androidx.compose.ui.text.intl.Locale
import
com.isidroid.c23.JobNotFoundException
import
com.isidroid.c23.NoLocationPermissionException
import
com.isidroid.c23.constant.Argument
import
com.isidroid.c23.data.mapper.createListItem
import
com.isidroid.c23.ext.isDebug
import
com.isidroid.core.FlowResult
import
com.isidroid.job.repository.JobRepository
import
com.isidroid.location.ext.isMileUnit
import
com.isidroid.location.repository.LocationRepository
import
com.isidroid.spot.repository.SpotRepository
import
com.isidroid.ui.maps.ext.addKilometerToLatLng
import
com.isidroid.ui.maps.model.MapMarker
import
com.isidroid.utils.hasPermission
import
dagger.hilt.android.qualifiers.ApplicationContext
import
kotlinx.coroutines.flow.flow
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Singleton
import
kotlin.math.round
@Singleton
class
DetailsUseCase
@Inject
constructor
(
...
...
@@ -26,11 +33,30 @@ class DetailsUseCase @Inject constructor(
fun
loadDetails
(
id
:
String
)
=
flow
{
emit
(
FlowResult
.
Loading
)
val
hasLocationPermission
=
context
.
hasPermission
(
Manifest
.
permission
.
ACCESS_FINE_LOCATION
)
if
(!
hasLocationPermission
)
{
throw
NoLocationPermissionException
()
}
val
job
=
jobRepository
.
readLocalList
(
ids
=
listOf
(
id
)).
firstOrNull
()
?:
throw
JobNotFoundException
()
val
richSpot
=
spotRepository
.
findLocalRichSpots
(
ids
=
listOf
(
job
.
spotId
))
?.
firstOrNull
()
val
result
=
job
.
createListItem
(
context
=
context
,
richSpot
=
richSpot
)
val
currentLocation
=
locationRepository
.
getCurrentLocation
()
val
distanceKm
=
locationRepository
.
findDistance
(
currentLocation
.
first
,
currentLocation
.
second
,
richSpot
?.
spot
?.
lat
,
richSpot
?.
spot
?.
lng
)
?.
let
{
(
round
(
it
*
100
)
/
100
)
}
val
distance
=
buildString
{
distanceKm
?.
let
{
append
(
"$distanceKm "
)
}
if
(
Locale
.
current
.
isMileUnit
)
append
(
"miles"
)
else
append
(
"km"
)
}
val
location
=
if
(
isDebug
()
&&
richSpot
?.
spot
!=
null
)
addKilometerToLatLng
(
richSpot
.
spot
.
lat
,
richSpot
.
spot
.
lng
,
distance
=
0.2
)
...
...
@@ -47,7 +73,8 @@ class DetailsUseCase @Inject constructor(
Argument
.
INFO
to
result
,
Argument
.
LATITUDE
to
location
.
first
,
Argument
.
LONGITUDE
to
location
.
second
,
Argument
.
MARKER
to
mapMarker
Argument
.
MARKER
to
mapMarker
,
Argument
.
DISTANCE
to
distance
)
)
)
...
...
app/src/main/java/com/isidroid/c23/ui/screen/details/JobDetailsContract.kt
View file @
4bf53d84
...
...
@@ -11,6 +11,7 @@ class JobDetailsContract {
data
object
OpenConfirmationMapRoute
:
Event
data
object
DismissBuildRouteConfirmation
:
Event
data
object
OpenNavigationApp
:
Event
data
object
LocationPermissionGranted
:
Event
}
sealed
interface
Effect
:
ViewSideEffect
{
...
...
@@ -25,6 +26,8 @@ class JobDetailsContract {
val
printJob
:
PrintJobListItem
?
=
null
,
val
lat
:
Double
?
=
null
,
val
lng
:
Double
?
=
null
,
val
routeConfirmationVisible
:
Boolean
=
false
val
routeConfirmationVisible
:
Boolean
=
false
,
val
requestLocationPermission
:
Boolean
=
false
,
val
distance
:
String
?
=
null
)
:
ViewState
}
\ No newline at end of file
app/src/main/java/com/isidroid/c23/ui/screen/details/JobDetailsScreen.kt
View file @
4bf53d84
package
com.isidroid.c23.ui.screen.details
import
androidx.activity.compose.BackHandler
import
androidx.compose.foundation.background
import
androidx.compose.foundation.layout.Box
import
androidx.compose.foundation.layout.WindowInsets
import
androidx.compose.foundation.layout.consumeWindowInsets
import
androidx.compose.foundation.layout.fillMaxSize
import
androidx.compose.foundation.layout.fillMaxWidth
import
androidx.compose.foundation.layout.padding
import
androidx.compose.foundation.layout.statusBars
import
androidx.compose.foundation.shape.RoundedCornerShape
import
androidx.compose.material3.Card
import
androidx.compose.material3.CardDefaults
import
androidx.compose.material3.ExperimentalMaterial3Api
import
androidx.compose.material3.ModalBottomSheet
import
androidx.compose.material3.Scaffold
import
androidx.compose.material3.Text
import
androidx.compose.material3.TextButton
import
androidx.compose.material3.TopAppBarDefaults
import
androidx.compose.material3.rememberModalBottomSheetState
import
androidx.compose.runtime.Composable
import
androidx.compose.runtime.LaunchedEffect
import
androidx.compose.runtime.State
import
androidx.compose.runtime.getValue
import
androidx.compose.ui.Alignment
import
androidx.compose.ui.Modifier
import
androidx.compose.ui.draw.clip
import
androidx.compose.ui.graphics.Color
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.constraintlayout.compose.ConstraintLayout
import
androidx.constraintlayout.compose.Dimension
import
com.airbnb.lottie.compose.LottieAnimation
import
com.airbnb.lottie.compose.LottieCompositionSpec
import
com.airbnb.lottie.compose.LottieConstants
import
com.airbnb.lottie.compose.rememberLottieComposition
import
com.isidroid.c23.R
import
com.isidroid.c23.ui._component.TopAppBarComponent
import
com.isidroid.c23.ui.screen.details.component.DetailsV1
import
com.isidroid.c23.ui.screen.details.component.DetailsV2
import
com.isidroid.c23.ui.screen.details.component.PrintCodeComponent
import
com.isidroid.c23.ui.screen.map.MapContract
import
com.isidroid.c23.ui.screen.map._components.TPMapComponent
import
com.isidroid.c23.ui.screen.map._components.RequestLocationPermissionComponent
import
com.isidroid.core.vm.SIDE_EFFECTS_KEY
import
com.isidroid.ui.maps.model.MapMarker
import
kotlinx.coroutines.flow.Flow
...
...
@@ -59,7 +21,21 @@ fun JobDetailsScreen(
onNavigationRequested
:
(
navigationEffect
:
JobDetailsContract
.
Effect
.
Navigation
)
->
Unit
,
modifier
:
Modifier
=
Modifier
,
)
{
DetailsV1
(
state
,
effectFlow
,
spotResultStateFlow
,
onEventSent
,
onNavigationRequested
,
modifier
)
// DetailsV2(state, effectFlow, spotResultStateFlow, onEventSent, onNavigationRequested, modifier)
}
RequestLocationPermissionComponent
(
requestLocationPermission
=
state
.
value
.
requestLocationPermission
,
onGranted
=
{
onEventSent
(
JobDetailsContract
.
Event
.
LocationPermissionGranted
)
}
)
LaunchedEffect
(
SIDE_EFFECTS_KEY
)
{
effectFlow
?.
collect
{
effect
->
when
(
effect
)
{
is
JobDetailsContract
.
Effect
.
Navigation
->
onNavigationRequested
(
effect
)
}
}
}
BackHandler
{
onEventSent
(
JobDetailsContract
.
Event
.
ToBack
)
}
// DetailsV1(state, effectFlow, spotResultStateFlow, onEventSent, onNavigationRequested, modifier)
DetailsV2
(
state
,
spotResultStateFlow
,
onEventSent
,
modifier
)
}
\ No newline at end of file
app/src/main/java/com/isidroid/c23/ui/screen/details/JobDetailsViewModel.kt
View file @
4bf53d84
...
...
@@ -4,6 +4,7 @@ import androidx.compose.material.icons.Icons
import
androidx.compose.material.icons.outlined.AccountCircle
import
androidx.lifecycle.SavedStateHandle
import
androidx.lifecycle.viewModelScope
import
com.isidroid.c23.NoLocationPermissionException
import
com.isidroid.c23.constant.Argument
import
com.isidroid.c23.domain.dto.PrintJobListItem
import
com.isidroid.c23.domain.use_case.DetailsUseCase
...
...
@@ -17,9 +18,9 @@ import kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.flow.MutableStateFlow
import
kotlinx.coroutines.flow.asStateFlow
import
kotlinx.coroutines.flow.filterNotNull
import
kotlinx.coroutines.flow.firstOrNull
import
kotlinx.coroutines.flow.flowOn
import
kotlinx.coroutines.flow.onEach
import
kotlinx.coroutines.flow.singleOrNull
import
kotlinx.coroutines.launch
import
javax.inject.Inject
...
...
@@ -31,12 +32,17 @@ class JobDetailsViewModel @Inject constructor(
private
val
_spotResultStateFlow
=
MutableStateFlow
<
List
<
MapMarker
>>(
emptyList
())
val
spotResultStateFlow
=
_spotResultStateFlow
.
asStateFlow
()
private
lateinit
var
jobId
:
String
init
{
viewModelScope
.
launch
{
savedStateHandle
.
getStateFlow
<
String
?>(
Argument
.
ID
,
null
)
.
filterNotNull
()
.
onEach
{
loadDetails
(
it
)
}
.
firstOrNull
()
.
onEach
{
jobId
=
it
loadDetails
()
}
.
singleOrNull
()
}
}
...
...
@@ -49,14 +55,19 @@ class JobDetailsViewModel @Inject constructor(
JobDetailsContract
.
Event
.
OpenConfirmationMapRoute
->
setState
{
copy
(
routeConfirmationVisible
=
true
)
}
JobDetailsContract
.
Event
.
DismissBuildRouteConfirmation
->
setState
{
copy
(
routeConfirmationVisible
=
false
)
}
JobDetailsContract
.
Event
.
OpenNavigationApp
->
openNavigationApp
()
JobDetailsContract
.
Event
.
LocationPermissionGranted
->
locationPermissionGranted
()
}
}
// handle events
private
suspend
fun
loadDetails
(
id
:
String
)
{
useCase
.
loadDetails
(
i
d
)
private
suspend
fun
loadDetails
()
{
useCase
.
loadDetails
(
jobI
d
)
.
flowOn
(
Dispatchers
.
IO
)
.
catchTimber
{
}
.
catchTimber
{
when
(
it
)
{
is
NoLocationPermissionException
->
setState
{
copy
(
requestLocationPermission
=
true
)
}
}
}
.
collect
{
res
->
when
(
res
)
{
FlowResult
.
Loading
->
setState
{
copy
(
isLoading
=
true
)
}
...
...
@@ -64,21 +75,27 @@ class JobDetailsViewModel @Inject constructor(
item
=
res
.
result
[
Argument
.
INFO
]
as
?
PrintJobListItem
,
lat
=
res
.
result
[
Argument
.
LATITUDE
]
as
?
Double
,
lng
=
res
.
result
[
Argument
.
LONGITUDE
]
as
?
Double
,
mapMarker
=
res
.
result
[
Argument
.
MARKER
]
as
?
MapMarker
mapMarker
=
res
.
result
[
Argument
.
MARKER
]
as
?
MapMarker
,
distance
=
res
.
result
[
Argument
.
DISTANCE
]
as
?
String
)
}
}
}
private
suspend
fun
onDetails
(
item
:
PrintJobListItem
?,
lat
:
Double
?,
lng
:
Double
?,
mapMarker
:
MapMarker
?)
{
private
suspend
fun
onDetails
(
item
:
PrintJobListItem
?,
lat
:
Double
?,
lng
:
Double
?,
mapMarker
:
MapMarker
?
,
distance
:
String
?
)
{
val
myLocation
=
MapMarker
(
id
=
"my_location"
,
lat
=
lat
?:
0.0
,
lng
=
lng
?:
0.0
,
name
=
"My location"
,
imageVector
=
Icons
.
Outlined
.
AccountCircle
)
_spotResultStateFlow
.
emit
(
listOfNotNull
(
mapMarker
,
myLocation
))
setState
{
copy
(
isLoading
=
false
,
printJob
=
item
,
lat
=
lat
,
lng
=
lng
)
}
setState
{
copy
(
isLoading
=
false
,
printJob
=
item
,
lat
=
lat
,
lng
=
lng
,
distance
=
distance
)
}
}
private
fun
openNavigationApp
(){
private
fun
openNavigationApp
()
{
setState
{
copy
(
routeConfirmationVisible
=
false
)
}
setEffect
{
JobDetailsContract
.
Effect
.
Navigation
.
ToNavigationApp
(
viewState
.
value
.
lat
,
viewState
.
value
.
lng
)
}
}
private
suspend
fun
locationPermissionGranted
()
{
setState
{
copy
(
requestLocationPermission
=
false
)
}
loadDetails
()
}
}
\ No newline at end of file
app/src/main/java/com/isidroid/c23/ui/screen/details/component/DisplayMapRouteConfirmation.kt
0 → 100644
View file @
4bf53d84
package
com.isidroid.c23.ui.screen.details.component
import
androidx.compose.foundation.layout.WindowInsets
import
androidx.compose.foundation.layout.fillMaxWidth
import
androidx.compose.foundation.layout.padding
import
androidx.compose.foundation.layout.statusBars
import
androidx.compose.material3.ExperimentalMaterial3Api
import
androidx.compose.material3.ModalBottomSheet
import
androidx.compose.material3.Text
import
androidx.compose.material3.TextButton
import
androidx.compose.material3.rememberModalBottomSheetState
import
androidx.compose.runtime.Composable
import
androidx.compose.runtime.State
import
androidx.compose.ui.Modifier
import
androidx.compose.ui.res.stringResource
import
androidx.compose.ui.text.style.TextAlign
import
androidx.compose.ui.unit.dp
import
com.isidroid.c23.R
import
com.isidroid.c23.ui.screen.details.JobDetailsContract
@OptIn
(
ExperimentalMaterial3Api
::
class
)
@Composable
internal
fun
DisplayMapRouteConfirmation
(
state
:
State
<
JobDetailsContract
.
State
>,
onEventSent
:
(
event
:
JobDetailsContract
.
Event
)
->
Unit
)
{
if
(!
state
.
value
.
routeConfirmationVisible
)
return
val
sheetState
=
rememberModalBottomSheetState
()
ModalBottomSheet
(
onDismissRequest
=
{
onEventSent
(
JobDetailsContract
.
Event
.
DismissBuildRouteConfirmation
)
},
sheetState
=
sheetState
,
windowInsets
=
WindowInsets
.
statusBars
,
modifier
=
Modifier
.
fillMaxWidth
()
.
padding
(
bottom
=
120
.
dp
),
)
{
Text
(
text
=
stringResource
(
id
=
R
.
string
.
app_navigation_explanation
),
modifier
=
Modifier
.
padding
(
horizontal
=
16
.
dp
),
textAlign
=
TextAlign
.
Center
)
TextButton
(
onClick
=
{
onEventSent
(
JobDetailsContract
.
Event
.
OpenNavigationApp
)
},
modifier
=
Modifier
.
fillMaxWidth
()
.
padding
(
horizontal
=
12
.
dp
,
vertical
=
18
.
dp
)
)
{
Text
(
text
=
"Open in Google Maps"
,
)
}
}
}
\ No newline at end of file
app/src/main/java/com/isidroid/c23/ui/screen/details/component/v1.kt
View file @
4bf53d84
...
...
@@ -172,39 +172,3 @@ private fun InformationContent(
}
}
}
\ No newline at end of file
@OptIn
(
ExperimentalMaterial3Api
::
class
)
@Composable
private
fun
DisplayMapRouteConfirmation
(
state
:
State
<
JobDetailsContract
.
State
>,
onEventSent
:
(
event
:
JobDetailsContract
.
Event
)
->
Unit
)
{
if
(!
state
.
value
.
routeConfirmationVisible
)
return
val
sheetState
=
rememberModalBottomSheetState
()
ModalBottomSheet
(
onDismissRequest
=
{
onEventSent
(
JobDetailsContract
.
Event
.
DismissBuildRouteConfirmation
)
},
sheetState
=
sheetState
,
windowInsets
=
WindowInsets
.
statusBars
,
modifier
=
Modifier
.
fillMaxWidth
()
.
padding
(
bottom
=
120
.
dp
),
)
{
Text
(
text
=
stringResource
(
id
=
R
.
string
.
app_navigation_explanation
),
modifier
=
Modifier
.
padding
(
horizontal
=
16
.
dp
),
textAlign
=
TextAlign
.
Center
)
TextButton
(
onClick
=
{
onEventSent
(
JobDetailsContract
.
Event
.
OpenNavigationApp
)
},
modifier
=
Modifier
.
fillMaxWidth
()
.
padding
(
horizontal
=
12
.
dp
,
vertical
=
18
.
dp
)
)
{
Text
(
text
=
"Open in Google Maps"
,
)
}
}
}
\ No newline at end of file
app/src/main/java/com/isidroid/c23/ui/screen/details/component/v2.kt
View file @
4bf53d84
package
com.isidroid.c23.ui.screen.details.component
import
androidx.compose.foundation.background
import
androidx.compose.foundation.layout.Box
import
androidx.compose.foundation.layout.Column
import
androidx.compose.foundation.layout.
Row
import
androidx.compose.foundation.layout.
ColumnScope
import
androidx.compose.foundation.layout.Spacer
import
androidx.compose.foundation.layout.consumeWindowInsets
import
androidx.compose.foundation.layout.fillMaxSize
import
androidx.compose.foundation.layout.fillMaxWidth
import
androidx.compose.foundation.layout.height
import
androidx.compose.foundation.layout.padding
import
androidx.compose.foundation.layout.width
import
androidx.compose.foundation.layout.wrapContentHeight
import
androidx.compose.foundation.shape.RoundedCornerShape
import
androidx.compose.material3.Button
import
androidx.compose.material3.Card
import
androidx.compose.material3.CardDefaults
import
androidx.compose.material3.ExperimentalMaterial3Api
import
androidx.compose.material3.
MaterialTheme
import
androidx.compose.material3.
HorizontalDivider
import
androidx.compose.material3.Scaffold
import
androidx.compose.material3.Surface
import
androidx.compose.material3.Text
import
androidx.compose.material3.TopAppBarDefaults
import
androidx.compose.runtime.Composable
...
...
@@ -26,30 +25,27 @@ import androidx.compose.ui.draw.clip
import
androidx.compose.ui.graphics.Color
import
androidx.compose.ui.res.stringResource
import
androidx.compose.ui.text.font.FontWeight
import
androidx.compose.ui.
tooling.preview.Preview
import
androidx.compose.ui.
unit.Dp
import
androidx.compose.ui.unit.dp
import
androidx.compose.ui.unit.sp
import
androidx.constraintlayout.compose.ConstraintLayout
import
androidx.constraintlayout.compose.Dimension
import
com.isidroid.c23.R
import
com.isidroid.c23.domain.dto.PrintJobListItem
import
com.isidroid.c23.ui._component.TopAppBarComponent
import
com.isidroid.c23.ui.screen.details.JobDetailsContract
import
com.isidroid.c23.ui.screen.map.MapContract
import
com.isidroid.c23.ui.screen.map._components.TPMapComponent
import
com.isidroid.ui.maps.model.MapMarker
import
com.isidroid.utils.asCost
import
com.isidroid.utils.spaceInCenter
import
kotlinx.coroutines.flow.Flow
import
kotlinx.coroutines.flow.StateFlow
import
java.text.DateFormat
import
java.text.SimpleDateFormat
@OptIn
(
ExperimentalMaterial3Api
::
class
)
@Composable
fun
DetailsV2
(
state
:
State
<
JobDetailsContract
.
State
>,
effectFlow
:
Flow
<
JobDetailsContract
.
Effect
>?,
spotResultStateFlow
:
StateFlow
<
List
<
MapMarker
>>,
onEventSent
:
(
event
:
JobDetailsContract
.
Event
)
->
Unit
,
onNavigationRequested
:
(
navigationEffect
:
JobDetailsContract
.
Effect
.
Navigation
)
->
Unit
,
modifier
:
Modifier
=
Modifier
,
)
{
val
printJob
=
state
.
value
.
printJob
?:
return
...
...
@@ -69,10 +65,33 @@ fun DetailsV2(
.
padding
(
top
=
paddingValues
.
calculateTopPadding
())
.
padding
(
horizontal
=
16
.
dp
)
)
{
val
textStrings
=
arrayOf
(
PrintInfoComponent
(
printJob
)
HorizontalDivider
(
Modifier
.
padding
(
vertical
=
16
.
dp
))
SpotInfoComponent
(
lat
=
state
.
value
.
lat
,
lng
=
state
.
value
.
lng
,
distance
=
state
.
value
.
distance
,
printJob
=
printJob
,
spotResultStateFlow
=
spotResultStateFlow
,
onEventSent
=
onEventSent
,
paddingBottom
=
paddingValues
.
calculateBottomPadding
()
)
}
DisplayMapRouteConfirmation
(
state
,
onEventSent
)
}
}
@Composable
private
fun
PrintInfoComponent
(
printJob
:
PrintJobListItem
)
{
val
textStrings
=
listOfNotNull
(
"PIN code: ${printJob.accessCode.spaceInCenter()}"
,
"${printJob.cost.asCost()} | Unpaid"
,
DateFormat
.
getDateTimeInstance
(
DateFormat
.
LONG
,
DateFormat
.
SHORT
).
format
(
printJob
.
createdAt
)
"${printJob.profileCost?.asCost()} per page"
,
DateFormat
.
getDateTimeInstance
(
DateFormat
.
LONG
,
DateFormat
.
SHORT
).
format
(
printJob
.
createdAt
),
"copies: ${printJob.copies}"
,
printJob
.
profileName
,
)
for
(
text
in
textStrings
)
{
...
...
@@ -90,18 +109,57 @@ fun DetailsV2(
.
background
(
printJob
.
statusColor
)
.
padding
(
horizontal
=
6
.
dp
,
vertical
=
2
.
dp
)
)
}
Box
(
modifier
=
Modifier
.
fillMaxWidth
()
.
padding
(
horizontal
=
16
.
dp
,
vertical
=
8
.
dp
)
.
background
(
Color
.
Black
)
@Composable
private
fun
ColumnScope
.
SpotInfoComponent
(
lat
:
Double
?,
lng
:
Double
?,
distance
:
String
?,
printJob
:
PrintJobListItem
,
spotResultStateFlow
:
StateFlow
<
List
<
MapMarker
>>,
onEventSent
:
(
event
:
JobDetailsContract
.
Event
)
->
Unit
,
paddingBottom
:
Dp
)
{
val
textStrings
=
listOfNotNull
(
"Spot: ${printJob.spotName}"
,
"Address: ${printJob.spotAddress}"
,
distance
?.
let
{
"Distance: $distance"
}
)
for
(
text
in
textStrings
)
{
Text
(
text
=
text
)
Spacer
(
modifier
=
Modifier
.
height
(
8
.
dp
))
}
Row
(
modifier
=
Modifier
.
fillMaxWidth
())
{
Card
(
modifier
=
Modifier
.
fillMaxWidth
()
.
padding
(
vertical
=
24
.
dp
)
.
weight
(
1f
),
shape
=
RoundedCornerShape
(
topStart
=
16
.
dp
,
topEnd
=
16
.
dp
,
bottomStart
=
0
.
dp
,
bottomEnd
=
0
.
dp
),
elevation
=
CardDefaults
.
cardElevation
(
defaultElevation
=
12
.
dp
)
)
{
TPMapComponent
(
modifier
=
Modifier
,
onEventSent
=
{
event
->
when
(
event
)
{
is
MapContract
.
Event
.
ClickOnMarker
->
onEventSent
(
JobDetailsContract
.
Event
.
OpenConfirmationMapRoute
)
else
->
{}
}
},
mapMarkersStateFlow
=
spotResultStateFlow
,
lat
=
lat
,
lng
=
lng
)
}
Button
(
onClick
=
{
onEventSent
(
JobDetailsContract
.
Event
.
OpenConfirmationMapRoute
)
},
modifier
=
Modifier
.
padding
(
bottom
=
paddingBottom
,
top
=
16
.
dp
)
.
fillMaxWidth
()
)
{
Text
(
text
=
"Build a route to Print spot"
)
}
}
\ No newline at end of file
app/src/main/java/com/isidroid/c23/ui/screen/map/MapScreen.kt
View file @
4bf53d84
...
...
@@ -53,7 +53,7 @@ fun MapScreen(
}
RequestLocationPermissionComponent
(
state
=
state
,
requestLocationPermission
=
state
.
value
.
requestLocationPermission
,
onGranted
=
{
onEventSent
(
MapContract
.
Event
.
PermissionGranted
)
}
)
...
...
app/src/main/java/com/isidroid/c23/ui/screen/map/_components/RequestLocationPermissionComponent.kt
View file @
4bf53d84
...
...
@@ -19,8 +19,8 @@ import com.isidroid.c23.ui.screen.map.MapContract
import
rememberPermissionState
@Composable
internal
fun
RequestLocationPermissionComponent
(
state
:
State
<
MapContract
.
State
>
,
onGranted
:
()
->
Unit
)
{
if
(!
state
.
value
.
requestLocationPermission
)
internal
fun
RequestLocationPermissionComponent
(
requestLocationPermission
:
Boolean
,
onGranted
:
()
->
Unit
)
{
if
(!
requestLocationPermission
)
return
val
context
=
LocalContext
.
current
...
...
app/src/test/java/com/isidroid/c23/ExampleUnitTest.kt
View file @
4bf53d84
...
...
@@ -12,11 +12,16 @@ import kotlinx.coroutines.delay
import
kotlinx.coroutines.flow.Flow
import
kotlinx.coroutines.flow.MutableSharedFlow
import
kotlinx.coroutines.flow.MutableStateFlow
import
kotlinx.coroutines.flow.SharingStarted
import
kotlinx.coroutines.flow.buffer
import
kotlinx.coroutines.flow.collect
import
kotlinx.coroutines.flow.firstOrNull
import
kotlinx.coroutines.flow.flow
import
kotlinx.coroutines.flow.flowOf
import
kotlinx.coroutines.flow.onEach
import
kotlinx.coroutines.flow.single
import
kotlinx.coroutines.flow.singleOrNull
import
kotlinx.coroutines.flow.stateIn
import
kotlinx.coroutines.flow.toList
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.runBlocking
...
...
@@ -34,6 +39,25 @@ import kotlin.coroutines.cancellation.CancellationException
class
ExampleUnitTest
{
@Test
fun
stateFlow
()
{
val
coroutineScope
=
CoroutineScope
(
Dispatchers
.
Default
)
val
job
=
coroutineScope
.
launch
{
val
flow
=
flow
{
repeat
(
10
)
{
delay
(
1000
)
emit
(
it
)
}
}
val
stateFlow
=
flow
.
stateIn
(
this
,
started
=
SharingStarted
.
Lazily
,
-
1
)
stateFlow
.
onEach
{
println
(
"result: $it"
)
}.
singleOrNull
()
}
runBlocking
{
delay
(
5
_000
)
job
.
cancel
()
}
}
}
\ No newline at end of file
library/location/src/google/java/com/isidroid/location/repository/LocationRepositoryImpl.kt
View file @
4bf53d84
package
com.isidroid.location.repository
import
android.annotation.SuppressLint
import
android.content.Context
import
androidx.compose.ui.text.intl.Locale
import
com.google.android.gms.location.LocationServices
import
com.google.android.gms.tasks.Tasks
import
com.isidroid.location.repository.LocationRepository
import
com.isidroid.location.ext.calculateDistanceBetweenPoints
import
timber.log.Timber
class
LocationRepositoryImpl
(
private
val
context
:
Context
)
:
LocationRepository
{
@SuppressLint
(
"MissingPermission"
)
override
fun
getCurrentLocation
():
Pair
<
Double
,
Double
>
{
val
client
=
LocationServices
.
getFusedLocationProviderClient
(
context
)
return
with
(
Tasks
.
await
(
client
.
lastLocation
))
{
Pair
(
latitude
,
longitude
)
return
with
(
Tasks
.
await
(
client
.
lastLocation
))
{
Pair
(
latitude
,
longitude
)
}
}
override
fun
findDistance
(
myLat
:
Double
,
myLng
:
Double
,
locationLat
:
Double
?,
locationLng
:
Double
?):
Float
?
{
if
(
locationLat
==
null
||
locationLng
==
null
)
return
null
val
result
=
calculateDistanceBetweenPoints
(
Locale
.
Companion
.
current
,
myLat
,
myLng
,
locationLat
,
locationLng
)
return
result
.
toFloat
()
}
}
\ No newline at end of file
library/location/src/main/java/com/isidroid/location/ext/ExtLocation.kt
0 → 100644
View file @
4bf53d84
package
com.isidroid.location.ext
import
androidx.compose.ui.text.intl.Locale
import
kotlin.math.atan2
import
kotlin.math.cos
import
kotlin.math.sin
import
kotlin.math.sqrt
internal
fun
calculateDistanceBetweenPoints
(
locale
:
Locale
,
myLat
:
Double
,
myLng
:
Double
,
locationLat
:
Double
,
locationLng
:
Double
):
Double
{
val
earthRadius
=
6371.0
// Радиус Земли в километрах
val
dLat
=
Math
.
toRadians
(
locationLat
-
myLat
)
val
dLng
=
Math
.
toRadians
(
locationLng
-
myLng
)
val
a
=
sin
(
dLat
/
2
)
*
sin
(
dLat
/
2
)
+
cos
(
Math
.
toRadians
(
myLat
))
*
cos
(
Math
.
toRadians
(
locationLat
))
*
sin
(
dLng
/
2
)
*
sin
(
dLng
/
2
)
val
c
=
2
*
atan2
(
sqrt
(
a
),
sqrt
(
1
-
a
))
val
distanceKm
=
earthRadius
*
c
val
distance
=
if
(
locale
.
isMileUnit
)
convertKmToMiles
(
distanceKm
)
else
distanceKm
return
distance
}
val
Locale
.
isMileUnit
get
()
=
region
in
arrayOf
(
"US"
)
internal
fun
convertKmToMiles
(
km
:
Double
):
Double
{
return
km
*
0.621371
}
library/location/src/main/java/com/isidroid/location/repository/LocationRepository.kt
View file @
4bf53d84
...
...
@@ -2,4 +2,5 @@ package com.isidroid.location.repository
interface
LocationRepository
{
fun
getCurrentLocation
():
Pair
<
Double
,
Double
>
fun
findDistance
(
myLat
:
Double
,
myLng
:
Double
,
locationLat
:
Double
?,
locationLng
:
Double
?):
Float
?
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment