From ca05f3a1ec0f1eb2c2bfb13d927ed9504a941eb9 Mon Sep 17 00:00:00 2001 From: Kailash Sharma Date: Thu, 28 Dec 2023 02:11:57 +0530 Subject: [PATCH] Add here api's --- .../com/collectwaste/ListOfCollectWaste.kt | 2 +- .../com/ktorClient/repository/Api Routes.kt | 2 + .../ktorClient/repository/PlacesRepoImpl.kt | 39 ++++ .../ktorClient/repository/PlacesRepository.kt | 6 + .../com/ktorClient/weather/dto/Current.kt | 15 ++ .../ktorClient/weather/dto/CurrentUnits.kt | 15 ++ .../com/ktorClient/weather/dto/HereWeather.kt | 27 +++ .../app/waste2wealth/com/maps/MapsScreen.kt | 3 +- .../waste2wealth/com/maps/MapsSearchBar.kt | 175 ++++++++++++++++++ .../com/maps/MapsSearchViewModel.kt | 58 ++++++ 10 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/Current.kt create mode 100644 app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/CurrentUnits.kt create mode 100644 app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/HereWeather.kt diff --git a/app/src/main/java/app/waste2wealth/com/collectwaste/ListOfCollectWaste.kt b/app/src/main/java/app/waste2wealth/com/collectwaste/ListOfCollectWaste.kt index 5d7fd66..614a108 100644 --- a/app/src/main/java/app/waste2wealth/com/collectwaste/ListOfCollectWaste.kt +++ b/app/src/main/java/app/waste2wealth/com/collectwaste/ListOfCollectWaste.kt @@ -726,7 +726,7 @@ fun WasteItemCard( } -private fun distance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double { +fun distance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double { val theta = lon1 - lon2 var dist = (sin(deg2rad(lat1)) * sin(deg2rad(lat2)) diff --git a/app/src/main/java/app/waste2wealth/com/ktorClient/repository/Api Routes.kt b/app/src/main/java/app/waste2wealth/com/ktorClient/repository/Api Routes.kt index e98f636..e481610 100644 --- a/app/src/main/java/app/waste2wealth/com/ktorClient/repository/Api Routes.kt +++ b/app/src/main/java/app/waste2wealth/com/ktorClient/repository/Api Routes.kt @@ -10,4 +10,6 @@ object ApiRoutes { const val routing = "https://router.hereapi.com/v8/routes" + const val weather = "https://weather.cc.api.here.com/weather/1.0/report.json" + } \ No newline at end of file diff --git a/app/src/main/java/app/waste2wealth/com/ktorClient/repository/PlacesRepoImpl.kt b/app/src/main/java/app/waste2wealth/com/ktorClient/repository/PlacesRepoImpl.kt index 9f9089f..bf6368f 100644 --- a/app/src/main/java/app/waste2wealth/com/ktorClient/repository/PlacesRepoImpl.kt +++ b/app/src/main/java/app/waste2wealth/com/ktorClient/repository/PlacesRepoImpl.kt @@ -7,6 +7,7 @@ import app.waste2wealth.com.ktorClient.here.dto.HerePlaces import app.waste2wealth.com.ktorClient.hereSearch.HereSearchResponse import app.waste2wealth.com.ktorClient.placesAPI.dto.Places import app.waste2wealth.com.ktorClient.routing.dto.HereRoutes +import app.waste2wealth.com.ktorClient.weather.dto.HereWeather import io.ktor.client.HttpClient import io.ktor.client.request.get import io.ktor.client.request.header @@ -14,6 +15,8 @@ import io.ktor.client.request.headers import io.ktor.client.request.url import io.ktor.http.ContentType import io.ktor.http.HttpHeaders +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import java.net.URLEncoder class PlacesRepoImpl(private val client: HttpClient) : PlacesRepository { @@ -110,5 +113,41 @@ class PlacesRepoImpl(private val client: HttpClient) : PlacesRepository { } } + override suspend fun hereWeather(latitude: String, longitude: String): HereWeather { + return try { + val ok = ApiRoutes.weather + val encodeLat = URLEncoder.encode(latitude, "UTF-8") + val encodeLong = URLEncoder.encode(longitude, "UTF-8") + val a = client.get { + url( + "https://api.open-meteo.com/v1/forecast" + + "?latitude=$encodeLat&longitude=$encodeLong" + + "¤t=temperature_2m&temperature_unit=celsius" + ) + header(HttpHeaders.ContentType, ContentType.Application.Json) + headers { + append("Accept", "*/*") + append("Content-Type", "application/json") + } + } + println("weatherrrrrr 1: $latitude & $longitude") + println("weatherrrrrr: $a") + return a + } catch (e: Exception) { + Log.i("ApiException", e.message.toString()) + return HereWeather( + current = null, + currentUnits = null, + elevation = null, + generationtimeMs = null, + latitude = null, + longitude = null, + timezone = null, + timezoneAbbreviation = null, + utcOffsetSeconds = null, + ) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/app/waste2wealth/com/ktorClient/repository/PlacesRepository.kt b/app/src/main/java/app/waste2wealth/com/ktorClient/repository/PlacesRepository.kt index dc009f6..c667aa4 100644 --- a/app/src/main/java/app/waste2wealth/com/ktorClient/repository/PlacesRepository.kt +++ b/app/src/main/java/app/waste2wealth/com/ktorClient/repository/PlacesRepository.kt @@ -6,6 +6,7 @@ import app.waste2wealth.com.ktorClient.here.dto.HerePlaces import app.waste2wealth.com.ktorClient.hereSearch.HereSearchResponse import app.waste2wealth.com.ktorClient.placesAPI.dto.Places import app.waste2wealth.com.ktorClient.routing.dto.HereRoutes +import app.waste2wealth.com.ktorClient.weather.dto.HereWeather interface PlacesRepository { @@ -26,5 +27,10 @@ interface PlacesRepository { destination:String ): HereRoutes + suspend fun hereWeather( + latitude: String, + longitude: String + ): HereWeather + } diff --git a/app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/Current.kt b/app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/Current.kt new file mode 100644 index 0000000..7e8d98a --- /dev/null +++ b/app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/Current.kt @@ -0,0 +1,15 @@ +package app.waste2wealth.com.ktorClient.weather.dto + + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + +@Serializable +data class Current( + @SerializedName("interval") + val interval: Int?, + @SerializedName("temperature_2m") + val temperature: Double?, + @SerializedName("time") + val time: String? +) \ No newline at end of file diff --git a/app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/CurrentUnits.kt b/app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/CurrentUnits.kt new file mode 100644 index 0000000..da00ade --- /dev/null +++ b/app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/CurrentUnits.kt @@ -0,0 +1,15 @@ +package app.waste2wealth.com.ktorClient.weather.dto + + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + +@Serializable +data class CurrentUnits( + @SerializedName("interval") + val interval: String?, + @SerializedName("temperature_2m") + val temperature_2m: String?, + @SerializedName("time") + val time: String? +) \ No newline at end of file diff --git a/app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/HereWeather.kt b/app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/HereWeather.kt new file mode 100644 index 0000000..ed71f59 --- /dev/null +++ b/app/src/main/java/app/waste2wealth/com/ktorClient/weather/dto/HereWeather.kt @@ -0,0 +1,27 @@ +package app.waste2wealth.com.ktorClient.weather.dto + + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + +@Serializable +data class HereWeather( + @SerializedName("current") + val current: Current?, + @SerializedName("current_units") + val currentUnits: CurrentUnits?, + @SerializedName("elevation") + val elevation: Double?, + @SerializedName("generationtime_ms") + val generationtimeMs: Double?, + @SerializedName("latitude") + val latitude: Double?, + @SerializedName("longitude") + val longitude: Double?, + @SerializedName("timezone") + val timezone: String?, + @SerializedName("timezone_abbreviation") + val timezoneAbbreviation: String?, + @SerializedName("utc_offset_seconds") + val utcOffsetSeconds: Int? +) \ No newline at end of file diff --git a/app/src/main/java/app/waste2wealth/com/maps/MapsScreen.kt b/app/src/main/java/app/waste2wealth/com/maps/MapsScreen.kt index 7099b00..d024f2a 100644 --- a/app/src/main/java/app/waste2wealth/com/maps/MapsScreen.kt +++ b/app/src/main/java/app/waste2wealth/com/maps/MapsScreen.kt @@ -290,7 +290,8 @@ fun MapScreen( onTrailingClick = { mapsSearchViewModel.setQuery(TextFieldValue("")) }, - navController = navController + navController = navController, + locationViewModel = viewModel ) Spacer(modifier = Modifier.height(10.dp)) AnimatedVisibility( diff --git a/app/src/main/java/app/waste2wealth/com/maps/MapsSearchBar.kt b/app/src/main/java/app/waste2wealth/com/maps/MapsSearchBar.kt index 74051cf..cdfcb07 100644 --- a/app/src/main/java/app/waste2wealth/com/maps/MapsSearchBar.kt +++ b/app/src/main/java/app/waste2wealth/com/maps/MapsSearchBar.kt @@ -1,6 +1,7 @@ package app.waste2wealth.com.maps import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInVertically @@ -10,6 +11,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -22,6 +24,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape @@ -45,6 +48,7 @@ import androidx.compose.material.LinearProgressIndicator import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.material.TextFieldDefaults +import androidx.compose.material.icons.filled.LocationOn import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -56,6 +60,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue @@ -63,9 +68,21 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController +import app.waste2wealth.com.collectwaste.WasteItemCard +import app.waste2wealth.com.collectwaste.convertDistance +import app.waste2wealth.com.collectwaste.distance +import app.waste2wealth.com.collectwaste.getTimeAgo +import app.waste2wealth.com.location.LocationViewModel import app.waste2wealth.com.login.TextFieldWithIcons +import app.waste2wealth.com.tags.Tag +import app.waste2wealth.com.tags.TagItem import app.waste2wealth.com.ui.theme.CardBackground +import app.waste2wealth.com.ui.theme.CardColor +import app.waste2wealth.com.ui.theme.CardTextColor +import app.waste2wealth.com.ui.theme.appBackground import app.waste2wealth.com.ui.theme.lightText +import app.waste2wealth.com.ui.theme.monteBold +import app.waste2wealth.com.ui.theme.monteSB import app.waste2wealth.com.ui.theme.textColor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -75,6 +92,7 @@ import kotlinx.coroutines.launch @Composable fun MapsSearchBar( + locationViewModel: LocationViewModel, mutableText: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, onTrailingClick: () -> Unit = {}, @@ -84,6 +102,7 @@ fun MapsSearchBar( val isChecking by viewModel.isChecking.collectAsState() var isCheckingJob: Job? = null // Initialize isCheckingJob val addresses = viewModel.addresses.collectAsState() + val moreWasteInfo = viewModel.moreInfoWaste.collectAsState() Column( modifier = Modifier @@ -208,9 +227,165 @@ fun MapsSearchBar( } } } + + AnimatedVisibility( + visible = addresses.value.isEmpty() && viewModel.isClicked.value, + enter = slideInVertically(tween(1000), initialOffsetY = { + it + }), + exit = slideOutVertically(tween(1000), targetOffsetY = { + it + }) + ) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.BottomCenter + ) { + MoreWasteItemCard( + modifier = Modifier, + locationNo = "Your Location", + address = moreWasteInfo.value?.address ?: "", + distance = "${moreWasteInfo.value?.distance}", + time = moreWasteInfo.value?.time ?: "", + weather = moreWasteInfo.value?.weather ?: "" + ) { + + } + } + + } + } +} + + +@Composable +fun MoreWasteItemCard( + weather: String, + modifier: Modifier = Modifier, + locationNo: String, + address: String, + distance: String, + time: String, + isCollectedInfo: Boolean = false, + isEllipsis: Boolean = true, + onCollected: () -> Unit = {}, + onClick: () -> Unit = {} +) { + Card( + modifier = modifier + .fillMaxWidth() + .padding(13.dp) + .clickable { + onClick() + }, + shape = RoundedCornerShape(10.dp), + backgroundColor = CardColor, + elevation = 5.dp + ) { + Column(modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + start = 10.dp, + bottom = 7.dp + ), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Filled.LocationOn, + contentDescription = "", + tint = Color.Gray, + modifier = Modifier + .size(25.dp) + .padding(end = 10.dp) + ) + + Text( + text = locationNo, + color = Color.Gray, + fontFamily = monteSB, + fontSize = 14.sp + ) + } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 10.dp, bottom = 7.dp, end = 15.dp) + ) { + Text( + text = address, + color = CardTextColor, + fontFamily = monteSB, + fontSize = 15.sp, + maxLines = if (isEllipsis) 1 else Int.MAX_VALUE, + softWrap = true, + overflow = if (isEllipsis) TextOverflow.Ellipsis else TextOverflow.Visible + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(end = 10.dp, bottom = 7.dp), + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.Bottom + ) { + Text( + text = weather, + color = CardTextColor.copy(0.75f), + fontFamily = monteBold, + fontSize = 10.sp + ) + + } + } + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.Bottom + ) { + Button( + onClick = { + if (isCollectedInfo) onCollected() else onClick() + }, + colors = ButtonDefaults.buttonColors( + backgroundColor = appBackground, + contentColor = textColor + ), + shape = RoundedCornerShape(15.dp), + modifier = Modifier.padding(start = 10.dp) + ) { + Text( + text = if (isCollectedInfo) "Navigate" else "Collect", + color = textColor, + fontFamily = monteSB, + fontSize = 10.sp + ) + } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(end = 10.dp, bottom = 7.dp), + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.Bottom + ) { + Text( + text = "$distance, $time minutes", + color = CardTextColor.copy(0.75f), + fontFamily = monteBold, + fontSize = 10.sp + ) + + } + } + } + + } + } + @Composable fun TextFieldWithIcons( modifier: Modifier = Modifier, diff --git a/app/src/main/java/app/waste2wealth/com/maps/MapsSearchViewModel.kt b/app/src/main/java/app/waste2wealth/com/maps/MapsSearchViewModel.kt index 9121329..0183454 100644 --- a/app/src/main/java/app/waste2wealth/com/maps/MapsSearchViewModel.kt +++ b/app/src/main/java/app/waste2wealth/com/maps/MapsSearchViewModel.kt @@ -9,6 +9,10 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import app.waste2wealth.com.ktorClient.repository.PlacesRepository +import app.waste2wealth.com.location.LocationClientProvider +import app.waste2wealth.com.location.LocationTracker +import app.waste2wealth.com.tags.Tag +import com.google.android.gms.location.LocationServices import com.mapbox.geojson.Point import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers @@ -42,7 +46,13 @@ import javax.inject.Inject class MapsSearchViewModel @Inject constructor( private val application: Application, private val repository: PlacesRepository, + private val locationTracker: LocationTracker, ) : AndroidViewModel(application) { + private val locationClient = LocationClientProvider( + application.applicationContext, + LocationServices.getFusedLocationProviderClient(application.applicationContext) + ) + private val _imageState = MutableStateFlow(ApiState.NotStarted) val imageState: StateFlow = _imageState.asStateFlow() @@ -50,6 +60,9 @@ class MapsSearchViewModel @Inject constructor( private val _query = MutableStateFlow(TextFieldValue()) val query: StateFlow = _query.asStateFlow() + private val _moreInfoWaste = MutableStateFlow(null) + val moreInfoWaste: StateFlow = _moreInfoWaste.asStateFlow() + private val _addresses = MutableStateFlow>(listOf()) val addresses: StateFlow> = _addresses.asStateFlow() @@ -71,6 +84,7 @@ class MapsSearchViewModel @Inject constructor( var isReset = mutableStateOf(false) var currentPoint = mutableStateOf(null) + var currentAddress = mutableStateOf("") fun setImageState(state: ApiState) { @@ -122,10 +136,13 @@ class MapsSearchViewModel @Inject constructor( } } + fun searchPlace(index: Int) { try { isReset.value = false isClicked.value = true + currentAddress.value = _addresses.value[index].name + getMoreWasteInfo() currentPoint.value = _addresses.value[index].let { Point.fromLngLat(it.longitude, it.latitude) } @@ -148,6 +165,37 @@ class MapsSearchViewModel @Inject constructor( // } } + + fun getMoreWasteInfo() { + viewModelScope.launch { + locationTracker.getCurrentLocation()?.let {myLocation -> + withContext(Dispatchers.IO) { + try { + val apiData = repository.hereRoutes( + transportMode = "car", + origin = "${myLocation.latitude},${myLocation.longitude}", + destination = "${currentPoint.value?.latitude()},${currentPoint.value?.longitude()}" + ) + val weatherData = repository.hereWeather( + latitude = "${currentPoint.value?.latitude()}", + longitude = "${currentPoint.value?.longitude()}", + ) + _moreInfoWaste.value = MoreWasteInfo( + weather = "${weatherData.current?.temperature} Celsius", + distance = apiData.routes?.get(0)?.sections?.get(0)?.summary?.length.toString(), + time = apiData.routes?.get(0)?.sections?.get(0)?.summary?.duration.toString(), + latitude = currentPoint.value?.latitude() ?: 0.0, + longitude = currentPoint.value?.longitude() ?: 0.0, + address = currentAddress.value + ) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + } + } + fun getAutoComplete(query: String) { viewModelScope.launch { withContext(Dispatchers.IO){ @@ -223,3 +271,13 @@ data class Address( val countryIso1: String? = null, ) +data class MoreWasteInfo( + val weather: String, + val distance: String, + val time: String, + val latitude: Double, + val longitude: Double, + val address: String +) + +