diff --git a/.github/workflows/BuildAndRelease.yml b/.github/workflows/BuildAndRelease.yml
index 663f243..3682de9 100644
--- a/.github/workflows/BuildAndRelease.yml
+++ b/.github/workflows/BuildAndRelease.yml
@@ -10,9 +10,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: setup jdk
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
diff --git a/.github/workflows/android-apk-generation.yml b/.github/workflows/android-apk-generation.yml
index 4834ecc..a811900 100644
--- a/.github/workflows/android-apk-generation.yml
+++ b/.github/workflows/android-apk-generation.yml
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v4
# Set Current Date As Env Variable
- name: Set current date as env variable
@@ -40,7 +40,7 @@ jobs:
run: echo "repository_name=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV
- name: Set Up JDK
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
distribution: 'zulu' # See 'Supported distributions' for available options
java-version: '17'
@@ -73,21 +73,21 @@ jobs:
# Upload Artifact Build
# Noted For Output [main_project_module]/build/outputs/apk/debug/
- name: Upload APK Debug - ${{ env.repository_name }}
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: ${{ env.date_today }} - ${{ env.playstore_name }} - ${{ env.repository_name }} - APK(s) debug generated
path: ${{ env.main_project_module }}/build/outputs/apk/debug/
# Noted For Output [main_project_module]/build/outputs/apk/release/
- name: Upload APK Release - ${{ env.repository_name }}
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: ${{ env.date_today }} - ${{ env.playstore_name }} - ${{ env.repository_name }} - APK(s) release generated
path: ${{ env.main_project_module }}/build/outputs/apk/release/
# Noted For Output [main_project_module]/build/outputs/bundle/release/
- name: Upload AAB (App Bundle) Release - ${{ env.repository_name }}
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: ${{ env.date_today }} - ${{ env.playstore_name }} - ${{ env.repository_name }} - App bundle(s) AAB release generated
path: ${{ env.main_project_module }}/build/outputs/bundle/release/
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index d536a1e..c5c1754 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -12,9 +12,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: set up JDK 17
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2f1ccde..31b200d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,7 +12,7 @@ jobs:
steps:
# 1
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
# 2
- name: Generate Release APK
run: ./gradlew assembleFossRelease
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 3c1f4f1..db18282 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
# 1
- - uses: dawidd6/action-download-artifact@v2
+ - uses: dawidd6/action-download-artifact@v3
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
@@ -24,7 +24,7 @@ jobs:
echo "::set-output name=release_tag::APP_$(date +"%Y.%m.%d_%H-%M")"
# 3
- name: Release
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index f56b4be..50a5436 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -9,6 +9,21 @@ plugins {
alias(libs.plugins.googleAndroidLibrariesMapsplatformSecretsGradlePlugin)
}
+secrets {
+ // Optionally specify a different file name containing your secrets.
+ // The plugin defaults to "local.properties"
+ propertiesFileName = "secrets.properties"
+
+ // A properties file containing default secret values. This file can be
+ // checked in version control.
+ defaultPropertiesFileName = "local.defaults.properties"
+
+ // Configure which keys should be ignored by the plugin by providing regular expressions.
+ // "sdk.dir" is ignored by default.
+ ignoreList.add("keyToIgnore") // Ignore the key "keyToIgnore"
+ ignoreList.add("sdk.*") // Ignore all keys matching the regexp "sdk.*"
+}
+
android {
namespace = "ie.coconnor.mobileappdev"
compileSdk = 34
@@ -63,7 +78,7 @@ android {
viewBinding = true
}
composeOptions {
- kotlinCompilerExtensionVersion = "1.5.1"
+ kotlinCompilerExtensionVersion = "1.5.12"
}
packaging {
resources {
@@ -101,32 +116,32 @@ dependencies {
debugImplementation(libs.androidx.ui.test.manifest)
// Splash API
implementation ("androidx.core:core-splashscreen:1.0.1")
- implementation("com.google.android.gms:play-services-auth:20.5.0")
- implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.0")
- implementation ("androidx.lifecycle:lifecycle-runtime-compose:2.6.0")
- implementation ("androidx.navigation:navigation-compose:2.5.3")
- implementation ("io.coil-kt:coil-compose:2.2.2")
- implementation ("androidx.compose.material:material-icons-extended:1.6.3")
- implementation ("androidx.appcompat:appcompat:1.2.0")
- implementation ("com.firebaseui:firebase-ui-auth:7.2.0")
-
- implementation("com.google.dagger:hilt-android:2.51")
- kapt("com.google.dagger:hilt-android-compiler:2.51")
- implementation("androidx.hilt:hilt-navigation-compose:1.1.0")
+ implementation("com.google.android.gms:play-services-auth:21.1.0")
+ implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
+ implementation ("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
+ implementation ("androidx.navigation:navigation-compose:2.7.7")
+ implementation ("io.coil-kt:coil-compose:2.6.0")
+ implementation ("androidx.compose.material:material-icons-extended:1.6.6")
+ implementation ("androidx.appcompat:appcompat:1.6.1")
+ implementation ("com.firebaseui:firebase-ui-auth:8.0.2")
+
+ implementation("com.google.dagger:hilt-android:2.51.1")
+ kapt("com.google.dagger:hilt-android-compiler:2.51.1")
+ implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
// Retrofit for network requests
- implementation ("com.squareup.retrofit2:retrofit:2.10.0")
- implementation ("com.squareup.retrofit2:converter-gson:2.10.0")
+ implementation ("com.squareup.retrofit2:retrofit:2.11.0")
+ implementation ("com.squareup.retrofit2:converter-gson:2.11.0")
- implementation ("androidx.compose.runtime:runtime-livedata:1.6.3")
- implementation ("com.google.android.gms:play-services-maps:18.1.0")
- implementation ("com.google.android.gms:play-services-location:21.0.1")
- implementation ("com.google.maps.android:maps-compose:4.3.3")
+ implementation ("androidx.compose.runtime:runtime-livedata:1.6.6")
+ implementation ("com.google.android.gms:play-services-maps:18.2.0")
+ implementation ("com.google.android.gms:play-services-location:21.2.0")
+ implementation ("com.google.maps.android:maps-compose:4.4.1")
implementation ("com.google.maps.android:maps-ktx:5.0.0")
- implementation ("com.squareup.moshi:moshi:1.14.0")
- implementation ("com.squareup.moshi:moshi-kotlin:1.14.0")
+ implementation ("com.squareup.moshi:moshi:1.15.1")
+ implementation ("com.squareup.moshi:moshi-kotlin:1.15.1")
- implementation("io.klogging:klogging-jvm:0.5.11")
+ implementation ("com.jakewharton.timber:timber:5.0.1")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 19456a2..6ce65c5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -47,7 +47,10 @@
/>
+ android:value="${MAPS_API_KEY}"/>
+
Unit
-) {
- TopAppBar(
-
- title = { Text(currentScreen) },
- modifier = modifier,
-
- navigationIcon = {
- if (showBackButton) {
- // Show back navigation button if allowed
- IconButton(onClick = onBackButtonClick) {
- Icon(
- imageVector = Icons.Filled.ArrowBack,
- tint = Color.Black,
- contentDescription = stringResource(R.string.back_button)
- )
- }
-
- }
- }
-
- )
-}
-
-/**
- * A preview function for [CustomAppBar].
- */
-@Preview
-@Composable
-fun PreviewCustomTopAppBar() {
- // Example usage with navigation controller
- CustomAppBar(
- currentScreen = "TopAppBar",
- showBackButton = true,
- onBackButtonClick = { }
- )
-}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/AuthViewModel.kt b/app/src/main/java/ie/coconnor/mobileappdev/AuthViewModel.kt
index 467f15e..04a1c18 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/AuthViewModel.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/AuthViewModel.kt
@@ -47,4 +47,8 @@ class AuthViewModel @Inject constructor(
DataProvider.signOutResponse = Response.Loading
DataProvider.signOutResponse = repository.signOut()
}
+
+ fun signInWithCredentials() = {
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/MainActivity.kt b/app/src/main/java/ie/coconnor/mobileappdev/MainActivity.kt
index 2b7cf6b..1a81a9e 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/MainActivity.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/MainActivity.kt
@@ -1,9 +1,15 @@
+@file:OptIn(ExperimentalPermissionsApi::class)
+
package ie.coconnor.mobileappdev
-//import com.google.firebase.firestore.FirebaseFirestore
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.annotation.SuppressLint
import android.app.PendingIntent
+import android.content.ComponentName
import android.content.Intent
+import android.content.IntentSender
+import android.content.pm.PackageManager
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
@@ -11,61 +17,68 @@ import android.os.Bundle
import android.provider.Settings
import android.util.Log
import androidx.activity.ComponentActivity
-import androidx.activity.compose.rememberLauncherForActivityResult
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.padding
-import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.sp
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.navigation.NavHostController
+import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
import com.google.accompanist.permissions.ExperimentalPermissionsApi
-import com.google.accompanist.permissions.rememberMultiplePermissionsState
+import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.FusedLocationProviderClient
+import com.google.android.gms.location.GeofencingClient
+import com.google.android.gms.location.LocationRequest
+import com.google.android.gms.location.LocationServices
+import com.google.android.gms.location.LocationSettingsRequest
+import com.google.android.gms.location.LocationSettingsResponse
+import com.google.android.gms.location.SettingsClient
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.tasks.Task
import dagger.hilt.android.AndroidEntryPoint
import ie.coconnor.mobileappdev.models.AuthState
+import ie.coconnor.mobileappdev.models.Constants.BACKGROUND_LOCATION_PERMISSION_REQUEST_CODE
+import ie.coconnor.mobileappdev.models.Constants.Geofencing.LOCATION_FASTEST_INTERVAL
+import ie.coconnor.mobileappdev.models.Constants.Geofencing.LOCATION_INTERVAL
+import ie.coconnor.mobileappdev.models.Constants.Geofencing.REQUEST_CHECK_SETTINGS
+import ie.coconnor.mobileappdev.models.Constants.Geofencing.REQUEST_CODE
+import ie.coconnor.mobileappdev.models.Constants.LOCATION_PERMISSION_REQUEST_CODE
import ie.coconnor.mobileappdev.models.DataProvider
import ie.coconnor.mobileappdev.models.locations.LocationDetailsViewModel
import ie.coconnor.mobileappdev.models.locations.LocationsViewModel
import ie.coconnor.mobileappdev.models.plan.PlanViewModel
+import ie.coconnor.mobileappdev.receiver.GeofenceBroadcastReceiver
+import ie.coconnor.mobileappdev.service.BootReceiver
import ie.coconnor.mobileappdev.service.LocationForegroundService
+import ie.coconnor.mobileappdev.ui.locations.LocationDetailsScreen
+import ie.coconnor.mobileappdev.ui.locations.LocationsScreen
import ie.coconnor.mobileappdev.ui.login.LoginScreen
-import ie.coconnor.mobileappdev.ui.login.SignUpScreen
import ie.coconnor.mobileappdev.ui.navigation.BottomBar
import ie.coconnor.mobileappdev.ui.navigation.Destinations
import ie.coconnor.mobileappdev.ui.plan.PlanScreen
import ie.coconnor.mobileappdev.ui.screens.SettingsScreen
import ie.coconnor.mobileappdev.ui.screens.TestScreen
-import ie.coconnor.mobileappdev.ui.screens.locations.LocationDetailsScreen
-import ie.coconnor.mobileappdev.ui.screens.locations.LocationsScreen
import ie.coconnor.mobileappdev.ui.theme.MobileAppDevTheme
import ie.coconnor.mobileappdev.utils.SharedPref
import ie.coconnor.mobileappdev.utils.UIThemeController
+import timber.log.Timber
import javax.inject.Inject
-import androidx.core.content.ContextCompat.startActivity
-import androidx.compose.ui.graphics.Color
-import com.google.android.gms.location.GeofencingClient
-import com.google.android.gms.location.LocationServices
-import ie.coconnor.mobileappdev.receiver.GeofenceBroadcastReceiver
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@@ -81,7 +94,7 @@ class MainActivity : ComponentActivity() {
private lateinit var fusedLocationClient: FusedLocationProviderClient
lateinit var geofencingClient: GeofencingClient
// private val geofenceList = mutableListOf()
-//
+
private val geofencePendingIntent: PendingIntent by lazy {
val intent = Intent(this, GeofenceBroadcastReceiver::class.java)
PendingIntent.getBroadcast(
@@ -102,11 +115,42 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- if (savedInstanceState != null) {
-
+ if (BuildConfig.DEBUG) {
+ Timber.plant(Timber.DebugTree())
}
+ Timber.tag(TAG).i("Creating geofence client")
geofencingClient = LocationServices.getGeofencingClient(this)
+ //check permission
+ if (ActivityCompat.checkSelfPermission(
+ this,
+ android.Manifest.permission.ACCESS_FINE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED
+ ) {
+ currentLocation()
+ } else {
+ ActivityCompat.requestPermissions(
+ this,
+ arrayOf(
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ ),
+ REQUEST_CODE
+ )
+ }
+
+ val receiver = ComponentName(this, BootReceiver::class.java)
+ packageManager.setComponentEnabledSetting(
+ receiver,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP
+ )
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ checkNotificationPermission()
+ }else{
+ checkAndRequestLocationPermissions()
+ }
WindowCompat.setDecorFitsSystemWindows(window, true)
// window.setFlags(
// WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
@@ -114,13 +158,11 @@ class MainActivity : ComponentActivity() {
//
// )
// window.setTitle("Test")
- //createLocationRequest()
- // createGeofence()
+ createLocationRequest()
+ // createGeofence()
// println(sharedPref.getDarkMode())
- UIThemeController.updateUITheme(sharedPref.getDarkMode())
-
+// UIThemeController.updateUITheme(sharedPref.getDarkMode())
setContent {
-
val isDarkMode by UIThemeController.isDarkMode.collectAsState()
MobileAppDevTheme (darkTheme = isDarkMode){
@@ -130,179 +172,111 @@ class MainActivity : ComponentActivity() {
val currentUser = authViewModel.currentUser.collectAsState().value
DataProvider.updateAuthState(currentUser)
- Log.i("AuthRepo", "Authenticated: ${DataProvider.isAuthenticated}")
- Log.i("AuthRepo", "Anonymous: ${DataProvider.isAnonymous}")
- Log.i("AuthRepo", "User: ${DataProvider.user}")
-
- val multiplePermission = rememberMultiplePermissionsState(
- permissions = listOf(
- android.Manifest.permission.POST_NOTIFICATIONS,
- android.Manifest.permission.ACCESS_COARSE_LOCATION,
- android.Manifest.permission.ACCESS_FINE_LOCATION,
- android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ Timber.tag(TAG).i("Authenticated: ${DataProvider.isAuthenticated}")
+ Timber.tag(TAG).i( "Anonymous: ${DataProvider.isAnonymous}")
+ Timber.tag(TAG).i("User: ${DataProvider.user}")
- )
- )
- val context = LocalContext.current
- val showRationalDialog = remember { mutableStateOf(false) }
-
- val requestPermissionLauncher = rememberLauncherForActivityResult(
- ActivityResultContracts.RequestPermission()
- ) { isGranted ->
- if (isGranted) {
- // Permission granted
- } else {
- // Handle permission denial
+ Scaffold(
+ bottomBar = {
+ BottomBar(
+ navController = navController,
+ state = buttonsVisible,
+ modifier = Modifier
+ )
+ },
+ ) { paddingValues ->
+ Box(
+ modifier = Modifier.padding(paddingValues)
+ ) {
+ NavigationGraph(navController = navController, authViewModel = authViewModel, tourViewModel = tourViewModel, locationDetailsViewModel = locationDetailsViewModel, planViewModel, sharedPref = sharedPref)
}
}
+ }
+ }
+ }
- if (showRationalDialog.value) {
- AlertDialog(
- onDismissRequest = {
- showRationalDialog.value = false
- },
- title = {
- Text(
- text = "Permission",
- fontWeight = FontWeight.Bold,
- fontSize = 16.sp
- )
- },
- text = {
- Text(
- if (multiplePermission.revokedPermissions.size == 4) {
- "We need some permissions for geofencing activities"
- } else if (multiplePermission.revokedPermissions.isNotEmpty() && multiplePermission.revokedPermissions.first().permission == android.Manifest.permission.ACCESS_FINE_LOCATION) {
- "We need fine location permission. Please grant the permission."
- } else if (multiplePermission.revokedPermissions.isNotEmpty() && multiplePermission.revokedPermissions.first().permission == android.Manifest.permission.ACCESS_BACKGROUND_LOCATION){
- "We need background location permission. Please grant the permission to be allowed all the time."
- } else if (multiplePermission.revokedPermissions.isNotEmpty() && multiplePermission.revokedPermissions.first().permission == android.Manifest.permission.POST_NOTIFICATIONS) {
- "We need to send notifications."
- } else if (multiplePermission.revokedPermissions.isNotEmpty()) {
- "We need ${multiplePermission.revokedPermissions.first().permission.toString()} permission. Please grant the permission."
- } else {
- showRationalDialog.value = false
- ""
- },
- fontSize = 16.sp
- )
- },
- confirmButton = {
- TextButton(
- onClick = {
- showRationalDialog.value = false
- if(multiplePermission.revokedPermissions.isNotEmpty()) {
- requestPermissionLauncher.launch(multiplePermission.revokedPermissions.first().permission)
- }
- }) {
- Text("OK", style = TextStyle(color = Color.Black))
- }
- },
- dismissButton = {
- TextButton(
- onClick = {
- showRationalDialog.value = false
- }) {
- Text("Cancel", style = TextStyle(color = Color.Black))
- }
- },
- )
- }
+ private fun areLocationPermissionsAlreadyGranted(): Boolean {
+ return ContextCompat.checkSelfPermission(
+ this,
+ ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
+ }
- Scaffold(
- bottomBar = {
- BottomBar(
- navController = navController,
- state = buttonsVisible,
- modifier = Modifier
- )
- }) { paddingValues ->
- if (multiplePermission.shouldShowRationale) {
- // Show a rationale if needed (optional)
- showRationalDialog.value = true
- } else {
- // Request the permission
- if(!multiplePermission.revokedPermissions.isEmpty())
- requestPermissionLauncher.launch(multiplePermission.revokedPermissions.first().permission)
+ private fun openApplicationSettings() {
+ Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", packageName, null)).also {
+ startActivity(it)
+ }
+ }
- }
- Box(
- modifier = Modifier.padding(paddingValues)
- ) {
- NavigationGraph(navController = navController, authViewModel = authViewModel, tourViewModel = tourViewModel, locationDetailsViewModel = locationDetailsViewModel, planViewModel, sharedPref = sharedPref)
- }
- }
- }
+ private fun decideCurrentPermissionStatus(locationPermissionsGranted: Boolean,
+ shouldShowPermissionRationale: Boolean): String {
+ return if (locationPermissionsGranted) "Granted"
+ else if (shouldShowPermissionRationale) "Rejected"
+ else "Denied"
+ }
+
+ private fun currentLocation() {
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
+ if (ActivityCompat.checkSelfPermission(
+ this,
+ ACCESS_FINE_LOCATION
+ ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
+ this,
+ ACCESS_COARSE_LOCATION
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ // TODO: Consider calling
+ // ActivityCompat#requestPermissions
+ // here to request the missing permissions, and then overriding
+ // public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ // int[] grantResults)
+ // to handle the case where the user grants the permission. See the documentation
+ // for ActivityCompat#requestPermissions for more details.
+ return
+ }
+ fusedLocationClient.lastLocation.addOnSuccessListener { location ->
+ if (location != null) {
+ val latlng = LatLng(location.latitude, location.longitude)
+ println(latlng)
}
}
+ }
+ private fun createLocationRequest() {
+ val locationRequest = LocationRequest.create().apply {
+ interval = LOCATION_INTERVAL
+ fastestInterval = LOCATION_FASTEST_INTERVAL
+ priority = LocationRequest.PRIORITY_HIGH_ACCURACY
+ }
-// // @SuppressLint("MissingPermission")
-// private fun currentLocation() {
-// fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
-// if (ActivityCompat.checkSelfPermission(
-// this,
-// Manifest.permission.ACCESS_FINE_LOCATION
-// ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
-// this,
-// Manifest.permission.ACCESS_COARSE_LOCATION
-// ) != PackageManager.PERMISSION_GRANTED
-// ) {
-// // TODO: Consider calling
-// // ActivityCompat#requestPermissions
-// // here to request the missing permissions, and then overriding
-// // public void onRequestPermissionsResult(int requestCode, String[] permissions,
-// // int[] grantResults)
-// // to handle the case where the user grants the permission. See the documentation
-// // for ActivityCompat#requestPermissions for more details.
-// return
-// }
-// fusedLocationClient.lastLocation.addOnSuccessListener { location ->
-// if (location != null) {
-// val latlng = LatLng(location.latitude, location.longitude)
-// println(latlng)
-//
-// }
-// }
-// }
+ val builder = LocationSettingsRequest.Builder()
+ .addLocationRequest(locationRequest)
-// private fun createLocationRequest() {
-// val locationRequest = LocationRequest.create().apply {
-// interval = LOCATION_INTERVAL
-// fastestInterval = LOCATION_FASTEST_INTERVAL
-// priority = LocationRequest.PRIORITY_HIGH_ACCURACY
-// }
-//
-// val builder = LocationSettingsRequest.Builder()
-// .addLocationRequest(locationRequest)
-//
-//
-// val client: SettingsClient = LocationServices.getSettingsClient(this)
-// val task: Task = client.checkLocationSettings(builder.build())
-//
-// task.addOnSuccessListener {
-// Log.i("Location", " Enable Successful")
-//
-// }
-// task.addOnFailureListener { exception ->
-// Log.i("Location", exception.message.toString())
-// if (exception is ResolvableApiException) {
-// try {
-// exception.startResolutionForResult(
-// this,
-// REQUEST_CHECK_SETTINGS
-// )
-// } catch (sendEx: IntentSender.SendIntentException) {
-// // Ignore the error.
-// println(sendEx)
-// }
-// }
-// }
-// }
-// private fun createGeofence(){
+ val client: SettingsClient = LocationServices.getSettingsClient(this)
+ val task: Task = client.checkLocationSettings(builder.build())
+
+ task.addOnSuccessListener {
+ Log.i("Location", " Enable Successful")
+
+ }
+ task.addOnFailureListener { exception ->
+ Log.i("Location", exception.message.toString())
+ if (exception is ResolvableApiException) {
+ try {
+ exception.startResolutionForResult(
+ this,
+ REQUEST_CHECK_SETTINGS
+ )
+ } catch (sendEx: IntentSender.SendIntentException) {
+ // Ignore the error.
+ println(sendEx)
+ }
+ }
+ }
+ }
+ // private fun createGeofence(){
// geofenceList.add(
// Geofence.Builder()
// .setRequestId("entry.key")
@@ -337,114 +311,85 @@ class MainActivity : ComponentActivity() {
// }.build()
// }
//
-// private fun checkBackGroundLocationPermission(): Boolean {
-// return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-// (ContextCompat.checkSelfPermission(
-// this,
-// android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
-// ) == PackageManager.PERMISSION_GRANTED)
-// } else {
-// return true
-// }
-//
-// }
+ private fun checkBackGroundLocationPermission(): Boolean {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ (ContextCompat.checkSelfPermission(
+ this,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED)
+ } else {
+ return true
+ }
-// private fun requestBackGroundLocationPermission() {
-// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-// ActivityCompat.requestPermissions(
-// this,
-// arrayOf(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION),
-// BACKGROUND_LOCATION_PERMISSION_REQUEST_CODE
-// )
-// }
-// }
-//
-// override fun onRequestPermissionsResult(
-// requestCode: Int,
-// permissions: Array,
-// grantResults: IntArray
-// ) {
-// super.onRequestPermissionsResult(requestCode, permissions, grantResults)
-//
-// when (requestCode) {
-// BACKGROUND_LOCATION_PERMISSION_REQUEST_CODE -> {
-// if (grantResults.isNotEmpty() &&
-// grantResults[0] == PackageManager.PERMISSION_GRANTED
-// ) {
-// startLocationService()
-// } else {
-// // Handle the case where the user denies the foreground service permission
-// }
-// }
-// LOCATION_PERMISSION_REQUEST_CODE -> {
-// if (grantResults.isNotEmpty() &&
-// grantResults[0] == PackageManager.PERMISSION_GRANTED &&
-// grantResults[1] == PackageManager.PERMISSION_GRANTED
-// ) {
-// checkAndRequestLocationPermissions()
-// } else {
-// // Handle the case where the user denies the location permission
-// }
-// }
-// }
-// }
-//
-// private fun checkAndRequestLocationPermissions() {
-// if (checkLocationPermission()) {
-// if (checkBackGroundLocationPermission()){
-// startLocationService()
-// }else{
-// requestBackGroundLocationPermission()
-// }
-//
-// } else {
-// requestLocationPermission()
-// }
-// }
+ }
-// private fun checkLocationPermission(): Boolean {
-// return (ContextCompat.checkSelfPermission(
-// this,
-// android.Manifest.permission.ACCESS_FINE_LOCATION
-// ) == PackageManager.PERMISSION_GRANTED &&
-// ContextCompat.checkSelfPermission(
-// this,
-// android.Manifest.permission.ACCESS_COARSE_LOCATION
-// ) == PackageManager.PERMISSION_GRANTED)
-// }
+ private fun requestBackGroundLocationPermission() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ ActivityCompat.requestPermissions(
+ this,
+ arrayOf(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION),
+ BACKGROUND_LOCATION_PERMISSION_REQUEST_CODE
+ )
+ }
+ }
-// private fun requestLocationPermission() {
-// ActivityCompat.requestPermissions(
-// this,
-// arrayOf(
-// android.Manifest.permission.ACCESS_FINE_LOCATION,
-// android.Manifest.permission.ACCESS_COARSE_LOCATION
-// ),
-// LOCATION_PERMISSION_REQUEST_CODE
-// )
-// }
+ private fun checkAndRequestLocationPermissions() {
+ if (checkLocationPermission()) {
+ if (checkBackGroundLocationPermission()){
+ startLocationService()
+ }else{
+ requestBackGroundLocationPermission()
+ }
-// fun checkNotificationPermission() {
-// val permission = android.Manifest.permission.POST_NOTIFICATIONS
-// when {
-// ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -> {
-// // make your action here
-// checkAndRequestLocationPermissions()
-// }
-// shouldShowRequestPermissionRationale(permission) -> {
-// // permission denied permanently
-// }
-// else -> {
-// requestNotificationPermission.launch(permission)
-// }
-// }
-// }
+ } else {
+ requestLocationPermission()
+ }
+ }
-// private val requestNotificationPermission =
-// registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted->
-// if (isGranted) // make your action here
-// checkAndRequestLocationPermissions()
-// }
+ private fun checkLocationPermission(): Boolean {
+ return (ContextCompat.checkSelfPermission(
+ this,
+ ACCESS_FINE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED &&
+ ContextCompat.checkSelfPermission(
+ this,
+ ACCESS_COARSE_LOCATION
+ ) == PackageManager.PERMISSION_GRANTED)
+ }
+
+ private fun requestLocationPermission() {
+ ActivityCompat.requestPermissions(
+ this,
+ arrayOf(
+ ACCESS_FINE_LOCATION,
+ ACCESS_COARSE_LOCATION
+ ),
+ LOCATION_PERMISSION_REQUEST_CODE
+ )
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ fun checkNotificationPermission() {
+ val permission = android.Manifest.permission.POST_NOTIFICATIONS
+ when {
+ ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -> {
+ // make your action here
+ checkAndRequestLocationPermissions()
+ }
+ shouldShowRequestPermissionRationale(permission) -> {
+ // permission denied permanently
+ }
+ else -> {
+ requestNotificationPermission.launch(permission)
+ }
+ }
+ }
+
+ private val requestNotificationPermission =
+ registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted->
+ if (isGranted) // make your action here
+ checkAndRequestLocationPermissions()
+ }
private fun startLocationService() {
val serviceIntent = Intent(this, LocationForegroundService::class.java)
startService(serviceIntent)
@@ -453,9 +398,40 @@ class MainActivity : ComponentActivity() {
val serviceIntent = Intent(this, LocationForegroundService::class.java)
stopService(serviceIntent)
}
+ companion object {
+ private const val TAG = "MainActivity"
+ }
}
+//@Composable
+//fun getPermissions(){
+// val lifecycleOwner = LocalLifecycleOwner.current
+// val permissionState = rememberMultiplePermissionsState(permissions = listOf(
+// ACCESS_COARSE_LOCATION,
+// ACCESS_FINE_LOCATION
+// ))
+//
+// DisposableEffect(key1 = lifecycleOwner) {
+// val observer = LifecycleEventObserver{ source, event ->
+// when (event) {
+// Lifecycle.Event.ON_START -> {
+// permissionState.launchMultiplePermissionRequest()
+// }
+//
+// else -> {
+//
+// }
+// }
+//
+// }
+// lifecycleOwner.lifecycle.addObserver(observer)
+// onDispose {
+// lifecycleOwner.lifecycle.removeObserver(observer)
+// }
+// }
+//}
+
@Composable
fun NavigationGraph(navController: NavHostController,
authViewModel: AuthViewModel,
@@ -473,23 +449,24 @@ fun NavigationGraph(navController: NavHostController,
composable(Destinations.LoginScreen.route) {
LoginScreen( authViewModel)
}
- composable(Destinations.Favourite.route) {
- SignUpScreen(navController)
- }
composable(Destinations.LocationsScreen.route) {
LocationsScreen(tourViewModel, navController , sharedPref)
}
- composable(Destinations.PlanScreen.route) {
+// composable(Destinations.PlanScreenWithId.route + "/{location}", arguments = listOf(navArgument("location") { type = NavType.StringType })
+ composable(Destinations.PlanScreen.route ) {
PlanScreen(planViewModel, navController, sharedPref)
}
+
composable(Destinations.TestScreen.route) {
TestScreen(navController)
}
composable(Destinations.SettingsScreen.route) {
SettingsScreen(navController, authViewModel)
}
- composable(Destinations.LocationDetailsScreen.route) {
- LocationDetailsScreen(navController, locationDetailsViewModel,sharedPref)
+ composable(Destinations.LocationDetailsScreen.route + "/{location}", arguments = listOf(navArgument("location") { type = NavType.StringType })
+ ) { backStackEntry ->
+ val location = backStackEntry.arguments?.getString("location")
+ LocationDetailsScreen(navController, locationDetailsViewModel, location)
}
}
}
@@ -509,3 +486,4 @@ fun MobileAppDevPreview() {
}
}
+
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/models/DataProvider.kt b/app/src/main/java/ie/coconnor/mobileappdev/models/DataProvider.kt
index 1ac6976..7f10113 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/models/DataProvider.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/models/DataProvider.kt
@@ -41,4 +41,16 @@ object DataProvider {
AuthState.SignedOut
}
}
+
+ fun getDisplayName(user: FirebaseUser?): String{
+ this.user = user
+ val displayName = user!!.displayName
+ return displayName.toString()
+ }
+
+ fun getProfilePhoto(user: FirebaseUser?): String{
+ this.user = user
+ val photoUrl = user!!.photoUrl
+ return photoUrl.toString().replace("s96", "s250")
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/models/Location.kt b/app/src/main/java/ie/coconnor/mobileappdev/models/Location.kt
index f7cc0bd..66d9f97 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/models/Location.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/models/Location.kt
@@ -20,16 +20,19 @@ data class Location(
var url: String? = "",
var latitude: String? = "",
var longitude: String? = ""
-)
+){
+ constructor(): this("","")
+
+}
data class Address(
- val street1: String? = null,
- val street2: String? = null,
- var city: String? = null,
- val state: String? = null,
- val country: String? = null,
- val postalcode: String? = null,
- val address_string: String
+ val street1: String? = "",
+ val street2: String? = "",
+ var city: String? = "",
+ val state: String? = "",
+ val country: String? = "",
+ val postalcode: String? = "",
+ val address_string: String? = ""
)
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/models/locations/LocationDetailsViewModel.kt b/app/src/main/java/ie/coconnor/mobileappdev/models/locations/LocationDetailsViewModel.kt
index 25f2814..4d5beb9 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/models/locations/LocationDetailsViewModel.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/models/locations/LocationDetailsViewModel.kt
@@ -1,16 +1,14 @@
package ie.coconnor.mobileappdev.models.locations
-import android.content.res.Resources
-import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import ie.coconnor.mobileappdev.R
import ie.coconnor.mobileappdev.models.LocationDetails
import ie.coconnor.mobileappdev.repository.LocationsRepository
import kotlinx.coroutines.launch
import retrofit2.HttpException
+import timber.log.Timber
class LocationDetailsViewModel(): ViewModel() {
private val repository = LocationsRepository()
@@ -22,17 +20,20 @@ class LocationDetailsViewModel(): ViewModel() {
viewModelScope.launch {
try {
val details = repository.getLocationDetails(location_id, tripAdvisorApiKey)
- println("location " + details.description.toString())
_locationDetails.value = details
-// Log.e("TourViewModel", _locationDetails.value.toString()
+ Timber.tag(TAG).i( _locationDetails.value.toString())
} catch (http: HttpException) {
- Log.e("TourViewModel", http.response().toString())
+ Timber.tag(TAG).e( http.response().toString())
} catch (e: Exception) {
// Handle error
- Log.e("TourViewModel", e.cause.toString());
- Log.e("TourViewModel", e.message.toString());
- Log.e("TourViewModel", e.stackTrace.contentToString());
+ Timber.tag(TAG).e( e.cause.toString());
+ Timber.tag(TAG).e( e.message.toString());
+ Timber.tag(TAG).e( e.stackTrace.contentToString());
}
}
}
+
+ companion object {
+ private const val TAG = "LocationDetailsViewModel"
+ }
}
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/models/locations/LocationsViewModel.kt b/app/src/main/java/ie/coconnor/mobileappdev/models/locations/LocationsViewModel.kt
index a27e43d..cdbfe4b 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/models/locations/LocationsViewModel.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/models/locations/LocationsViewModel.kt
@@ -1,6 +1,5 @@
package ie.coconnor.mobileappdev.models.locations
-import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -11,6 +10,7 @@ import ie.coconnor.mobileappdev.repository.FirestoreRepository
import ie.coconnor.mobileappdev.repository.LocationsRepository
import ie.coconnor.mobileappdev.repository.Trip
import kotlinx.coroutines.launch
+import timber.log.Timber
class LocationsViewModel() : ViewModel() {
@@ -31,10 +31,10 @@ class LocationsViewModel() : ViewModel() {
val cards = repository.getLocations(tripAdvisorApiKey, location, category)
println(cards.data.toString())
_locations.value = cards
- Log.e("TourViewModel", _locations.value.toString())
+ Timber.tag(TAG).i(_locations.value.toString())
} catch (e: Exception) {
// Handle error
- Log.e("TourViewModel", e.message.toString());
+ Timber.tag(TAG).e( e.message.toString());
}
}
}
@@ -42,26 +42,43 @@ class LocationsViewModel() : ViewModel() {
fun fetchTrips(){
viewModelScope.launch {
try {
- val cards = fireStoreRepository.getTrips()
- println(cards.toString())
- _trips.value = cards
- Log.e("TourViewModel", _trips.value.toString())
+ val trips = fireStoreRepository.getTrips()
+ println(trips.toString())
+ _trips.value = trips
+ Timber.tag(TAG).i( _trips.value.toString())
} catch (e: Exception) {
// Handle error
- Log.e("TourViewModel", e.message.toString());
+ Timber.tag(TAG).e( e.message.toString());
}
}
}
- fun createOrUpdateTrip(location: Location){
+ fun updateTrip(location: Location, documentName: String): Boolean{
+ var saved: Boolean = false
viewModelScope.launch {
try {
- Log.i("PlanViewModel", location.toString())
- val cards = fireStoreRepository.createOrUpdateTrip(location)
- println(cards.toString())
+ Timber.tag(TAG).i( location.name)
+ val cards = fireStoreRepository.updateTrip(location, documentName)
+ saved = true
} catch (e: Exception){
- Log.e("PlanViewModel", e.message.toString());
+ Timber.tag(TAG).e( e.message.toString());
}
+ return@launch
}
+ return saved
+ }
+
+ fun createTrip(location: Location){
+ viewModelScope.launch {
+ try {
+ Timber.tag(TAG).i( location.location_id)
+ fireStoreRepository.createTrip(location)
+ } catch (e: Exception){
+ Timber.tag(TAG).e( e.message.toString());
+ }
+ }
+ }
+ companion object {
+ private const val TAG = "LocationsViewModel"
}
}
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/models/plan/PlanViewModel.kt b/app/src/main/java/ie/coconnor/mobileappdev/models/plan/PlanViewModel.kt
index db873cb..0c2da16 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/models/plan/PlanViewModel.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/models/plan/PlanViewModel.kt
@@ -1,6 +1,5 @@
package ie.coconnor.mobileappdev.models.plan
-import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -9,6 +8,7 @@ import ie.coconnor.mobileappdev.models.Location
import ie.coconnor.mobileappdev.repository.FirestoreRepository
import ie.coconnor.mobileappdev.repository.Trip
import kotlinx.coroutines.launch
+import timber.log.Timber
class PlanViewModel: ViewModel() {
@@ -18,14 +18,13 @@ class PlanViewModel: ViewModel() {
val trips: LiveData> = _trips
- fun createOrUpdateTrip(tripName: Location){
+ fun createTrip( location: Location){
viewModelScope.launch {
try {
- Log.i("PlanViewModel", tripName.toString())
- val cards = repository.createOrUpdateTrip(tripName)
- println(cards.toString())
+ Timber.tag(TAG).i( location.location_id)
+ repository.createTrip(location)
} catch (e: Exception){
- Log.e("PlanViewModel", e.message.toString());
+ Timber.tag(TAG).i( e.message.toString());
}
}
}
@@ -34,13 +33,16 @@ class PlanViewModel: ViewModel() {
viewModelScope.launch {
try {
val cards = repository.getTrips()
- println(cards.toString())
_trips.value = cards
- Log.e("PlanViewModel", _trips.value.toString())
+ Timber.tag(TAG).i(_trips.value.toString())
} catch (e: Exception) {
// Handle error
- Log.e("PlanViewModel", e.message.toString());
+ Timber.tag(TAG).e( e.message.toString());
}
}
}
+
+ companion object {
+ private const val TAG = "PlanViewModel"
+ }
}
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/receiver/GeofenceBroadcastReceiver.kt b/app/src/main/java/ie/coconnor/mobileappdev/receiver/GeofenceBroadcastReceiver.kt
index f1ffa11..9f72236 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/receiver/GeofenceBroadcastReceiver.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/receiver/GeofenceBroadcastReceiver.kt
@@ -19,19 +19,18 @@ import com.google.android.gms.location.GeofenceStatusCodes
import com.google.android.gms.location.GeofencingEvent
import ie.coconnor.mobileappdev.MainActivity
import ie.coconnor.mobileappdev.R
+import ie.coconnor.mobileappdev.models.plan.PlanViewModel
+import timber.log.Timber
class GeofenceBroadcastReceiver : BroadcastReceiver() {
- private val TAG = "Broadcast =>"
- private val CHANNEL_ID = "Geofence Channel"
-
override fun onReceive(context: Context?, intent: Intent?) {
//Added Notification
val geofencingEvent = intent?.let { GeofencingEvent.fromIntent(it) }
if (geofencingEvent?.hasError() == true) {
val errorMessage = GeofenceStatusCodes
.getStatusCodeString(geofencingEvent.errorCode)
- Log.e(TAG, errorMessage)
+ Timber.tag(TAG).e( errorMessage)
return
}
// Get the transition type.
@@ -49,7 +48,7 @@ class GeofenceBroadcastReceiver : BroadcastReceiver() {
sendNotification(context!!, "Exit")
}
else -> {
- Log.e(TAG, "Error in setting up the geofence")
+ Timber.tag(TAG).i("Error in setting up the geofence")
}
}
}
@@ -103,4 +102,10 @@ class GeofenceBroadcastReceiver : BroadcastReceiver() {
notificationManager.notify(1234, notification)
}
+
+ companion object {
+ private const val TAG = "GeofenceBroadcastReciever"
+ private const val CHANNEL_ID = "Geofence Channel"
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/repository/AuthRepository.kt b/app/src/main/java/ie/coconnor/mobileappdev/repository/AuthRepository.kt
index 67d79d6..16fbd81 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/repository/AuthRepository.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/repository/AuthRepository.kt
@@ -5,6 +5,7 @@ import ie.coconnor.mobileappdev.models.FirebaseSignInResponse
import ie.coconnor.mobileappdev.models.OneTapSignInResponse
import ie.coconnor.mobileappdev.models.SignOutResponse
import com.google.android.gms.auth.api.identity.SignInCredential
+import com.google.firebase.auth.FirebaseUser
import kotlinx.coroutines.CoroutineScope
interface AuthRepository {
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/repository/AuthRepositoryImpl.kt b/app/src/main/java/ie/coconnor/mobileappdev/repository/AuthRepositoryImpl.kt
index cf65469..33556ba 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/repository/AuthRepositoryImpl.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/repository/AuthRepositoryImpl.kt
@@ -1,12 +1,5 @@
package ie.coconnor.mobileappdev.repository
-import android.util.Log
-import ie.coconnor.mobileappdev.models.Constants
-import ie.coconnor.mobileappdev.models.DataProvider
-import ie.coconnor.mobileappdev.models.FirebaseSignInResponse
-import ie.coconnor.mobileappdev.models.OneTapSignInResponse
-import ie.coconnor.mobileappdev.models.Response
-import ie.coconnor.mobileappdev.models.SignOutResponse
import com.google.android.gms.auth.api.identity.BeginSignInRequest
import com.google.android.gms.auth.api.identity.SignInClient
import com.google.android.gms.auth.api.identity.SignInCredential
@@ -15,15 +8,24 @@ import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseAuth.AuthStateListener
import com.google.firebase.auth.FirebaseAuthException
import com.google.firebase.auth.GoogleAuthProvider
+import ie.coconnor.mobileappdev.models.Constants
+import ie.coconnor.mobileappdev.models.DataProvider
+import ie.coconnor.mobileappdev.models.FirebaseSignInResponse
+import ie.coconnor.mobileappdev.models.OneTapSignInResponse
+import ie.coconnor.mobileappdev.models.Response
+import ie.coconnor.mobileappdev.models.SignOutResponse
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.tasks.await
+import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
+import javax.inject.Singleton
+@Singleton
class AuthRepositoryImpl @Inject constructor(
private val auth: FirebaseAuth,
private var oneTapClient: SignInClient,
@@ -32,10 +34,11 @@ class AuthRepositoryImpl @Inject constructor(
@Named(Constants.SIGN_UP_REQUEST)
private var signUpRequest: BeginSignInRequest,
): AuthRepository {
+
override fun getAuthState(viewModelScope: CoroutineScope) = callbackFlow {
val authStateListener = AuthStateListener { auth ->
trySend(auth.currentUser)
- Log.i(TAG, "User: ${auth.currentUser?.uid ?: "Not authenticated"}")
+ Timber.tag(TAG).i("User: ${auth.currentUser?.uid ?: "Not authenticated"}")
}
auth.addAuthStateListener(authStateListener)
awaitClose {
@@ -47,11 +50,11 @@ class AuthRepositoryImpl @Inject constructor(
return try {
val authResult = auth.signInAnonymously().await()
authResult?.user?.let { user ->
- Log.i(TAG, "FirebaseAuthSuccess: Anonymous UID: ${user.uid}")
+ Timber.tag(TAG).i("FirebaseAuthSuccess: Anonymous UID: ${user.uid}")
}
Response.Success(authResult)
} catch (error: Exception) {
- Log.e(TAG, "FirebaseAuthError: Failed to Sign in anonymously")
+ Timber.tag(TAG).e("FirebaseAuthError: Failed to Sign in anonymously")
Response.Failure(error)
}
}
@@ -87,7 +90,7 @@ class AuthRepositoryImpl @Inject constructor(
private suspend fun authSignIn(credential: AuthCredential): FirebaseSignInResponse {
return try {
val authResult = auth.signInWithCredential(credential).await()
- Log.i(TAG, "User: ${authResult?.user?.uid}")
+ Timber.tag(TAG).i( "User: ${authResult?.user?.uid}")
DataProvider.updateAuthState(authResult?.user)
Response.Success(authResult)
}
@@ -99,7 +102,7 @@ class AuthRepositoryImpl @Inject constructor(
private suspend fun authLink(credential: AuthCredential): FirebaseSignInResponse {
return try {
val authResult = auth.currentUser?.linkWithCredential(credential)?.await()
- Log.i(TAG, "User: ${authResult?.user?.uid}")
+ Timber.tag(TAG).i( "User: ${authResult?.user?.uid}")
DataProvider.updateAuthState(authResult?.user)
Response.Success(authResult)
}
@@ -107,7 +110,7 @@ class AuthRepositoryImpl @Inject constructor(
when (error.errorCode) {
Constants.AuthErrors.CREDENTIAL_ALREADY_IN_USE,
Constants.AuthErrors.EMAIL_ALREADY_IN_USE -> {
- Log.e(TAG, "FirebaseAuthError: authLink(credential:) failed, ${error.message}")
+ Timber.tag(TAG).e("FirebaseAuthError: authLink(credential:) failed, ${error.message}")
return authSignIn(credential)
}
}
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/repository/FirestoreRepository.kt b/app/src/main/java/ie/coconnor/mobileappdev/repository/FirestoreRepository.kt
index 6f65ae0..e90bdc2 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/repository/FirestoreRepository.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/repository/FirestoreRepository.kt
@@ -1,55 +1,64 @@
package ie.coconnor.mobileappdev.repository
-import android.util.Log
+import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.toObjects
import ie.coconnor.mobileappdev.models.Constants
import ie.coconnor.mobileappdev.models.DataProvider
import ie.coconnor.mobileappdev.models.Location
import kotlinx.coroutines.tasks.await
+import timber.log.Timber
class FirestoreRepository {
suspend fun getTrips(): List {
+ println(DataProvider.user?.uid)
val querySnapshot = Constants.db.collection("trips")
- .whereEqualTo("user", DataProvider.user?.uid)
- .get()
- .addOnSuccessListener { result ->
- for (document in result) {
- Log.d("TAG", "Trips recevied => ${document.toString()}")
- }
+ .whereEqualTo("username", DataProvider.user?.uid)
+ .get()
+ .addOnSuccessListener { result ->
+ for (document in result) {
+ Timber.tag(TAG).i("Trips recevied => ${document.toString()}")
}
- .addOnFailureListener { e ->
- // Handle any errors
- }
- .await()
-
- var trips: List = querySnapshot.toObjects()
+ }
+ .addOnFailureListener { e ->
+ // Handle any errors
+ }
+ .await()
- return trips
+ return querySnapshot.toObjects()
}
- suspend fun createOrUpdateTrip(tripName: Location): String{
- val trip = hashMapOf(
- "name" to tripName.name,
- "user" to (DataProvider.user?.uid ?: ""),
- "location" to {tripName}
+ suspend fun updateTrip(location: Location, documentName: String): String{
+
+// val trip = Trip(
+// documentName,
+// DataProvider.user?.uid
+// )
+
+ var trips = Constants.db.collection("trips").document(documentName)
+ .update("locations", FieldValue.arrayUnion(location))
+ .addOnSuccessListener { Timber.tag(TAG).d("DocumentSnapshot successfully written!") }
+ .addOnFailureListener { e -> Timber.tag(TAG).e( "Error writing document $e") }.await()
+ return trips.toString()
+ return ""
+ }
+ suspend fun createTrip(location: Location){
+ val trip = Trip(
+ location,
+ DataProvider.user?.uid
)
- var trips = Constants.db.collection("trips").document("iWywtwQ04MXkETRcaqDD")
- .update(trip)
- .addOnSuccessListener { Log.d(TAG, "DocumentSnapshot successfully written!") }
- .addOnFailureListener { e -> Log.w(TAG, "Error writing document", e) }.await()
- println("HERE ${trip.get("name")}")
- return trip.toString()
+ Constants.db.collection("trips").document(location.location_id.toString() + "::" + DataProvider.user?.uid)
+ .set(trip)
+ .addOnSuccessListener { Timber.tag(TAG).d( "Document ${location.location_id.toString() + "::" + DataProvider.user?.uid} successfully written!") }
+ .addOnFailureListener { e -> Timber.tag(TAG).e("Error writing document ${location.location_id.toString() + "::" + DataProvider.user?.uid} $e") }.await()
}
+
companion object {
private const val TAG = "FirestoreRepository"
}
}
-data class Trips(
- var data: List
-)
+
data class Trip(
- var name: String? = "",
+ var location: Location? = null,
var username: String? = "",
- var locations: List? = null,
)
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/repository/LocationsRepository.kt b/app/src/main/java/ie/coconnor/mobileappdev/repository/LocationsRepository.kt
index 1f9454a..f44c165 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/repository/LocationsRepository.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/repository/LocationsRepository.kt
@@ -12,13 +12,15 @@ class LocationsRepository {
suspend fun getLocations(apiKey: String, searchQuery: String, category: String): LocationResponse {
var locations = locationsService.getLocations(apiKey,searchQuery,category)
- if(!locations.data.isEmpty()){
+ if(locations.data.isNotEmpty()){
locations.data.forEach {
- var photos = it.location_id?.let { it1 -> getLocationPhotos(it1, apiKey) }
- it.imageUrl= photos?.data?.get(0)?.images?.medium?.url.toString()
-
+// var photos = it.location_id?.let { it1 -> getLocationPhotos(it1, apiKey) }
+// it.imageUrl= photos?.data?.get(0)?.images?.medium?.url.toString()
+//
var details = it.location_id?.let { it1 -> getLocationDetails(it1, apiKey) }
it.url = details?.web_url
+ it.latitude = details?.latitude
+ it.longitude = details?.longitude
}
}
return locations
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/service/LocationForegroundService.kt b/app/src/main/java/ie/coconnor/mobileappdev/service/LocationForegroundService.kt
index 6f03688..c368d8c 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/service/LocationForegroundService.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/service/LocationForegroundService.kt
@@ -13,12 +13,17 @@ import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.IBinder
-import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
-import com.google.android.gms.location.*
+import com.google.android.gms.location.FusedLocationProviderClient
+import com.google.android.gms.location.LocationCallback
+import com.google.android.gms.location.LocationRequest
+import com.google.android.gms.location.LocationResult
+import com.google.android.gms.location.LocationServices
+import com.google.android.gms.location.Priority
import ie.coconnor.mobileappdev.MainActivity
import ie.coconnor.mobileappdev.R
+import timber.log.Timber
class LocationForegroundService : Service() {
@@ -62,7 +67,7 @@ class LocationForegroundService : Service() {
return NotificationCompat.Builder(this, "location_channel")
.setContentTitle("Location Service")
.setContentText("Running")
- .setSmallIcon(R.drawable.ic_launcher_foreground)
+ .setSmallIcon(R.drawable.vector)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.build()
@@ -90,7 +95,7 @@ class LocationForegroundService : Service() {
) {
return
}
- Log.e("Location","asked for location")
+ Timber.tag(TAG).d("asked for location")
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)
}
@@ -126,4 +131,9 @@ class LocationForegroundService : Service() {
stopSelf()
}
+ companion object {
+ private const val TAG = "LocationForegroundService"
+
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/CardList.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/component/CardList.kt
similarity index 96%
rename from app/src/main/java/ie/coconnor/mobileappdev/ui/screens/CardList.kt
rename to app/src/main/java/ie/coconnor/mobileappdev/ui/component/CardList.kt
index 87211ee..6f7a3bc 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/CardList.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/ui/component/CardList.kt
@@ -1,4 +1,4 @@
-package ie.coconnor.mobileappdev.ui.screens
+package ie.coconnor.mobileappdev.ui.component
// CardList.kt
import androidx.compose.foundation.layout.Column
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/component/CustomDialog.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/component/CustomDialog.kt
index b4da148..8694645 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/component/CustomDialog.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/ui/component/CustomDialog.kt
@@ -43,7 +43,7 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
@Composable
-fun CustomDialog(value: String, title: String, setShowDialog: (Boolean) -> Unit, setValue: (String) -> Unit) {
+fun CustomDialog(value: String, title: String, textField: String, setShowDialog: (Boolean) -> Unit, setValue: (String) -> Unit) {
val txtFieldError = remember { mutableStateOf("") }
val txtField = remember { mutableStateOf(value) }
@@ -108,7 +108,7 @@ fun CustomDialog(value: String, title: String, setShowDialog: (Boolean) -> Unit,
.height(20.dp)
)
},
- placeholder = { Text(text = "Enter value") },
+ placeholder = { Text(text = textField) },
value = txtField.value,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
onValueChange = {
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/locations/LocationDetailsScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/locations/LocationDetailsScreen.kt
similarity index 79%
rename from app/src/main/java/ie/coconnor/mobileappdev/ui/screens/locations/LocationDetailsScreen.kt
rename to app/src/main/java/ie/coconnor/mobileappdev/ui/locations/LocationDetailsScreen.kt
index bd31ef1..a4605dd 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/locations/LocationDetailsScreen.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/ui/locations/LocationDetailsScreen.kt
@@ -1,40 +1,32 @@
-package ie.coconnor.mobileappdev.ui.screens.locations
+package ie.coconnor.mobileappdev.ui.locations
import android.annotation.SuppressLint
import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.ui.Alignment
-import androidx.compose.foundation.layout.Row
-import androidx.compose.ui.Modifier
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-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.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CardDefaults.shape
import androidx.compose.material3.ElevatedCard
-import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.SmallFloatingActionButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
@@ -47,41 +39,30 @@ import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController
import androidx.wear.compose.material.ContentAlpha
import androidx.wear.compose.material.LocalContentAlpha
-import coil.ImageLoader
-import coil.compose.AsyncImage
-import coil.request.CachePolicy
-import coil.request.ImageRequest
-import com.google.android.gms.maps.GoogleMap
-import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
-import com.google.android.gms.maps.model.Marker
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
import ie.coconnor.mobileappdev.R
import ie.coconnor.mobileappdev.models.Location
import ie.coconnor.mobileappdev.models.LocationDetails
import ie.coconnor.mobileappdev.models.locations.LocationDetailsViewModel
import ie.coconnor.mobileappdev.ui.component.UserRatingBar
-import ie.coconnor.mobileappdev.utils.SharedPref
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun LocationDetailsScreen(
navController: NavHostController,
viewModel: LocationDetailsViewModel,
- sharedPref: SharedPref
+ location: String?
) {
val locationDetails by viewModel.locationDetails.observeAsState()
-
val context = LocalContext.current
val tripAdvisorApiKey = context.getString(R.string.tripadvisor)
LaunchedEffect(Unit) {
- val location_id = sharedPref.getLocationId()
-// locationImageUrl = sharedPref.getLocationImageUrl()
- println(location_id)
- viewModel.fetchLocationDetails(location_id, tripAdvisorApiKey)
+ if (location != null) {
+ viewModel.fetchLocationDetails(location, tripAdvisorApiKey)
+ }
}
Scaffold(
@@ -143,23 +124,6 @@ fun LocationDetailsDisplay(
}
}
- AsyncImage(
- model = ImageRequest.Builder(LocalContext.current)
- .data(placeholder)
- .crossfade(true)
- .error(placeholder)
- .fallback(placeholder)
- .diskCachePolicy(CachePolicy.ENABLED)
- .memoryCachePolicy(CachePolicy.ENABLED)
- .build(),
- placeholder = painterResource(placeholder),
- contentDescription = "",
- contentScale = ContentScale.FillBounds,
- modifier = Modifier
- .background(color = MaterialTheme.colorScheme.secondary)
- .fillMaxWidth()
- .height(250.dp)
- )
Row(Modifier.padding(start = 16.dp, end = 24.dp, top = 16.dp)) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(
@@ -187,14 +151,17 @@ fun LocationDetailsDisplay(
fontSize = 10.sp
)
}
- val locationOnMap = LatLng(locationDetails!!.latitude?.toDoubleOrNull()!!, locationDetails!!.longitude?.toDoubleOrNull()!!)
- val cameraPositionState = rememberCameraPositionState {
- position = CameraPosition.fromLatLngZoom(locationOnMap, 10f)
- }
+
+ val locationOnMap = LatLng(locationDetails!!.latitude?.toDoubleOrNull()!!, locationDetails!!.longitude?.toDoubleOrNull()!!)
+ println(locationOnMap.toString())
+// val cameraPositionState = rememberCameraPositionState {
+// position = CameraPosition.fromLatLngZoom(locationOnMap, 10f)
+// }
GoogleMap(
modifier = Modifier.fillMaxWidth(),
- cameraPositionState = cameraPositionState) {
+// cameraPositionState = cameraPositionState
+ ) {
Marker(
state = MarkerState(position = locationOnMap),
title = locationDetails!!.name,
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/locations/LocationsScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/locations/LocationsScreen.kt
similarity index 90%
rename from app/src/main/java/ie/coconnor/mobileappdev/ui/screens/locations/LocationsScreen.kt
rename to app/src/main/java/ie/coconnor/mobileappdev/ui/locations/LocationsScreen.kt
index 6848f44..d7cfadb 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/locations/LocationsScreen.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/ui/locations/LocationsScreen.kt
@@ -1,4 +1,4 @@
-package ie.coconnor.mobileappdev.ui.screens.locations
+package ie.coconnor.mobileappdev.ui.locations
import android.annotation.SuppressLint
import android.content.Intent
@@ -21,6 +21,7 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.AlertDialog
@@ -54,8 +55,6 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity
@@ -83,16 +82,18 @@ fun LocationsScreen(viewModel: LocationsViewModel,
sharedPref: SharedPref)
{
val locations by viewModel.locations.observeAsState()
- val trips by viewModel.trips.observeAsState()
+// val trips by viewModel.trips.observeAsState()
var location by remember { mutableStateOf("Waterford, Ireland") }
+// var tripName by remember { mutableStateOf("") }
+
var showDialog by remember { mutableStateOf(false) }
+
val context = LocalContext.current
val tripAdvisorApiKey = context.getString(R.string.tripadvisor)
LaunchedEffect(Unit) {
viewModel.fetchTours(location, tripAdvisorApiKey)
- viewModel.fetchTrips()
}
Scaffold(
floatingActionButton = {
@@ -117,7 +118,7 @@ fun LocationsScreen(viewModel: LocationsViewModel,
onDismissRequest = {
showDialog = false
},
- title = { Text("Enter Text") },
+ title = { Text("Search new destination") },
text = {
TextField(
value = location,
@@ -149,11 +150,11 @@ fun LocationsScreen(viewModel: LocationsViewModel,
}
)
}
- // Display the list of credit cards
+
+ // Display the list of Locations
LazyColumn {
locations?.let {
items(it.data) { location ->
- //Text(text = tour.name)
StandardLocationCard(
location = location,
navController = navController,
@@ -168,12 +169,13 @@ fun LocationsScreen(viewModel: LocationsViewModel,
}
}
-@Preview(group = "Locations")
-@Composable
-fun PreviewStandCardItem(
- @PreviewParameter(SampleLocationProvider::class) location: Location){
- StandardLocationCard(location = location)
-}
+//@Preview(group = "Locations")
+//@Composable
+//fun PreviewStandCardItem(
+// @PreviewParameter(SampleLocationProvider::class) location: Location,
+//){
+// StandardLocationCard(location = location, trips = trips)
+//}
@Composable
fun StandardLocationCard(
@@ -186,10 +188,9 @@ fun StandardLocationCard(
navController: NavController = rememberNavController(),
sharedPref: SharedPref? = null,
viewModel: LocationsViewModel = hiltViewModel()
-
-) {
+ ) {
val placeholder = R.drawable.vector
-
+ val showTripDialog = false
ElevatedCard(
shape = shape,
elevation = CardDefaults.cardElevation(
@@ -267,21 +268,22 @@ fun StandardLocationCard(
Row(modifier = Modifier.align(Alignment.CenterStart)) {
TextButton(onClick = {
- location.location_id?.let { sharedPref?.setLocationId(it) }
- navController.navigate(Destinations.LocationDetailsScreen.route)
+ navController.navigate(Destinations.LocationDetailsScreen.route + "/${location.location_id}")
}) {
Text(text = "More Details")
}
}
-
Row(modifier = Modifier.align(Alignment.CenterEnd)) {
IconButton(onClick = {
- viewModel.createOrUpdateTrip(location)
-
- //viewModel.saveLocation(location)
+ viewModel.createTrip(location)
+ navController.navigate(Destinations.PlanScreen.route)
}) {
-
- Icon(Icons.Default.Favorite, contentDescription = null)
+ val saved = false
+ if(saved) {
+ Icon(Icons.Default.Favorite, contentDescription = null)
+ } else {
+ Icon(Icons.Default.FavoriteBorder, contentDescription = null)
+ }
}
val sendIntent = Intent(Intent.ACTION_SEND).apply {
@@ -294,7 +296,6 @@ fun StandardLocationCard(
IconButton(onClick = {
startActivity(context, shareIntent, null)
-
}) {
Icon(Icons.Default.Share, contentDescription = null)
}
@@ -305,20 +306,6 @@ fun StandardLocationCard(
}
}
-@Composable
-fun SeachButton(onClick: () -> Unit) {
- SmallFloatingActionButton(
- onClick = { },
- containerColor = MaterialTheme.colorScheme.secondaryContainer,
- contentColor = MaterialTheme.colorScheme.secondary,
- shape = CircleShape,
-
- ) {
- Icon(Icons.Filled.Search, "Search new location.")
- }
-}
-
-
class SampleLocationProvider : PreviewParameterProvider{
override val values = sequenceOf(
Location(
@@ -337,4 +324,5 @@ class SampleLocationProvider : PreviewParameterProvider{
address_obj = Address("High Street", "", "", "", "", "", "Street 3")
)
)
+
}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/login/LoginScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/login/LoginScreen.kt
index 4b1e120..3524368 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/login/LoginScreen.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/ui/login/LoginScreen.kt
@@ -79,7 +79,6 @@ fun LoginScreen(
MobileAppDevTheme {
Scaffold(
-// containerColor = MaterialTheme.colorScheme.primary
) { paddingValues ->
Column(
modifier = Modifier
@@ -90,18 +89,7 @@ fun LoginScreen(
Arrangement.spacedBy(8.dp),
Alignment.CenterHorizontally
) {
-// Image(
-// modifier = Modifier
-// .fillMaxWidth()
-// .padding(16.dp)
-// .weight(1f),
-// painter = painterResource(R.drawable.vector),
-// contentDescription = "app_logo",
-// contentScale = ContentScale.Fit,
-//// colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.tertiary)
-// )
- val drawableResource = R.drawable.vector//if (dar) R.drawable.xxx else R.drawable.xxx
-
+ val drawableResource = R.drawable.vector
Image(
painter = painterResource(id = drawableResource),
contentDescription = "Logo",
@@ -111,43 +99,6 @@ fun LoginScreen(
.weight(1f)
)
if (DataProvider.authState == AuthState.SignedOut || DataProvider.isAnonymous) {
-
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(paddingValues),
- verticalAlignment = Alignment.Bottom,
-
- ) {
- var text by rememberSaveable { mutableStateOf("") }
-
- OutlinedTextField(
- modifier = Modifier.weight(1f),
- value = text,
- onValueChange = { text = it },
- label = { Text("Username") }
- )
- Spacer(modifier = Modifier.width(8.dp))
- OutlinedTextField(
- modifier = Modifier.weight(1f),
- value = text,
- onValueChange = { text = it },
- label = { Text("Password")},
- visualTransformation = PasswordVisualTransformation(),
- )
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.Bottom,
- ) {
- ElevatedButton(
- onClick = {
-
- }
- ) {
- Text(text = "Create Account")
- }
- }
Button(
onClick = {
authViewModel.oneTapSignIn()
@@ -157,9 +108,6 @@ fun LoginScreen(
.fillMaxWidth()
.padding(horizontal = 16.dp),
shape = RoundedCornerShape(10.dp),
-// colors = ButtonDefaults.buttonColors(
-// containerColor = Color.White
-// )
) {
Image(
painter = painterResource(id = R.drawable.ic_google_logo),
@@ -196,9 +144,6 @@ fun LoginScreen(
.fillMaxWidth()
.padding(horizontal = 16.dp),
shape = RoundedCornerShape(10.dp),
-// colors = ButtonDefaults.buttonColors(
-// containerColor = Color.White
-// )
) {
Image(
painter = painterResource(id = R.drawable.ic_google_logo),
@@ -207,7 +152,6 @@ fun LoginScreen(
Text(
text = "Sign Out",
modifier = Modifier.padding(6.dp),
-// color = Color.Black.copy(alpha = 0.5f)
)
}
}
@@ -234,7 +178,7 @@ fun LoginScreen(
@Preview
@Composable
fun LoginScreenPreview() {
- // MobileAppDevTheme {
+// MobileAppDevTheme {
// LoginScreen(LoginScreen)
- //}
+// }
}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/navigation/Destinations.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/navigation/Destinations.kt
index b9be00b..21e4599 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/navigation/Destinations.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/ui/navigation/Destinations.kt
@@ -4,14 +4,13 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AccessTime
import androidx.compose.material.icons.outlined.Favorite
import androidx.compose.material.icons.outlined.Home
-import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.ui.graphics.vector.ImageVector
sealed class Destinations(
val route: String,
val title: String? = null,
- val icon: ImageVector? = null,
+ val icon: ImageVector? = null
) {
object LoginScreen : Destinations(
route = "LoginScreen",
@@ -31,18 +30,16 @@ sealed class Destinations(
icon = Icons.Outlined.Home
)
- object Favourite : Destinations(
- route = "favourite_screen",
- title = "Search",
- icon = Icons.Outlined.Search
- )
-
object PlanScreen : Destinations(
route = "PlanScreen",
title = "Plan",
icon = Icons.Outlined.Favorite
)
-
+ object PlanScreenWithId : Destinations(
+ route = "PlanScreenWithId",
+ title = "Plan",
+ icon = Icons.Outlined.Favorite
+ )
object TestScreen : Destinations(
route = "test_screen",
title = "Test",
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/plan/PlanScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/plan/PlanScreen.kt
index f56e140..9006a6f 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/plan/PlanScreen.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/ui/plan/PlanScreen.kt
@@ -1,6 +1,5 @@
package ie.coconnor.mobileappdev.ui.plan
-import android.util.Log
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@@ -23,12 +22,13 @@ import androidx.compose.foundation.shape.CornerBasedShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Create
+import androidx.compose.material.icons.filled.Map
import androidx.compose.material.icons.outlined.Favorite
import androidx.compose.material.icons.outlined.LocationOn
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ElevatedCard
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -43,9 +43,11 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
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.painterResource
import androidx.compose.ui.text.font.FontWeight
@@ -53,19 +55,29 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Popup
+import androidx.compose.ui.window.PopupProperties
+import androidx.compose.ui.zIndex
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import androidx.wear.compose.material.ContentAlpha
import androidx.wear.compose.material.LocalContentAlpha
+import com.google.android.gms.maps.model.CameraPosition
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.LatLngBounds
+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
import ie.coconnor.mobileappdev.R
import ie.coconnor.mobileappdev.models.DataProvider
import ie.coconnor.mobileappdev.models.plan.PlanViewModel
import ie.coconnor.mobileappdev.repository.Trip
-import ie.coconnor.mobileappdev.ui.component.CustomDialog
import ie.coconnor.mobileappdev.ui.navigation.Destinations
import ie.coconnor.mobileappdev.utils.SharedPref
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PlanScreen(
viewModel: PlanViewModel,
@@ -75,194 +87,263 @@ fun PlanScreen(
val trips by viewModel.trips.observeAsState()
var showDialog by remember { mutableStateOf(false) }
val tripName = remember { mutableStateOf("") }
-
- if(showDialog) {
- CustomDialog(value = "", title = "Create a trip", setShowDialog = {
- showDialog = it
-
- }) {
-
- tripName.value = it
- println(tripName)
-// viewModel.createOrUpdateTrip(tripName.value)
- Log.i("PlanScreen", "PlanScreen : ${it.toString()}")
- }
- }
+ var showPopup by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(Unit) {
viewModel.fetchTrips()
}
+// val locationOnMap = LatLng(trips.!!.latitude?.toDoubleOrNull()!!, locationDetails!!.longitude?.toDoubleOrNull()!!)
+// println(locationOnMap.toString())
+// val cameraPositionState = rememberCameraPositionState {
+// position = CameraPosition.fromLatLngZoom(locationOnMap, 10f)
+// }
+
Scaffold(
floatingActionButton = {
- if(!trips.isNullOrEmpty()) {
- ExtendedFloatingActionButton(
- onClick = { showDialog = true },
- containerColor = MaterialTheme.colorScheme.secondaryContainer,
- contentColor = MaterialTheme.colorScheme.secondary,
- shape = CircleShape,
+ ExtendedFloatingActionButton(
+ onClick = {
+ showPopup = true
+ },
+ containerColor = MaterialTheme.colorScheme.secondaryContainer,
+ contentColor = MaterialTheme.colorScheme.secondary,
+ shape = CircleShape,
- ) {
- Icon(Icons.Filled.Create, "Search for new location.")
- Text("Create a trip")
- }
+ ) {
+ Icon(Icons.Filled.Map, "Map View.")
+ Text("Map View")
}
}
) { paddingValues ->
- if (DataProvider.isAuthenticated) {
- Column(
- modifier = Modifier
- .padding(paddingValues)
- .fillMaxSize(),
- ) {
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(
- text = "Plans",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 50.sp,
- fontWeight = FontWeight.Bold
- )
- }
- }
- if(trips.isNullOrEmpty()) {
- Column(
- modifier = Modifier
- .padding(paddingValues)
- .fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center
- ) {
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
-
- Icon(
- imageVector = Icons.Outlined.Favorite,
- contentDescription = "Favourite"
- )
-
- Text(
- text = "Save your locations you'd like to visit",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 15.sp,
- fontWeight = FontWeight.Bold
- )
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- imageVector = Icons.Outlined.LocationOn,
- contentDescription = "Show saves on map"
- )
- Text(
- text = "See your locations on a map",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 15.sp,
- fontWeight = FontWeight.Bold
- )
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize(),
+ ) {
+ PopupBox(
+ popupWidth = 500F,
+ popupHeight = 600F,
+ showPopup = showPopup,
+ onClickOutside = { showPopup = false },
+ content = {
+ val boundsBuilder = LatLngBounds.builder()
+ for (trip in trips!!){
+ val location = LatLng(trip.location?.latitude?.toDoubleOrNull()!!, trip.location?.longitude?.toDoubleOrNull()!!)
+ boundsBuilder.include(location)
+ }
+ val bounds = boundsBuilder.build()
+ val cameraPositionState = rememberCameraPositionState {
+ position = CameraPosition.fromLatLngZoom(bounds.center, 20f)
}
- Spacer(modifier = Modifier.height(40.dp))
-
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- OutlinedButton(
- onClick = {
- showDialog = true
- },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- shape = RoundedCornerShape(10.dp),
- ) {
- Text(
- text = "+\tCreate a new trip",
- modifier = Modifier.padding(6.dp),
- fontSize = 15.sp,
- color = MaterialTheme.colorScheme.onBackground
+ GoogleMap(
+ modifier = Modifier.fillMaxSize(),
+ cameraPositionState = cameraPositionState
+ ){
+ for (trip in trips!!) {
+ println("here " + trip.location?.latitude)
+ val location = LatLng(trip.location?.latitude?.toDoubleOrNull()!!, trip.location?.longitude?.toDoubleOrNull()!!)
+ Marker(
+ state = MarkerState(position = location),
+ title = trip.location?.name,
+ snippet = "Marker in ${trip.location?.name}"
)
}
}
}
- } else {
- LazyColumn {
- trips?.let {
- items(it) { trip ->
- //Text(text = tour.name)
- StandardPlanCard(
- trip = trip,
- navController = navController,
- sharedPref = sharedPref
- )
- Spacer(modifier = Modifier.height(10.dp)) // Add a divider between items
- }
- }
- }
- }
- } else {
+ )
Row(
modifier = Modifier,
verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ text = "Plans",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 50.sp,
+ fontWeight = FontWeight.Bold
+ )
+ OutlinedButton(
+ onClick = {
- ) {
- Box(
- contentAlignment = Alignment.CenterStart,
+ },
modifier = Modifier
- .padding(30.dp)
- .background(
- color = Color.LightGray.copy(alpha = 0.5f),
- shape = RoundedCornerShape(20.dp)
- )
- .height(150.dp)
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ shape = RoundedCornerShape(10.dp),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = Color.White,
+ )
) {
+ Image(
+ painter = painterResource(id = R.drawable.ic_google_logo),
+ contentDescription = "Sign Out"
+ )
+
+ Text(
+ text = "Start Tour",
+ modifier = Modifier.padding(6.dp),
+ color = Color.Black.copy()
+ )
+ }
+ }
+
+ if (DataProvider.isAuthenticated && !DataProvider.isAnonymous) {
+
+ if(trips.isNullOrEmpty()) {
Column(
- modifier = Modifier,
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
) {
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+
+ Icon(
+ imageVector = Icons.Outlined.Favorite,
+ contentDescription = "Favourite"
+ )
+ Text(
+ text = "Save your locations you'd like to visit",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 15.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
Row(
- modifier = Modifier
- .padding(15.dp),
+ modifier = Modifier,
verticalAlignment = Alignment.CenterVertically,
) {
+ Icon(
+ imageVector = Icons.Outlined.LocationOn,
+ contentDescription = "Show saves on map"
+ )
Text(
- text = "Log in to manage your tours and easily plan your next tour",
- style = MaterialTheme.typography.bodyMedium,
+ text = "See your locations on a map",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 15.sp,
+ fontWeight = FontWeight.Bold
)
}
+ Spacer(modifier = Modifier.height(40.dp))
+
Row(
modifier = Modifier,
verticalAlignment = Alignment.CenterVertically,
) {
+
OutlinedButton(
onClick = {
- navController.navigate(Destinations.LoginScreen.route)
+ showDialog = true
},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
shape = RoundedCornerShape(10.dp),
- colors = ButtonDefaults.buttonColors(
- containerColor = Color.White,
-
- )
) {
Text(
- text = "Sign In",
+ text = "+\tCreate a new trip",
modifier = Modifier.padding(6.dp),
- color = Color.Black.copy()
+ fontSize = 15.sp,
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ }
+ }
+ }
+ } else {
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ LazyColumn {
+ trips?.let {
+ items(it) { trip ->
+
+ //Text(text = tour.name)
+ StandardPlanCard(
+ trip = trip,
+ navController = navController,
+ sharedPref = sharedPref
+ )
+ Spacer(modifier = Modifier.height(10.dp)) // Add a divider between items
+ }
+ }
+ }
+ }
+ }
+ } else {
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+
+ ) {
+ Box(
+ contentAlignment = Alignment.CenterStart,
+ modifier = Modifier
+ .padding(30.dp)
+ .background(
+ color = Color.LightGray.copy(alpha = 0.5f),
+ shape = RoundedCornerShape(20.dp)
)
+ .height(150.dp)
+
+ ) {
+ Column(
+ modifier = Modifier,
+ ) {
+
+ Row(
+ modifier = Modifier
+ .padding(15.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ text = "Log in to manage your tours and easily plan your next tour",
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ OutlinedButton(
+ onClick = {
+ navController.navigate(Destinations.LoginScreen.route)
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ shape = RoundedCornerShape(10.dp),
+ colors = ButtonDefaults.buttonColors(
+ containerColor = Color.White,
+
+ )
+ ) {
+ Text(
+ text = "Sign In",
+ modifier = Modifier.padding(6.dp),
+ color = Color.Black.copy()
+ )
+ }
+ }
}
}
}
@@ -327,7 +408,7 @@ fun StandardPlanCard(
Row(
Modifier
.fillMaxWidth()
- .height(72.dp)
+// .height(72.dp)
.padding(start = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
@@ -346,29 +427,11 @@ fun StandardPlanCard(
Spacer(modifier = Modifier.width(32.dp))
Column(Modifier.fillMaxWidth()) {
- Text(text = trip.name.toString(), style = MaterialTheme.typography.headlineMedium)
+ Text(text = trip.location?.name.toString(), style = MaterialTheme.typography.headlineMedium)
}
}
-// AsyncImage(
-// model = ImageRequest.Builder(LocalContext.current)
-// .data(location.imageUrl)
-// .crossfade(true)
-// .diskCacheKey(location.imageUrl)
-// .memoryCacheKey(location.imageUrl)
-// .error(placeholder)
-// .fallback(placeholder)
-// .diskCachePolicy(CachePolicy.ENABLED)
-// .memoryCachePolicy(CachePolicy.ENABLED)
-// .build(),
-// placeholder = painterResource(placeholder),
-// contentDescription = "",
-// contentScale = ContentScale.FillBounds,
-// modifier = Modifier
-// .background(color = MaterialTheme.colorScheme.secondary)
-// .fillMaxWidth()
-// .height(250.dp)
-// )
+
Row(Modifier.padding(start = 16.dp, end = 24.dp, top = 16.dp)) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(
@@ -385,6 +448,43 @@ fun StandardPlanCard(
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
+ }
+ }
+ }
+}
+
+
+@Composable
+fun PopupBox(popupWidth: Float, popupHeight:Float, showPopup: Boolean, onClickOutside: () -> Unit, content: @Composable() () -> Unit) {
+
+ if (showPopup) {
+ // full screen background
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+// .background(Color.Green)
+ .zIndex(10F),
+ contentAlignment = Alignment.Center
+ ) {
+ // popup
+ Popup(
+ alignment = Alignment.Center,
+ properties = PopupProperties(
+ excludeFromSystemGesture = true,
+ ),
+ // to dismiss on click outside
+ onDismissRequest = { onClickOutside() }
+ ) {
+ Box(
+ Modifier
+ .width(popupWidth.dp)
+ .height(popupHeight.dp)
+// .background(Color.White)
+ .clip(RoundedCornerShape(4.dp)),
+ contentAlignment = Alignment.Center
+ ) {
+ content()
+ }
}
}
}
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/ArticlesScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/ArticlesScreen.kt
deleted file mode 100644
index b952861..0000000
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/ArticlesScreen.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package ie.coconnor.mobileappdev.ui.screens
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import ie.coconnor.mobileappdev.models.Constants.db
-
-private val cardsRef = db.collection("attractions")
-@Composable
-fun ArticlesScreen(
-
-){
- Scaffold(
-// topBar = {
-// CustomAppBar(
-// currentScreen = "TopAppBar",
-// showBackButton = true,
-// onBackButtonClick = { }
-// )
-// }
- ) { paddingValues ->
- Column(
- modifier = Modifier
- .padding(paddingValues)
- .fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center
- ) {
- Text(text = "About Screen")
- }
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/ExploreScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/ExploreScreen.kt
deleted file mode 100644
index 356a066..0000000
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/ExploreScreen.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package ie.coconnor.mobileappdev.ui.screens
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.MutableState
-import androidx.compose.ui.tooling.preview.Preview
-import ie.coconnor.mobileappdev.AuthViewModel
-
-@Composable
-fun ExploreScreen() {
-
-
-}
-
-@Preview
-@Composable
-fun ExploreScreenPreview(){
- ExploreScreen()
-}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/NewsScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/NewsScreen.kt
deleted file mode 100644
index a6984df..0000000
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/NewsScreen.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package ie.coconnor.mobileappdev.ui.screens
-
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import ie.coconnor.mobileappdev.CustomAppBar
-
-@Composable
-fun NewsScreen() {
- Scaffold(
- topBar = {
- CustomAppBar(
- currentScreen = "TopAppBar",
- showBackButton = true,
- onBackButtonClick = { }
- )
- } ) { paddingValues ->
- Column(
- modifier = Modifier
- .padding(paddingValues)
- .fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center
- ) {
- Text(text = "News Screen")
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/SettingsScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/SettingsScreen.kt
index 1de83ee..d689baa 100644
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/SettingsScreen.kt
+++ b/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/SettingsScreen.kt
@@ -3,17 +3,22 @@ package ie.coconnor.mobileappdev.ui.screens
import android.annotation.SuppressLint
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
+import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
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.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.ButtonDefaults
@@ -33,7 +38,9 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
@@ -43,7 +50,12 @@ import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
+import coil.compose.AsyncImage
import coil.imageLoader
+import coil.request.CachePolicy
+import coil.request.ImageRequest
+import com.google.firebase.auth.ktx.auth
+import com.google.firebase.ktx.Firebase
import ie.coconnor.mobileappdev.AuthViewModel
import ie.coconnor.mobileappdev.R
import ie.coconnor.mobileappdev.models.DataProvider
@@ -55,12 +67,14 @@ import ie.coconnor.mobileappdev.utils.UIThemeController
@Composable
fun SettingsScreen(navController: NavHostController, authViewModel: AuthViewModel) {
val context = LocalContext.current
+ val placeholder = R.drawable.vector
Scaffold(
)
{
Column(
modifier = Modifier
+ .verticalScroll(rememberScrollState())
.fillMaxSize(),
) {
Row(
@@ -140,262 +154,319 @@ fun SettingsScreen(navController: NavHostController, authViewModel: AuthViewMode
}
}
}
- }
-
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(
- text = " Theme",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 30.sp,
- fontWeight = FontWeight.Medium
- )
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Spacer(modifier = Modifier.width(28.dp))
-
- Text(
- text = "Dark Mode",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 20.sp,
- fontWeight = FontWeight.Light
- )
-
- val isDarkMode by UIThemeController.isDarkMode.collectAsState()
+ } else {
+ val user = Firebase.auth.currentUser
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ text = " Welcome",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 30.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+ Column(
+ modifier = Modifier,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(
+ text = "${DataProvider.getDisplayName(user)}",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 30.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+ Column(
+ modifier = Modifier,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ println(DataProvider.getProfilePhoto(user))
+ AsyncImage(
+ model = ImageRequest.Builder(LocalContext.current)
+ .data(DataProvider.getProfilePhoto(user))
+ .crossfade(true)
+ .diskCacheKey(DataProvider.getProfilePhoto(user))
+ .memoryCacheKey(DataProvider.getProfilePhoto(user))
+ .error(placeholder)
+ .fallback(placeholder)
+ .diskCachePolicy(CachePolicy.ENABLED)
+ .memoryCachePolicy(CachePolicy.ENABLED)
+ .build(),
+ placeholder = painterResource(placeholder),
+ contentDescription = "",
+ modifier = Modifier
+ .height(150.dp)
+ .clip(
+ RoundedCornerShape(
+ topEnd = 80.dp,
+ topStart = 80.dp,
+ bottomEnd = 80.dp,
+ bottomStart = 80.dp
+ )
+ )
+ )
+ }
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ text = " Theme",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 30.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Spacer(modifier = Modifier.width(28.dp))
- Switch(
- checked = isDarkMode,
- onCheckedChange = {
- if (it) {
- println("The switch is checked.")
- UIThemeController.updateUITheme(true)
+ Text(
+ text = "Dark Mode",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Light
+ )
+
+ val isDarkMode by UIThemeController.isDarkMode.collectAsState()
+
+ Switch(
+ checked = isDarkMode,
+ onCheckedChange = {
+ if (it) {
+ println("The switch is checked.")
+ UIThemeController.updateUITheme(true)
// sharedPref.setDarkMode(true)
- } else {
- println("The switch is unchecked.")
- UIThemeController.updateUITheme(false)
+ } else {
+ println("The switch is unchecked.")
+ UIThemeController.updateUITheme(false)
// sharedPref.setDarkMode(false)
- }
- },
- )
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(
- text = " Permissions",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 30.sp,
- fontWeight = FontWeight.Medium
- )
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Column(
+ }
+ },
+ )
+ }
+ Row(
modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
) {
-
- Row(
+ Text(
+ text = " Permissions",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 30.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Column(
modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
) {
- Spacer(modifier = Modifier.width(28.dp))
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
- Text(
- text = "Allow Location Service",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 20.sp,
- fontWeight = FontWeight.Light
- )
+ Spacer(modifier = Modifier.width(28.dp))
- val foregroundServiceIsRunning = true
- Switch(
- checked = foregroundServiceIsRunning,
- onCheckedChange = {
- if (it) {
- println("The switch is checked.")
- } else {
- println("The switch is unchecked.")
- }
- },
- )
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Spacer(modifier = Modifier.width(28.dp))
+ Text(
+ text = "Allow Location Service",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Light
+ )
- Text(
- text = "Some Permission",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 20.sp,
- fontWeight = FontWeight.Light
- )
- SwitchWithIconExample {
+ val foregroundServiceIsRunning = true
+ Switch(
+ checked = foregroundServiceIsRunning,
+ onCheckedChange = {
+ if (it) {
+ println("The switch is checked.")
+ } else {
+ println("The switch is unchecked.")
+ }
+ },
+ )
+ }
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Spacer(modifier = Modifier.width(28.dp))
+ Text(
+ text = "Some Permission",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Light
+ )
+ SwitchWithIconExample {
+
+ }
}
}
}
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(
- text = "Text to Speech Settings",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 30.sp,
- fontWeight = FontWeight.Medium
- )
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Column(
+ Row(
modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
) {
-
- Row(
+ Text(
+ text = "Text to Speech Settings",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 30.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Column(
modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
) {
- Spacer(modifier = Modifier.width(28.dp))
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
- Text(
- text = "Auto Play",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 20.sp,
- fontWeight = FontWeight.Light
- )
- SwitchWithIconExample {
+ Spacer(modifier = Modifier.width(28.dp))
+
+ Text(
+ text = "Auto Play",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Light
+ )
+ SwitchWithIconExample {
+ }
}
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Spacer(modifier = Modifier.width(28.dp))
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Spacer(modifier = Modifier.width(28.dp))
- Text(
- text = "Text to Speech langauge",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 20.sp,
- fontWeight = FontWeight.Light
- )
- SwitchWithIconExample {
+ Text(
+ text = "Text to Speech langauge",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Light
+ )
+ SwitchWithIconExample {
+ }
}
}
}
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(
- text = "Image caching",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 30.sp,
- fontWeight = FontWeight.Medium
- )
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Column(
+ Row(
modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
) {
- Row(
+ Text(
+ text = "Image caching",
+ modifier = Modifier
+ .padding(10.dp),
+ fontSize = 30.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Column(
modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- OutlinedButton(
- onClick = {
- context.imageLoader.diskCache?.clear()
- context.imageLoader.memoryCache?.clear()
- },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- shape = RoundedCornerShape(10.dp),
+ ) {
+ Row(
+ modifier = Modifier,
+ verticalAlignment = Alignment.CenterVertically,
) {
- Text(
- text = "Clear Image Cache",
- modifier = Modifier.padding(6.dp),
- color = MaterialTheme.colorScheme.onBackground
- )
+ OutlinedButton(
+ onClick = {
+ context.imageLoader.diskCache?.clear()
+ context.imageLoader.memoryCache?.clear()
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ shape = RoundedCornerShape(10.dp),
+ ) {
+ Text(
+ text = "Clear Image Cache",
+ modifier = Modifier.padding(6.dp),
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ }
}
}
}
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(
- text = "Account Settings",
- modifier = Modifier
- .padding(10.dp),
- fontSize = 30.sp,
- fontWeight = FontWeight.Medium
- )
- }
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Column(
- modifier = Modifier.fillMaxWidth(),
- ) {
-
+ if (DataProvider.isAuthenticated && !DataProvider.isAnonymous) {
Row(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier,
verticalAlignment = Alignment.CenterVertically,
-
) {
- if(DataProvider.isAuthenticated) {
-
- OutlinedButton(
- onClick = {
- authViewModel.signOut()
- },
+ Text(
+ text = "Account Settings",
modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- shape = RoundedCornerShape(10.dp),
+ .padding(10.dp),
+ fontSize = 30.sp,
+ fontWeight = FontWeight.Medium
+ )
+ }
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
) {
- Image(
- painter = painterResource(id = R.drawable.ic_google_logo),
- contentDescription = "Sign Out"
- )
- Text(
- text = "Sign Out",
- modifier = Modifier.padding(6.dp),
- color = MaterialTheme.colorScheme.onBackground
- )
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+
+ ) {
+ if (DataProvider.isAuthenticated) {
+
+ OutlinedButton(
+ onClick = {
+ authViewModel.signOut()
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ shape = RoundedCornerShape(10.dp),
+ ) {
+ Image(
+ painter = painterResource(id = R.drawable.ic_google_logo),
+ contentDescription = "Sign Out"
+ )
+
+ Text(
+ text = "Sign Out",
+ modifier = Modifier.padding(6.dp),
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ }
+ }
}
}
}
@@ -466,3 +537,20 @@ fun SettingScreenPreview() {
SettingsScreen(navController, authViewModel)
}
+
+@Composable
+fun RoundImage(
+ image: Painter, modifier: Modifier = Modifier
+) {
+ Image(
+ painter = image,
+ contentDescription = "Profile image",
+ modifier = modifier
+ .aspectRatio(1f, matchHeightConstraintsFirst = true)
+ .border(
+ width = 6.dp, color = Color.White, shape = CircleShape
+ )
+ .padding(3.dp)
+ .clip(CircleShape)
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/SignOutScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/SignOutScreen.kt
deleted file mode 100644
index 41d5ed2..0000000
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/screens/SignOutScreen.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package ie.coconnor.mobileappdev.ui.screens
-
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import ie.coconnor.mobileappdev.CustomAppBar
-
-@Composable
-fun SignOutScreen() {
- Scaffold(
- topBar = {
- CustomAppBar(
- currentScreen = "TopAppBar",
- showBackButton = true,
- onBackButtonClick = { }
- )
- } ) { paddingValues ->
- Column(
- modifier = Modifier
- .fillMaxSize()
- .padding(paddingValues),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center
- ) {
- Text(text = " Settings Screen")
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/ie/coconnor/mobileappdev/ui/signup/SignUpScreen.kt b/app/src/main/java/ie/coconnor/mobileappdev/ui/signup/SignUpScreen.kt
deleted file mode 100644
index 608ea15..0000000
--- a/app/src/main/java/ie/coconnor/mobileappdev/ui/signup/SignUpScreen.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-package ie.coconnor.mobileappdev.ui.login
-
-import androidx.compose.runtime.setValue
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.getValue
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.foundation.layout.Box
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.width
-import androidx.compose.ui.unit.dp
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.Row
-import androidx.compose.material3.ElevatedButton
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.navigation.NavHostController
-import androidx.navigation.compose.rememberNavController
-import ie.coconnor.mobileappdev.CustomAppBar
-import ie.coconnor.mobileappdev.R
-
-@Composable
-fun SignUpScreen(navController: NavHostController) {
-
- Scaffold(
-// modifier = Modifier.fillMaxSize(),
- topBar = {
- CustomAppBar(
- currentScreen = "TopAppBar",
- showBackButton = true,
- onBackButtonClick = {
- navController.popBackStack()
- }
- )
- }
- ) { paddingValues ->
- Column(
- modifier = Modifier
- .fillMaxSize()
- .padding(paddingValues),
- horizontalAlignment = Alignment.CenterHorizontally
-
- //verticalArrangement = Arrangement.Center
- ){
- Box(
- modifier = Modifier,
- contentAlignment = Alignment.TopCenter,
- ) {
- Image(
- painter = painterResource(id = R.drawable.red_hat_logo),
- contentDescription = null,
- contentScale = ContentScale.Fit
- )
- }
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(paddingValues),
- verticalAlignment = Alignment.Bottom,
-
- ) {
- var text by rememberSaveable { mutableStateOf("") }
-
- OutlinedTextField(
- modifier = Modifier.weight(1f),
- value = text,
- onValueChange = { text = it },
- label = { Text("Username") }
- )
- Spacer(modifier = Modifier.width(8.dp))
- OutlinedTextField(
- modifier = Modifier.weight(1f),
- value = text,
- onValueChange = { text = it },
- label = { Text("Password")},
- visualTransformation = PasswordVisualTransformation(),
- )
- }
- Row(
- modifier = Modifier,
- verticalAlignment = Alignment.Bottom,
- ) {
- ElevatedButton(
- onClick = {
- /* Do something! */
- }
- ) {
- Text(text = "Create Account")
- }
- }
- }
-
- }
-}
-
-@Preview
-@Composable
-fun SignUpScreenPreview(){
- val navController = rememberNavController()
- SignUpScreen(navController)
-}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index b772924..85d80b8 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,7 +1,7 @@
buildscript {
dependencies {
classpath(libs.google.services)
- classpath("com.google.dagger:hilt-android-gradle-plugin:2.51")
+ classpath("com.google.dagger:hilt-android-gradle-plugin:2.51.1")
}
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6dcbb28..b5136c5 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,28 +1,28 @@
[versions]
-agp = "8.3.1"
-kotlin = "1.9.0"
-coreKtx = "1.10.1"
+agp = "8.3.2"
+kotlin = "1.9.23"
+coreKtx = "1.13.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
-lifecycleRuntimeKtx = "2.6.1"
-activityCompose = "1.7.0"
-composeBom = "2023.08.00"
+lifecycleRuntimeKtx = "2.7.0"
+activityCompose = "1.9.0"
+composeBom = "2024.04.01"
googleServices = "4.4.1"
firebaseAuth = "22.3.1"
material3Android = "1.2.1"
constraintlayoutCompose = "1.0.1"
transportationConsumer = "2.1.0"
-firebaseFirestoreKtx = "24.11.0"
+firebaseFirestoreKtx = "24.11.1"
googleAndroidLibrariesMapsplatformSecretsGradlePlugin = "2.0.1"
playServicesMaps = "18.2.0"
appcompat = "1.6.1"
constraintlayout = "2.1.4"
firebaseDatabaseKtx = "20.3.1"
-accompanist = "0.32.0"
+accompanist = "0.34.0"
composeMaterial = "1.3.1"
supportAnnotations = "28.0.0"
-kotlinStdlib = "1.9.22"
+kotlinStdlib = "1.9.23"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e708b1c..e644113 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 0f24719..b82aa23 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
-#Fri Mar 15 20:28:43 GMT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 4f906e0..1aa94a4 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
#
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,99 @@
#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +119,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,88 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=`expr $i + 1`
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index ac1b06f..7101f8e 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..5db72dd
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:recommended"
+ ]
+}