Skip to content

Commit

Permalink
Merge pull request #480 from bounswe/feature/mobile-463-map-implement…
Browse files Browse the repository at this point in the history
…ation

Map implementation
  • Loading branch information
GulbeycanCagri authored Nov 27, 2023
2 parents 898aee3 + 34b7787 commit 53d2fd2
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 32 deletions.
15 changes: 14 additions & 1 deletion resq/mobile/ResQ/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id ("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
}

android {
Expand Down Expand Up @@ -37,6 +38,7 @@ android {
jvmTarget = "1.8"
}
buildFeatures {
buildConfig = true
compose = true
}
composeOptions {
Expand All @@ -50,8 +52,13 @@ android {
}

dependencies {
val nav_version = "2.7.4"
implementation ("com.google.maps.android:maps-ktx:3.2.1")
implementation ("com.google.maps.android:maps-utils-ktx:3.2.1")
implementation ("com.google.android.gms:play-services-location:21.0.1")
implementation ("com.google.android.gms:play-services-maps:18.2.0")
implementation ("com.google.maps.android:maps-compose:4.1.1")

val nav_version = "2.7.4"
// Java language implementation
implementation("androidx.navigation:navigation-fragment:$nav_version")
implementation("androidx.navigation:navigation-ui:$nav_version")
Expand Down Expand Up @@ -94,5 +101,11 @@ dependencies {
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}

secrets{
propertiesFileName = "secrets.properties"

ignoreList.add("keyToIgnore") // Ignore the key "keyToIgnore"
ignoreList.add("sdk.*") // Ignore all keys matching the regexp "sdk.*"
}
9 changes: 9 additions & 0 deletions resq/mobile/ResQ/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
xmlns:tools="http://schemas.android.com/tools" >

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

<application
android:allowBackup="true"
Expand All @@ -15,6 +18,12 @@
android:theme="@style/Theme.ResQ"
android:usesCleartextTraffic="true"
tools:targetApi="31" >
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}" />
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.cmpe451.resq

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -19,6 +23,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.core.content.ContextCompat
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
Expand All @@ -38,15 +43,45 @@ import com.cmpe451.resq.ui.views.screens.ResourceScreen
import com.cmpe451.resq.ui.views.screens.SettingsScreen
import com.cmpe451.resq.utils.BottomNavigationItem
import com.cmpe451.resq.utils.NavigationItem
import com.cmpe451.resq.viewmodels.MapViewModel
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices

class MainActivity : ComponentActivity() {

private val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
mapViewModel.getDeviceLocation(fusedLocationProviderClient)
}
}

private fun askPermissions() = when {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED -> {
mapViewModel.getDeviceLocation(fusedLocationProviderClient)
}
else -> {
requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}
}

private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
private val mapViewModel: MapViewModel by viewModels()

@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
askPermissions()
setContent {
val appContext = applicationContext
ResQTheme {
MainScreen(appContext)
MainScreen(appContext, mapViewModel)
}
}
}
Expand All @@ -55,7 +90,7 @@ class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.O)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(appContext: Context) {
fun MainScreen(appContext: Context, mapViewModel: MapViewModel) {
val navController = rememberNavController()

Scaffold(
Expand All @@ -68,7 +103,8 @@ fun MainScreen(appContext: Context) {
) {
NavGraph(
navController = navController,
appContext = appContext
appContext = appContext,
mapViewModel = mapViewModel
)
}
},)
Expand All @@ -78,7 +114,8 @@ fun MainScreen(appContext: Context) {
@Composable
fun NavGraph(
navController: NavHostController,
appContext: Context
appContext: Context,
mapViewModel: MapViewModel,
) {
NavHost(
navController = navController,
Expand All @@ -91,7 +128,7 @@ fun NavGraph(
LoginScreen(navController, appContext)
}
composable(NavigationItem.Map.route) {
MapScreen(navController, appContext)
MapScreen(navController, appContext, mapViewModel)
}
composable(NavigationItem.Request.route) {
RequestScreen(navController, appContext)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.cmpe451.resq.data.models

data class Need(
val id: Int,
val userId: Int,
val categoryTreeId: String,
val description: String,
val quantity: Int,
val latitude: Double,
val longitude: Double,
val requestId: Int,
val status: String,
val createdDate: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.cmpe451.resq.data.models.CreateNeedRequestBody
import com.cmpe451.resq.data.models.CreateResourceRequestBody
import com.cmpe451.resq.data.models.LoginRequestBody
import com.cmpe451.resq.data.models.LoginResponse
import com.cmpe451.resq.data.models.Need
import com.cmpe451.resq.data.models.ProfileData
import com.cmpe451.resq.data.models.RegisterRequestBody
import com.cmpe451.resq.data.models.UserInfoRequest
Expand Down Expand Up @@ -47,6 +48,16 @@ interface NeedService {
@Body requestBody: CreateNeedRequestBody
): Response<Int>


@GET("need/filterByDistance")
fun filterNeedByDistance(
@Query("latitude") latitude: Double,
@Query("longitude") longitude: Double,
@Query("distance") distance: Double,
@Header("Authorization") jwtToken: String,
@Header("X-Selected-Role") role: String,
): Response<List<Need>>

}

interface AuthService {
Expand Down Expand Up @@ -138,6 +149,22 @@ class ResqService(appContext: Context) {
)
}

suspend fun filterNeedByDistance(
latitude: Double,
longitude: Double,
distance: Double
): Response<List<Need>> {
val selectedRole = userSessionManager.getSelectedRole() ?: ""
val token = userSessionManager.getUserToken() ?: ""
return needService.filterNeedByDistance(
latitude = latitude,
longitude = longitude,
distance = distance,
role = selectedRole,
jwtToken = "Bearer $token"
)
}

// Auth methods
suspend fun login(request: LoginRequestBody): Response<LoginResponse> = authService.login(request)
suspend fun register(request: RegisterRequestBody): Response<ResponseBody> = authService.register(request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
Expand Down Expand Up @@ -93,7 +92,7 @@ fun LoginScreen(navController: NavController, appContext: Context) {
modifier = Modifier.fillMaxWidth(),
colors = TextFieldDefaults.textFieldColors(
containerColor = DeepBlue,
textColor = Color.White,
focusedTextColor = Color.White,
cursorColor = Color.White
)
)
Expand All @@ -110,7 +109,7 @@ fun LoginScreen(navController: NavController, appContext: Context) {
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
colors = TextFieldDefaults.textFieldColors(
containerColor = DeepBlue,
textColor = Color.White,
focusedTextColor = Color.White,
cursorColor = Color.White
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.cmpe451.resq.ui.views.screens

import android.content.Context
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -23,27 +22,33 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.AddCircle
import androidx.compose.material.icons.filled.Search
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.cmpe451.resq.R
import com.cmpe451.resq.data.manager.UserSessionManager
import com.cmpe451.resq.ui.theme.DeepBlue
import com.cmpe451.resq.ui.theme.RequestColor
import com.cmpe451.resq.ui.theme.ResourceColor
import com.cmpe451.resq.utils.NavigationItem
import com.cmpe451.resq.viewmodels.MapViewModel
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.maps.android.compose.GoogleMap
import com.google.maps.android.compose.Marker
import com.google.maps.android.compose.MarkerState
import com.google.maps.android.compose.rememberCameraPositionState

@Composable
fun MapScreen(navController: NavController, appContext: Context) {
val viewModel: MapViewModel = viewModel()
fun MapScreen(navController: NavController, appContext: Context, mapViewModel: MapViewModel) {
val userSessionManager = UserSessionManager.getInstance(appContext)
val userRoles = userSessionManager.getUserRoles()



Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
Expand Down Expand Up @@ -77,12 +82,42 @@ fun MapScreen(navController: NavController, appContext: Context) {
}

Spacer(modifier = Modifier.height(16.dp))
SearchBar(viewModel)
Image(
painter = painterResource(id = R.drawable.mock_map),
contentDescription = "Mock Map",
modifier = Modifier.fillMaxSize()
)
SearchBar(mapViewModel)
val singapore = LatLng(41.086571, 29.046109)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(mapViewModel.lastKnownLocation.value?.let {
LatLng(it.latitude, it.longitude)
} ?: singapore, 12f)
}
LaunchedEffect(Unit) {
// mapViewModel.getNeedByDistance(appContext)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
mapViewModel.lastKnownLocation.value?.let {
val latLng = LatLng(it.latitude, it.longitude)
cameraPositionState.position = CameraPosition.fromLatLngZoom(latLng, 12f)
}
mapViewModel.needMarkerList.value.forEach { need ->
Marker(
state = MarkerState(position = LatLng(need.latitude, need.longitude)),
title = need.description,
snippet = "Quantity: ${need.quantity}"
)
}
Marker(
state = MarkerState(position = LatLng(41.086571, 29.046109)),
)
}
LaunchedEffect(mapViewModel.needMarkerList.value) {
if (mapViewModel.needMarkerList.value.isNotEmpty()) {
// Move camera to the first marker or any specific logic you want
val firstNeed = mapViewModel.needMarkerList.value.first()
cameraPositionState.move(CameraUpdateFactory.newLatLngZoom(LatLng(firstNeed.latitude, firstNeed.longitude), 12f))
}
}
}
}
}
Expand Down
Loading

0 comments on commit 53d2fd2

Please sign in to comment.