Home > OS >  I get "not attached to a context" error when I want to go back from current fragment
I get "not attached to a context" error when I want to go back from current fragment

Time:01-31

This code belongs to a fragment which shows the user's current location.After getting location when I want to go back to previous fragment App crashes.Logcat says error is happening here : "val geocoder = Geocoder(requireContext(), Locale.getDefault())" If anyone could assist me, I would greatly appreciate it

class LocationFragment : DialogFragment(), OnMapReadyCallback {

lateinit var binding: FragmentLocationBinding
private lateinit var map: GoogleMap

private val REQUEST_LOCATION_PERMISSION = 1
var lat: Double = 0.0
var long: Double = 0.0

private lateinit var fusedLocationProviderClient: FusedLocationProviderClient

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    binding = FragmentLocationBinding.inflate(inflater, container, false)
    return binding.root
}



override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
    StrictMode.setThreadPolicy(policy)

    fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireContext())
    enableMyLocation()
    binding.apply {
        prgBar.visibility=View.VISIBLE
        btnSaveLocation.setOnClickListener {
            dismiss()
        }
        val mapFragment = childFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment
        mapFragment.getMapAsync(this@LocationFragment)
    }
}

override fun onStart() {
    super.onStart()
    val dialog: Dialog? = dialog
    if (dialog != null) {
        dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
        //dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
    }
}

override fun onMapReady(p0: GoogleMap) {
    map = p0
}

private fun isPermissionGranted(): Boolean {
    return ContextCompat.checkSelfPermission(
        requireContext(),
        Manifest.permission.ACCESS_FINE_LOCATION
    ) == PackageManager.PERMISSION_GRANTED
}

@SuppressLint("VisibleForTests")
private fun checkDeviceLocationSettings(resolve: Boolean = true) {
    val locationRequest = LocationRequest.create().apply {
        priority = LocationRequest.PRIORITY_LOW_POWER
    }
    val requestBuilder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
    val settingsClient = LocationServices.getSettingsClient(requireActivity())
    val locationSettingsResponseTask =
        settingsClient.checkLocationSettings(requestBuilder.build())
    locationSettingsResponseTask.addOnFailureListener { exception ->
        if (exception is ResolvableApiException && resolve) {
            try {
                exception.startResolutionForResult(
                    requireActivity(),
                    REQUEST_TURN_DEVICE_LOCATION_ON
                )
            } catch (sendEx: IntentSender.SendIntentException) {
                Log.d(TAG, "Error getting location settings resolution: "   sendEx.message)
            }
        } else {
            Snackbar.make(
                binding.root,
                R.string.location_required_error, Snackbar.LENGTH_INDEFINITE
            ).setAction(android.R.string.ok) {
                checkDeviceLocationSettings()
            }.show()
        }

    }
}

@TargetApi(Build.VERSION_CODES.Q)
private fun requestQPermission() {
    val hasForegroundPermission = ActivityCompat.checkSelfPermission(
        requireContext(),
        Manifest.permission.ACCESS_FINE_LOCATION
    ) == PackageManager.PERMISSION_GRANTED

    if (hasForegroundPermission) {
        val hasBackgroundPermission = ActivityCompat.checkSelfPermission(
            requireContext(),
            Manifest.permission.ACCESS_BACKGROUND_LOCATION
        ) == PackageManager.PERMISSION_GRANTED
        if (hasBackgroundPermission) {
            checkDeviceLocationSettings()
        } else {
            ActivityCompat.requestPermissions(
                requireActivity(),
                arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                REQUEST_CODE_BACKGROUND
            )
        }
    }
}

private fun enableMyLocation() {
    if (isPermissionGranted()) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            checkDeviceLocationSettings()
        } else {
            requestQPermission()
        }

        updateLocation()

    } else {
        ActivityCompat.requestPermissions(
            context as Activity,
            arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION),
            REQUEST_LOCATION_PERMISSION
        )
    }
}

private fun updateLocation() {
    if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission
            (requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION)
        != PackageManager.PERMISSION_GRANTED
    ) {
        return
    }
    fusedLocationProviderClient.requestLocationUpdates(
        locationRequest(), locationCallback,
        Looper.myLooper()
    )
}

private fun locationRequest(): LocationRequest {
    return LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 3000)
        .setWaitForAccurateLocation(false)
        .setMinUpdateIntervalMillis(5000)
        .setMaxUpdateDelayMillis(5000)
        .build()
}

private var locationCallback = object : LocationCallback() {
    override fun onLocationResult(p0: LocationResult) {

        val location: Location? = p0.lastLocation

        if (location != null) {
            updateAddressUI(location)


        }

    }
}

@SuppressLint("MissingPermission")
fun updateAddressUI(location: Location) {
    map.isMyLocationEnabled = true
    val addressList: ArrayList<Address>
    val geocoder = Geocoder(requireContext(), Locale.getDefault())   //Getting error here
    addressList = geocoder.getFromLocation(
        location.latitude,
        location.longitude,
        1
    ) as ArrayList<Address>
    lat = addressList[0].latitude
    long = addressList[0].longitude

    binding.prgBar.visibility=View.INVISIBLE

    Toast.makeText(requireContext(), "$lat \n $long", Toast.LENGTH_SHORT).show()

    val latLng = LatLng(lat, long)
    val markerOptions = MarkerOptions().position(latLng).title("I am here!")
    map.animateCamera(CameraUpdateFactory.newLatLng(latLng))
    map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15f))
    map.addMarker(markerOptions)?.setIcon(bitmapFromVector(requireContext(),R.drawable.baseline_emoji_people_24))


}

private fun bitmapFromVector(context: Context, vectorResId: Int): BitmapDescriptor? {
    val vectorDrawable = ContextCompat.getDrawable(context, vectorResId)
    vectorDrawable!!.setBounds(0, 0, vectorDrawable.intrinsicWidth, vectorDrawable.intrinsicHeight)
    val bitmap = Bitmap.createBitmap(vectorDrawable.intrinsicWidth, vectorDrawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    vectorDrawable.draw(canvas)
    return BitmapDescriptorFactory.fromBitmap(bitmap)
}

override fun onResume() {
    super.onResume()
    enableMyLocation()
}

}

I used 'requireActivity' instead of 'requireContext' but didn't work

CodePudding user response:

As you said, beacuse the crash happens when you go back to the previous fragment, probably the locationCallback getting called when the current fragment is detached from the activity. Inside the updateAddressUI you are getting the context in that momment when your view is detached.

If is ok with your logic you can keep the context reference inside the callBack object and work with this.

Try to change the locationCallback to and pass the context to the updateAddressUI function.

private var locationCallback = object : LocationCallback() {

    val context = requireContext()

    override fun onLocationResult(p0: LocationResult) {

        val location: Location? = p0.lastLocation

        if (location != null) {
            updateAddressUI(location, context)


        }

    }
}

Change also the updateAddressUI function like this.

@SuppressLint("MissingPermission")
fun updateAddressUI(location: Location, context: Context) {
    map.isMyLocationEnabled = true
    val addressList: ArrayList<Address>
    val geocoder = Geocoder(context, Locale.getDefault())   //Getting error here
    addressList = geocoder.getFromLocation(
        location.latitude,
        location.longitude,
        1
    ) as ArrayList<Address>
    lat = addressList[0].latitude
    long = addressList[0].longitude

    binding.prgBar.visibility=View.INVISIBLE

    Toast.makeText(context, "$lat \n $long", Toast.LENGTH_SHORT).show()

    val latLng = LatLng(lat, long)
    val markerOptions = MarkerOptions().position(latLng).title("I am here!")
    map.animateCamera(CameraUpdateFactory.newLatLng(latLng))
    map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15f))
    map.addMarker(markerOptions)?.setIcon(bitmapFromVector(context,R.drawable.baseline_emoji_people_24))


}

CodePudding user response:

I changed the code in this way :

val geocoder = context?.let { Geocoder(it, Locale.getDefault()) }

in this methode :

fun updateAddressUI(location: Location) {
        map.isMyLocationEnabled = true
        val addressList: ArrayList<Address>
        val geocoder = context?.let { Geocoder(it, Locale.getDefault()) }
        if (geocoder != null) {
            addressList = geocoder.getFromLocation(
                location.latitude,
                location.longitude,
                1
            ) as ArrayList<Address>
            lat = addressList[0].latitude
            long = addressList[0].longitude
        }
  • Related