Skip to content

Commit

Permalink
Merge pull request #294 from LookUpGroup27/fix/google-map-location-re…
Browse files Browse the repository at this point in the history
…quest

feat : enable location button in the googleMap
  • Loading branch information
Ismaillat authored Dec 19, 2024
2 parents f01eaa0 + 46e2c4b commit 1bdedab
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,13 @@ class GoogleMapScreenTest {

// Pass the mock MutableState to LocationProvider
locationProvider = LocationProvider(context, mockCurrentLocation)

// Set the Compose content to GoogleMapScreen
composeTestRule.setContent {
GoogleMapScreen(navigationActions, postsViewModel, profileViewModel)
}
}

@Test
fun mapScreenDisplaysCorrectly() {
composeTestRule.setContent {
GoogleMapScreen(navigationActions, postsViewModel, profileViewModel)
}

// Verify that the GoogleMapScreen is displayed
composeTestRule.onNodeWithTag("googleMapScreen").assertIsDisplayed()
Expand All @@ -80,6 +78,10 @@ class GoogleMapScreenTest {

@Test
fun cameraPositionIsUpdatedOnLocationChange() {
composeTestRule.setContent {
GoogleMapScreen(navigationActions, postsViewModel, profileViewModel)
}

val fakeLocation = LatLng(37.7749, -122.4194) // Example coordinates for San Francisco

// Simulate location update
Expand All @@ -101,6 +103,10 @@ class GoogleMapScreenTest {

@Test
fun buttonsAreDisplayedCorrectly() {
composeTestRule.setContent {
GoogleMapScreen(navigationActions, postsViewModel, profileViewModel)
}

// Verify that the "Auto Center On" button is displayed
composeTestRule.onNodeWithText("Auto Center On").assertIsDisplayed()

Expand All @@ -110,6 +116,10 @@ class GoogleMapScreenTest {

@Test
fun autoCenteringButtonsFunctionality() {
composeTestRule.setContent {
GoogleMapScreen(navigationActions, postsViewModel, profileViewModel)
}

// Click the "Auto Center Off" button to disable auto-centering
composeTestRule.onNodeWithText("Auto Center Off").performClick()

Expand Down Expand Up @@ -150,6 +160,9 @@ class GoogleMapScreenTest {

@Test
fun floatingActionButtonDisplaysAndNavigatesToCameraCapture() {
composeTestRule.setContent {
GoogleMapScreen(navigationActions, postsViewModel, profileViewModel)
}
// Mock the FirebaseAuth instance
mockAuth = org.mockito.kotlin.mock()
whenever(mockAuth.currentUser).thenReturn(null) // Can change this to test different scenarios
Expand All @@ -167,4 +180,18 @@ class GoogleMapScreenTest {
verify(navigationActions).navigateTo(Screen.AUTH)
}
}

@Test
fun testEnableLocationButtonIsDisplayed() {
composeTestRule.setContent {
GoogleMapScreen(navigationActions, postsViewModel, profileViewModel, true)
}

// Verify the UI elements are displayed
composeTestRule.onNodeWithTag("background_image").assertExists() // Verify image is displayed
composeTestRule
.onNodeWithTag("enable_location_button")
.assertExists() // Verify button is displayed
composeTestRule.onNodeWithText("Enable Location").assertExists() // Verify button text
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,37 @@ package com.github.lookupgroup27.lookup.ui.googlemap

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.pm.PackageManager
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.*
import androidx.core.app.ActivityCompat
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.github.lookupgroup27.lookup.R
import com.github.lookupgroup27.lookup.model.location.LocationProviderSingleton
import com.github.lookupgroup27.lookup.model.profile.UserProfile
import com.github.lookupgroup27.lookup.ui.googlemap.components.*
import com.github.lookupgroup27.lookup.ui.navigation.*
import com.github.lookupgroup27.lookup.ui.post.PostsViewModel
import com.github.lookupgroup27.lookup.ui.profile.ProfileViewModel
import com.github.lookupgroup27.lookup.ui.theme.DarkPurple
import com.google.firebase.auth.FirebaseAuth

private const val LOCATION_PERMISSION_REQUEST_CODE: Int = 1001
private const val NUMBER_OF_STARS: Int = 3

/**
Expand All @@ -32,58 +41,70 @@ private const val NUMBER_OF_STARS: Int = 3
* @param navigationActions Actions to navigate to different screens.
* @param postsViewModel ViewModel to fetch posts.
* @param profileViewModel ViewModel to fetch user profile.
* @param testNoLoc use for test purpose: it simulates the case before user grants location
* permission
*/
@SuppressLint("StateFlowValueCalledInComposition")
@Composable
fun GoogleMapScreen(
navigationActions: NavigationActions,
postsViewModel: PostsViewModel = viewModel(),
profileViewModel: ProfileViewModel = viewModel()
profileViewModel: ProfileViewModel = viewModel(),
testNoLoca: Boolean = false
) {
val context = LocalContext.current
var hasLocationPermission by remember { mutableStateOf(false) }
val locationProvider = LocationProviderSingleton.getInstance(context)
var autoCenteringEnabled by remember { mutableStateOf(true) } // New state for auto-centering
var hasLocationPermission by remember {
mutableStateOf(
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) ==
PackageManager.PERMISSION_GRANTED)
}
var autoCenteringEnabled by remember { mutableStateOf(true) }
val auth = remember { FirebaseAuth.getInstance() }
val isLoggedIn = auth.currentUser != null

val allPosts by postsViewModel.allPosts.collectAsState()

profileViewModel.fetchUserProfile()
val profile = profileViewModel.userProfile.value
val user = FirebaseAuth.getInstance().currentUser // Get the current signed-in user
val user = auth.currentUser
val userEmail = user?.email ?: ""
val username by remember { mutableStateOf(profile?.username ?: "") }
val bio by remember { mutableStateOf(profile?.bio ?: "") }
val email by remember { mutableStateOf(userEmail) }
val postRatings = remember { mutableStateMapOf<String, List<Boolean>>() }

LaunchedEffect(Unit) {
hasLocationPermission =
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) ==
PackageManager.PERMISSION_GRANTED
// Permission request launcher
val permissionLauncher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()) {
isGranted: Boolean ->
hasLocationPermission = isGranted
if (isGranted) {
locationProvider.requestLocationUpdates()
} else {
Toast.makeText(
context,
"Location permission is required. Please enable it in the app settings.",
Toast.LENGTH_LONG)
.show()
}
}

// Request location updates if permission is granted
LaunchedEffect(hasLocationPermission) {
if (hasLocationPermission) {
locationProvider.requestLocationUpdates()
} else {
// Request permission
ActivityCompat.requestPermissions(
context as Activity,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE)
Toast.makeText(
context, "Location permission is required to access the map.", Toast.LENGTH_LONG)
.show()
}
}

// Sync posts and ratings
LaunchedEffect(allPosts, profile) {
if (userEmail.isEmpty()) {
Toast.makeText(context, "Please sign in to rate photos on the feed.", Toast.LENGTH_LONG)
.show()
if (hasLocationPermission)
Toast.makeText(context, "Please sign in to rate photos on the feed.", Toast.LENGTH_LONG)
.show()
}
allPosts.forEach { post ->
if (!postRatings.containsKey(post.uid)) {
// Get saved rating from profile, or initialize with all false
val savedRating = profile?.ratings?.get(post.uid) ?: 0
val initialRating = List(NUMBER_OF_STARS) { index -> index < savedRating }
postRatings[post.uid] = initialRating.toMutableList()
Expand All @@ -100,62 +121,80 @@ fun GoogleMapScreen(
isUserLoggedIn = isLoggedIn,
selectedItem = navigationActions.currentRoute())
},
// Floating action button to take a picture
floatingActionButton = {
FloatingActionButton(
onClick = {
if (isLoggedIn) {
navigationActions.navigateTo(Screen.TAKE_IMAGE)
} else {
Toast.makeText(context, "Please log in to take a picture.", Toast.LENGTH_LONG)
.show()
navigationActions.navigateTo(Screen.AUTH)
}
},
modifier = Modifier.testTag("fab_take_picture")) {
Icon(Icons.Default.Add, contentDescription = "Take Picture")
}
if (hasLocationPermission)
FloatingActionButton(
onClick = {
if (isLoggedIn) {
navigationActions.navigateTo(Screen.TAKE_IMAGE)
} else {
Toast.makeText(context, "Please log in to take a picture.", Toast.LENGTH_LONG)
.show()
navigationActions.navigateTo(Screen.AUTH)
}
},
modifier = Modifier.testTag("fab_take_picture")) {
Icon(Icons.Default.Add, contentDescription = "Take Picture")
}
},
content = { padding ->
Column {
// Buttons to toggle map modes
// Check and request permission
if (!hasLocationPermission || testNoLoca) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Image(
painter = painterResource(id = R.drawable.landing_screen_bckgrnd),
contentDescription = "Background",
modifier = Modifier.fillMaxSize().testTag("background_image").blur(20.dp),
contentScale = ContentScale.Crop)

MapControls(
autoCenteringEnabled = autoCenteringEnabled,
onCenteringToggle = { autoCenteringEnabled = it })
Button(
onClick = { permissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION) },
modifier = Modifier.testTag("enable_location_button"),
colors = ButtonDefaults.buttonColors(DarkPurple),
) {
Text("Enable Location")
}
}
} else {
// Buttons to toggle map modes
MapControls(
autoCenteringEnabled = autoCenteringEnabled,
onCenteringToggle = { autoCenteringEnabled = it })

// Map view below the buttons
MapView(
padding,
hasLocationPermission,
locationProvider.currentLocation.value,
autoCenteringEnabled, // Pass the state
allPosts,
userEmail,
updateProfile = { profile, updatedRatings ->
val newProfile: UserProfile =
profile?.copy(
username = username,
bio = bio,
email = email,
ratings = updatedRatings ?: emptyMap())
?: UserProfile(
username = username,
bio = bio,
email = email,
ratings = updatedRatings ?: emptyMap())
profileViewModel.updateUserProfile(newProfile)
},
profile = profile,
updatePost = { post, newAvg, newStarsCount, newUsersNumber, newRatedBy ->
postsViewModel.updatePost(
post.copy(
averageStars = newAvg,
starsCount = newStarsCount,
usersNumber = newUsersNumber,
ratedBy = newRatedBy))
},
postRatings = postRatings)
// Map view below the buttons
MapView(
padding,
hasLocationPermission,
locationProvider.currentLocation.value,
autoCenteringEnabled,
allPosts,
userEmail,
updateProfile = { profile, updatedRatings ->
val newProfile: UserProfile =
profile?.copy(
username = username,
bio = bio,
email = email,
ratings = updatedRatings ?: emptyMap())
?: UserProfile(
username = username,
bio = bio,
email = email,
ratings = updatedRatings ?: emptyMap())
profileViewModel.updateUserProfile(newProfile)
},
profile = profile,
updatePost = { post, newAvg, newStarsCount, newUsersNumber, newRatedBy ->
postsViewModel.updatePost(
post.copy(
averageStars = newAvg,
starsCount = newStarsCount,
usersNumber = newUsersNumber,
ratedBy = newRatedBy))
},
postRatings = postRatings)
}
}
})
}

0 comments on commit 1bdedab

Please sign in to comment.