diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 900c25c..1d8dd7d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -49,14 +49,14 @@ android { dependencies { implementation(project(":simulator")) - implementation("androidx.core:core-ktx:1.9.0") - implementation("androidx.appcompat:appcompat:1.6.1") - implementation("com.google.android.material:material:1.11.0") + implementation("androidx.core:core-ktx:1.13.1") + implementation("androidx.appcompat:appcompat:1.7.0") + implementation("com.google.android.material:material:1.12.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") // Places Library (required for search places) - implementation("com.google.android.libraries.places:places:3.3.0") - implementation("com.google.android.gms:play-services-maps:18.2.0") + implementation("com.google.android.libraries.places:places:4.0.0") + implementation("com.google.android.gms:play-services-maps:19.0.0") testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.5") - androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + androidTestImplementation("androidx.test.ext:junit:1.2.1") + androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") } \ No newline at end of file diff --git a/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsActivity.kt b/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsActivity.kt index 615a050..40cb359 100644 --- a/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsActivity.kt +++ b/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsActivity.kt @@ -1,5 +1,6 @@ package me.amitshekhar.ridesharing.ui.maps +import android.Manifest import android.content.Intent import android.content.pm.PackageManager import android.graphics.Color @@ -8,12 +9,16 @@ import android.os.Looper import android.util.Log import android.view.View import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat import com.google.android.gms.common.api.Status 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 com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.OnMapReadyCallback @@ -38,7 +43,7 @@ import me.amitshekhar.ridesharing.utils.MapUtils import me.amitshekhar.ridesharing.utils.PermissionUtils import me.amitshekhar.ridesharing.utils.ViewUtils -class MapsActivity : AppCompatActivity(), MapsView, OnMapReadyCallback { +class MapsActivity : AppCompatActivity(), MapsPresenter, OnMapReadyCallback { companion object { private const val TAG = "MapsActivity" @@ -48,7 +53,7 @@ class MapsActivity : AppCompatActivity(), MapsView, OnMapReadyCallback { } private lateinit var binding: ActivityMapsBinding - private lateinit var presenter: MapsPresenter + private lateinit var presenter: MapsView private lateinit var googleMap: GoogleMap private var fusedLocationProviderClient: FusedLocationProviderClient? = null private lateinit var locationCallback: LocationCallback @@ -71,7 +76,7 @@ class MapsActivity : AppCompatActivity(), MapsView, OnMapReadyCallback { ViewUtils.enableTransparentStatusBar(window) val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment mapFragment.getMapAsync(this) - presenter = MapsPresenter(NetworkService()) + presenter = MapsView(NetworkService()) presenter.onAttach(this) setUpClickListener() } @@ -134,14 +139,33 @@ class MapsActivity : AppCompatActivity(), MapsView, OnMapReadyCallback { private fun enableMyLocationOnMap() { googleMap.setPadding(0, ViewUtils.dpToPx(48f), 0, 0) + 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 + } googleMap.isMyLocationEnabled = true } private fun setUpLocationListener() { - fusedLocationProviderClient = FusedLocationProviderClient(this) + fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this) + // for getting the current location update after every 2 seconds - val locationRequest = LocationRequest().setInterval(2000).setFastestInterval(2000) - .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) + val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 2000) + .setMinUpdateIntervalMillis(2000) + .build() locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { @@ -162,6 +186,23 @@ class MapsActivity : AppCompatActivity(), MapsView, OnMapReadyCallback { // For example: Update the location of user on server } } + 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 + } fusedLocationProviderClient?.requestLocationUpdates( locationRequest, locationCallback, diff --git a/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsPresenter.kt b/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsPresenter.kt index 69abc98..152a22a 100644 --- a/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsPresenter.kt +++ b/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsPresenter.kt @@ -1,127 +1,27 @@ package me.amitshekhar.ridesharing.ui.maps -import android.util.Log import com.google.android.gms.maps.model.LatLng -import me.amitshekhar.ridesharing.data.network.NetworkService -import me.amitshekhar.ridesharing.simulator.WebSocket -import me.amitshekhar.ridesharing.simulator.WebSocketListener -import me.amitshekhar.ridesharing.utils.Constants -import org.json.JSONObject -class MapsPresenter(private val networkService: NetworkService) : WebSocketListener { +interface MapsPresenter { - companion object { - private const val TAG = "MapsPresenter" - } + fun showNearbyCabs(latLngList: List) - private var view: MapsView? = null - private lateinit var webSocket: WebSocket + fun informCabBooked() - fun onAttach(view: MapsView?) { - this.view = view - webSocket = networkService.createWebSocket(this) - webSocket.connect() - } + fun showPath(latLngList: List) - fun onDetach() { - webSocket.disconnect() - view = null - } + fun updateCabLocation(latLng: LatLng) - fun requestNearbyCabs(latLng: LatLng) { - val jsonObject = JSONObject() - jsonObject.put(Constants.TYPE, Constants.NEAR_BY_CABS) - jsonObject.put(Constants.LAT, latLng.latitude) - jsonObject.put(Constants.LNG, latLng.longitude) - webSocket.sendMessage(jsonObject.toString()) - } + fun informCabIsArriving() - fun requestCab(pickUpLatLng: LatLng, dropLatLng: LatLng) { - val jsonObject = JSONObject() - jsonObject.put("type", "requestCab") - jsonObject.put("pickUpLat", pickUpLatLng.latitude) - jsonObject.put("pickUpLng", pickUpLatLng.longitude) - jsonObject.put("dropLat", dropLatLng.latitude) - jsonObject.put("dropLng", dropLatLng.longitude) - webSocket.sendMessage(jsonObject.toString()) - } + fun informCabArrived() - private fun handleOnMessageNearbyCabs(jsonObject: JSONObject) { - val nearbyCabLocations = arrayListOf() - val jsonArray = jsonObject.getJSONArray(Constants.LOCATIONS) - for (i in 0 until jsonArray.length()) { - val lat = (jsonArray.get(i) as JSONObject).getDouble(Constants.LAT) - val lng = (jsonArray.get(i) as JSONObject).getDouble(Constants.LNG) - val latLng = LatLng(lat, lng) - nearbyCabLocations.add(latLng) - } - view?.showNearbyCabs(nearbyCabLocations) - } + fun informTripStart() - override fun onConnect() { - Log.d(TAG, "onConnect") - } + fun informTripEnd() - override fun onMessage(data: String) { - Log.d(TAG, "onMessage data : $data") - val jsonObject = JSONObject(data) - when (jsonObject.getString(Constants.TYPE)) { - Constants.NEAR_BY_CABS -> { - handleOnMessageNearbyCabs(jsonObject) - } - Constants.CAB_BOOKED -> { - view?.informCabBooked() - } - Constants.PICKUP_PATH, Constants.TRIP_PATH -> { - val jsonArray = jsonObject.getJSONArray("path") - val pickUpPath = arrayListOf() - for (i in 0 until jsonArray.length()) { - val lat = (jsonArray.get(i) as JSONObject).getDouble("lat") - val lng = (jsonArray.get(i) as JSONObject).getDouble("lng") - val latLng = LatLng(lat, lng) - pickUpPath.add(latLng) - } - view?.showPath(pickUpPath) - } - Constants.LOCATION -> { - val latCurrent = jsonObject.getDouble("lat") - val lngCurrent = jsonObject.getDouble("lng") - view?.updateCabLocation(LatLng(latCurrent, lngCurrent)) - } - Constants.CAB_IS_ARRIVING -> { - view?.informCabIsArriving() - } - Constants.CAB_ARRIVED -> { - view?.informCabArrived() - } - Constants.TRIP_START -> { - view?.informTripStart() - } - Constants.TRIP_END -> { - view?.informTripEnd() - } - } - } + fun showRoutesNotAvailableError() - override fun onDisconnect() { - Log.d(TAG, "onDisconnect") - } - - override fun onError(error: String) { - Log.d(TAG, "onError : $error") - val jsonObject = JSONObject(error) - when (jsonObject.getString(Constants.TYPE)) { - Constants.ROUTES_NOT_AVAILABLE -> { - view?.showRoutesNotAvailableError() - } - Constants.DIRECTION_API_FAILED -> { - view?.showDirectionApiFailedError( - "Direction API Failed : " + jsonObject.getString( - Constants.ERROR - ) - ) - } - } - } + fun showDirectionApiFailedError(error: String) } \ No newline at end of file diff --git a/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsView.kt b/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsView.kt index 8b34681..3bda3f0 100644 --- a/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsView.kt +++ b/app/src/main/java/me/amitshekhar/ridesharing/ui/maps/MapsView.kt @@ -1,27 +1,127 @@ package me.amitshekhar.ridesharing.ui.maps +import android.util.Log import com.google.android.gms.maps.model.LatLng +import me.amitshekhar.ridesharing.data.network.NetworkService +import me.amitshekhar.ridesharing.simulator.WebSocket +import me.amitshekhar.ridesharing.simulator.WebSocketListener +import me.amitshekhar.ridesharing.utils.Constants +import org.json.JSONObject -interface MapsView { +class MapsView(private val networkService: NetworkService) : WebSocketListener { - fun showNearbyCabs(latLngList: List) + companion object { + private const val TAG = "MapsPresenter" + } - fun informCabBooked() + private var view: MapsPresenter? = null + private lateinit var webSocket: WebSocket - fun showPath(latLngList: List) + fun onAttach(view: MapsPresenter?) { + this.view = view + webSocket = networkService.createWebSocket(this) + webSocket.connect() + } - fun updateCabLocation(latLng: LatLng) + fun onDetach() { + webSocket.disconnect() + view = null + } - fun informCabIsArriving() + fun requestNearbyCabs(latLng: LatLng) { + val jsonObject = JSONObject() + jsonObject.put(Constants.TYPE, Constants.NEAR_BY_CABS) + jsonObject.put(Constants.LAT, latLng.latitude) + jsonObject.put(Constants.LNG, latLng.longitude) + webSocket.sendMessage(jsonObject.toString()) + } - fun informCabArrived() + fun requestCab(pickUpLatLng: LatLng, dropLatLng: LatLng) { + val jsonObject = JSONObject() + jsonObject.put("type", "requestCab") + jsonObject.put("pickUpLat", pickUpLatLng.latitude) + jsonObject.put("pickUpLng", pickUpLatLng.longitude) + jsonObject.put("dropLat", dropLatLng.latitude) + jsonObject.put("dropLng", dropLatLng.longitude) + webSocket.sendMessage(jsonObject.toString()) + } - fun informTripStart() + private fun handleOnMessageNearbyCabs(jsonObject: JSONObject) { + val nearbyCabLocations = arrayListOf() + val jsonArray = jsonObject.getJSONArray(Constants.LOCATIONS) + for (i in 0 until jsonArray.length()) { + val lat = (jsonArray.get(i) as JSONObject).getDouble(Constants.LAT) + val lng = (jsonArray.get(i) as JSONObject).getDouble(Constants.LNG) + val latLng = LatLng(lat, lng) + nearbyCabLocations.add(latLng) + } + view?.showNearbyCabs(nearbyCabLocations) + } - fun informTripEnd() + override fun onConnect() { + Log.d(TAG, "onConnect") + } - fun showRoutesNotAvailableError() + override fun onMessage(data: String) { + Log.d(TAG, "onMessage data : $data") + val jsonObject = JSONObject(data) + when (jsonObject.getString(Constants.TYPE)) { + Constants.NEAR_BY_CABS -> { + handleOnMessageNearbyCabs(jsonObject) + } + Constants.CAB_BOOKED -> { + view?.informCabBooked() + } + Constants.PICKUP_PATH, Constants.TRIP_PATH -> { + val jsonArray = jsonObject.getJSONArray("path") + val pickUpPath = arrayListOf() + for (i in 0 until jsonArray.length()) { + val lat = (jsonArray.get(i) as JSONObject).getDouble("lat") + val lng = (jsonArray.get(i) as JSONObject).getDouble("lng") + val latLng = LatLng(lat, lng) + pickUpPath.add(latLng) + } + view?.showPath(pickUpPath) + } + Constants.LOCATION -> { + val latCurrent = jsonObject.getDouble("lat") + val lngCurrent = jsonObject.getDouble("lng") + view?.updateCabLocation(LatLng(latCurrent, lngCurrent)) + } + Constants.CAB_IS_ARRIVING -> { + view?.informCabIsArriving() + } + Constants.CAB_ARRIVED -> { + view?.informCabArrived() + } + Constants.TRIP_START -> { + view?.informTripStart() + } + Constants.TRIP_END -> { + view?.informTripEnd() + } + } + } - fun showDirectionApiFailedError(error: String) + override fun onDisconnect() { + Log.d(TAG, "onDisconnect") + } + + override fun onError(error: String) { + Log.d(TAG, "onError : $error") + val jsonObject = JSONObject(error) + when (jsonObject.getString(Constants.TYPE)) { + Constants.ROUTES_NOT_AVAILABLE -> { + view?.showRoutesNotAvailableError() + } + Constants.DIRECTION_API_FAILED -> { + view?.showDirectionApiFailedError( + "Direction API Failed : " + jsonObject.getString( + Constants.ERROR + ) + ) + } + } + } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index a546cc5..46a0348 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id("com.android.application") version "8.1.2" apply false + id("com.android.application") version "8.2.0" apply false id("org.jetbrains.kotlin.android") version "1.9.0" apply false id("org.jetbrains.kotlin.jvm") version "1.9.0" apply false - id("com.android.library") version "8.1.2" apply false + id("com.android.library") version "8.2.0" apply false } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9e9567c..525fd00 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sat Mar 02 16:47:10 IST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/simulator/build.gradle.kts b/simulator/build.gradle.kts index 69004f4..c0d7d2b 100644 --- a/simulator/build.gradle.kts +++ b/simulator/build.gradle.kts @@ -5,7 +5,7 @@ plugins { android { namespace = "me.amitshekhar.ridesharing.simulator" - compileSdk = 33 + compileSdk = 34 defaultConfig { minSdk = 24 @@ -33,11 +33,11 @@ android { } dependencies { - implementation("androidx.core:core-ktx:1.9.0") - implementation("androidx.appcompat:appcompat:1.6.1") - implementation("com.google.android.material:material:1.11.0") + implementation("androidx.core:core-ktx:1.13.1") + implementation("androidx.appcompat:appcompat:1.7.0") + implementation("com.google.android.material:material:1.12.0") api("com.google.maps:google-maps-services:0.2.9") testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.5") - androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + androidTestImplementation("androidx.test.ext:junit:1.2.1") + androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") } \ No newline at end of file