BlindlyTeam/Blindly

View on GitHub
app/src/main/java/ch/epfl/sdp/blindly/main_screen/map/UserMapActivity.kt

Summary

Maintainability
A
1 hr
Test Coverage
C
79%
package ch.epfl.sdp.blindly.main_screen.map

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import ch.epfl.sdp.blindly.R
import ch.epfl.sdp.blindly.location.BlindlyLatLng
import ch.epfl.sdp.blindly.database.DatabaseHelper
import ch.epfl.sdp.blindly.main_screen.my_matches.chat.Message
import ch.epfl.sdp.blindly.location.AndroidLocationService
import ch.epfl.sdp.blindly.location.permissions.LocationPermission.Companion.LOCATION_PERMISSION_REQUEST_CODE
import ch.epfl.sdp.blindly.location.permissions.LocationPermission.Companion.PermissionDeniedDialog.Companion.newInstance
import ch.epfl.sdp.blindly.location.permissions.LocationPermission.Companion.isPermissionGranted
import ch.epfl.sdp.blindly.location.permissions.LocationPermission.Companion.requestPermission
import ch.epfl.sdp.blindly.main_screen.profile.settings.LAUSANNE_LATLNG
import ch.epfl.sdp.blindly.user.UserHelper
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

/*
 * An activity displaying two users live
 */
@AndroidEntryPoint
class UserMapActivity : AppCompatActivity(), OnMapReadyCallback,
    ActivityCompat.OnRequestPermissionsResultCallback {

    /**
     * Flag indicating whether a requested permission has been denied after returning in
     * [onRequestPermissionsResult].
     */
    private var permissionDenied = false

    private lateinit var map: GoogleMap
    private var otherUserMarker: Marker? = null
    private lateinit var locationDatabase: DatabaseHelper.LocationLiveDatabase
    private lateinit var matchName: String

    @Inject
    lateinit var databaseHelper: DatabaseHelper

    @Inject
    lateinit var userHelper: UserHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user_map)
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        val mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment?
        mapFragment!!.getMapAsync(this)

        // Cancel loading if we can't get the user id
        val currentUserId = userHelper.getUserId() ?: return
        val matchId = intent.extras?.getString(MATCH_ID) ?: DEFAULT_MATCH_ID
        matchName =
            intent.extras?.getString(MATCH_NAME) ?: getString(R.string.default_label_map_pin)

        locationDatabase = databaseHelper.getLocationLiveDatabase(currentUserId, matchId)
        locationDatabase.addListener(object :
            DatabaseHelper.BlindlyLiveDatabase.EventListener<BlindlyLatLng>() {
            override fun onMessageReceived(message: Message<BlindlyLatLng>) {
                updateMarker(message)
            }

            override fun onMessageUpdated(message: Message<BlindlyLatLng>) {
                updateMarker(message)
            }
        })

        AndroidLocationService(applicationContext).addLocationChangeListener(object :
            AndroidLocationService.LocationChangeListener() {
            override fun onLocationChange(pos: BlindlyLatLng) {
                locationDatabase.updateLocation(pos)
            }
        })
    }

    private fun updateMarker(message: Message<BlindlyLatLng>) {
        if (!::map.isInitialized) return
        if (message.currentUserId == userHelper.getUserId()) return
        val pos = message.messageText ?: return
        val latLng = pos.toLatLng() ?: return
        tryToAddMarker(latLng)?.position = latLng
    }

    /**
     * Sometimes google map can fail to add the marker so we
     * retry everytime we need it
     *
     * @return Hopefully the marker
     */
    private fun tryToAddMarker(latLng: LatLng): Marker? {
        if (otherUserMarker == null && ::map.isInitialized) {
            otherUserMarker = map.addMarker(
                MarkerOptions()
                    .position(LAUSANNE_LATLNG)
                    .title(matchName)
            )
            // When we first have the match position,
            // center the map to their position
            map.moveCamera(CameraUpdateFactory.newLatLng(latLng))
        }
        return otherUserMarker
    }

    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     *
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    override fun onMapReady(googleMap: GoogleMap?) {
        map = googleMap ?: return

        enableMyLocation()
    }

    /**
     * Enables the My Location layer if the fine location permission has been granted.
     */
    private fun enableMyLocation() {
        if (!::map.isInitialized) return
        // [START maps_check_location_permission]
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED
        ) {
            map.isMyLocationEnabled = true
        } else {
            // Permission to access the location is missing. Show rationale and request permission
            requestPermission(
                this, LOCATION_PERMISSION_REQUEST_CODE,
                Manifest.permission.ACCESS_FINE_LOCATION, true
            )
        }
        // [END maps_check_location_permission]
    }

    // [START maps_check_location_permission_result]
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
            return
        }
        if (isPermissionGranted(
                permissions,
                grantResults,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
        ) {
            // Enable the my location layer if the permission has been granted.
            enableMyLocation()
        } else {
            // Permission was denied. Display an error message
            // [START_EXCLUDE]
            // Display the missing permission error dialog when the fragments resume.
            permissionDenied = true
            // [END_EXCLUDE]
        }
    }

    // [END maps_check_location_permission_result]
    override fun onResumeFragments() {
        super.onResumeFragments()
        if (permissionDenied) {
            // Permission was not granted, display error dialog.
            showMissingPermissionError()
            permissionDenied = false
        }
    }

    /**
     * Displays a dialog with error message explaining that the location permission is missing.
     */
    private fun showMissingPermissionError() {
        newInstance(true).show(supportFragmentManager, "dialog")
    }

    companion object {
        const val MATCH_ID = "matchedId"
        const val MATCH_NAME = "matchedName"
        const val DEFAULT_MATCH_ID = "default_user"
    }
}