I am trying to add multiple GeoJsonLayer run time my requirement is on responce of api i have to highlight multiple countries in map which is working fine but i am not getting click event multiple layer i know only single layer click is only suppored by android but is ther a any way to add multiple layer and get separate click event of all event
this is my code
import android.location.Address
import android.location.Geocoder
import android.os.AsyncTask
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
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.gson.Gson
import com.google.maps.android.data.geojson.GeoJsonLayer
import com.google.maps.android.data.geojson.GeoJsonPolygonStyle
import org.json.JSONException
import org.json.JSONObject
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.net.URL
import java.util.*
class MapsNewActivity : AppCompatActivity(), OnMapReadyCallback {
private val TAG = javaClass.simpleName
private lateinit var mMap: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
// 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)
}
/**
* 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) {
mMap = googleMap
mMap.setOnMapClickListener {
val geocoder = Geocoder(this, Locale.getDefault())
val addresses: List<Address> = geocoder.getFromLocation(it.latitude, it.longitude, 1)
if (addresses.isNotEmpty()) {
val country: String? = addresses[0].getCountryName()
Log.e(TAG, "country is ${country.toString()}")
Toast.makeText(this, country.toString(), Toast.LENGTH_SHORT).show()
}
}
mMap.uiSettings.isZoomControlsEnabled = true
val madrid = LatLng(40.416775, -3.70379)
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(madrid, 3f))
retrieveFileFromUrl()
}
private fun retrieveFileFromUrl() {
DownloadGeoJsonFile().execute("https://raw.githubusercontent.com/xomena-so/so41431384/master/app/src/main/res/raw/es_geojson.json")
DownloadGeoJsonFile().execute("https://raw.githubusercontent.com/xomena-so/so41431384/master/app/src/main/res/raw/us_geojson.json")
}
inner class DownloadGeoJsonFile : AsyncTask<String?, Void?, GeoJsonLayer?>() {
override fun doInBackground(vararg params: String?): GeoJsonLayer? {
try {
// Open a stream from the URL
val stream = URL(params[0]).openStream()
var line: String?
val result = StringBuilder()
val reader = BufferedReader(InputStreamReader(stream))
while (reader.readLine().also { line = it } != null) {
// Read and save each line of the stream
result.append(line)
}
// Close the stream
reader.close()
stream.close()
return GeoJsonLayer(mMap, JSONObject(result.toString()))
// return JSONObject(result.toString())
} catch (e: IOException) {
Log.e(TAG, "GeoJSON file could not be read")
} catch (e: JSONException) {
Log.e(TAG, "GeoJSON file could not be converted to a JSONObject")
}
return null
}
override fun onPostExecute(layer: GeoJsonLayer?) {
layer?.let { addGeoJsonLayerToMap(it) }
}
}
private fun addGeoJsonLayerToMap(layer: GeoJsonLayer) {
val style: GeoJsonPolygonStyle = layer.defaultPolygonStyle
style.fillColor = R.color.map_highlight
style.strokeColor = R.color.map_highlight_border
style.strokeWidth = 1f
layer.addLayerToMap()
layer.setOnFeatureClickListener {
Log.e(TAG, "layer click ${it.getProperty("title")}")
Log.e(TAG, "layer click Data = = = ${Gson().toJson(it)}")
Toast.makeText(
this,
"Feature clicked: " + it.getProperty("title"),
Toast.LENGTH_SHORT
).show()
}
}
}
this is what I am getting
I am also getting a country name in toast on click of any other country but not getting a country name or click event in setOnFeatureClickListener or setOnMapClickListener on click of the highlighted part
so how do I get data on click of highlighted part in map?
Please help me how do I achieve this
Any help would be highly appreciated.
this is not the best solution but if you disable the click of the layer then you will get an event in setOnMapClickListener
so I added this to get click event style.isClickable = false this will pervert the layer click event
val style: GeoJsonPolygonStyle = layer.defaultPolygonStyle
style.fillColor = R.color.map_highlight
style.strokeColor = R.color.map_highlight_border
style.strokeWidth = 1f
style.isClickable = false // this will pervert setOnFeatureClickListener so mMap.setOnMapClickListener work
layer.addLayerToMap()
you will receieve click event in this part
mMap.setOnMapClickListener {
val geocoder = Geocoder(this, Locale.getDefault())
val addresses: List<Address> = geocoder.getFromLocation(it.latitude, it.longitude, 1)
if (addresses.isNotEmpty()) {
val country: String? = addresses[0].getCountryName()
Log.e(TAG, "country is ${country.toString()}")
Toast.makeText(this, country.toString(), Toast.LENGTH_SHORT).show()
}
}
this is working for me please do correct me if I am doing some wrong
Related
I am lost on how to make certain properties from a data class (a Place to rent) in Kotlin with InfoWindow of each Marker on Google Maps. Can you please show a step-by-step procedure and possibly code with layout.xml to show how to show Place attributes on the Marker InfoWindow?
Places.kt
package com.example.myapplication.model
import com.google.android.gms.maps.model.LatLng
data class Place(
val name: String,
val latLng: LatLng,
//val address: LatLng,
val price: Int
)
MapsActivity.kt
package com.example.myapplication
//import android.util.Log
//import android.widget.Button
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.example.myapplication.databinding.ActivityMapsBinding
import com.example.myapplication.model.Place
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.*
import java.util.*
class MapsActivity : AppCompatActivity(), GoogleMap.OnMarkerClickListener, OnMapReadyCallback {
private lateinit var mMap: GoogleMap
private lateinit var binding: ActivityMapsBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
var name ="Kaleab Tekle"
var latLng= LatLng(9.005401, 38.763611)
var price = 1500
val place = Place( name, latLng, price)
binding = ActivityMapsBinding.inflate(layoutInflater)
setContentView(binding.root)
//binds activity_main.xml textview_id and apartment_id
binding.textViewId
//binding.apartmentId
// 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{ googleMap ->
addMarkers(googleMap)
}
}
//* This callback is triggered when the map is ready to be useia.
/**
* 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.d.
* 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, Austral
*/
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
val zoomlevel = 15f
val markerArrayList= ArrayList<Marker>()
// with empty "HashMap of <marker, Data>"
var hashMap : HashMap<Marker?, data> = HashMap<Marker?, data> ()
//add markers with related information
for(data in markerArrayList){
val marker = mMap . addMarker (MarkerOptions().position(data.position.latitude,data.position.longitude)
)
hashMap.put(marker, data)
}
mMap.setOnInfoWindowClickListener { marker ->
val data: Data? = hashMap[marker]
if (data != null) {
val intent = Intent(mContext, MapsActivity::class.java)
intent.putExtra(YourActivity.EXTRA_MESSAGE, data)
mContext.startActivity(intent)
}
}
mMap.let {
it.setOnMapClickListener {
//Add LatLng to Array
if (markerArrayList.size > 0) {
val markerToRemove = markerArrayList.get(0)
//remove the marker from the list
markerArrayList.remove(markerToRemove)
//remove the marker from the map
markerToRemove.remove()
}
//Marker Options
val markerOptions = MarkerOptions().position(it).draggable(true)
val currentMarker = mMap.addMarker(markerOptions)
//Add current marker to arrayList
if (currentMarker != null) {
markerArrayList.add(currentMarker)
//set default icon
currentMarker.setIcon(
BitmapDescriptorFactory.defaultMarker(
BitmapDescriptorFactory.HUE_GREEN
)
)
}
//val clickedPointLoc =(it)
//geocode LatLng to Address
//val address =Geocoder(this)
//val geocoder = Geocoder(this, Locale.getDefault())
//val addresses: List<Address> = geocoder.getFromLocation(it.latitude, it.longitude, 1)
//Log.i(addresses?.toString())
}
}
// Add a marker in Sydney and move the camera
val addisababa = LatLng(9.005401, 38.763611)
mMap.addMarker(MarkerOptions().position(addisababa).title("Marker in Addis Ababa"))
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(addisababa, zoomlevel))
}
}
/**
* Adds marker representations of the places list on the provided GoogleMap object
*/
private fun addMarkers(googleMap: GoogleMap) {
places.forEach { place ->
val marker = googleMap.addMarker(
MarkerOptions()
.title(place.name)
.position(place.latLng)
)
}
}
}
I'm making an application in Android (with Kotlin) that requires Google Maps services. When a button is pressed, it should locate me. However it is not accurate at all.
I use this version of Google Maps:
implementation 'com.google.android.gms:play-services-maps:18.0.2'
implementation 'com.google.android.gms:play-services-location:20.0.0'
Another versions:
Android Studio Chipmunk 2021.2.1 Patch 1
Tested with: Xiaomi Redmi Note 7 and Xiaomi Litte 9
And this is the code when I press the button:
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.*
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
private lateinit var mMap: GoogleMap
private var coordinatesUbication: LatLng? = null
private fun getMyUbication() {
if (isGPSPermissions() && isGPSActive()) {
mMap.isMyLocationEnabled = true
mMap.uiSettings.isMyLocationButtonEnabled = false
// 1) Get latitude and longitude of the current position.
fusedLocationProviderClient.lastLocation.addOnCompleteListener { task ->
val location = task.result
if (location != null) {
// 2) Get coordinates.
val coordinates: LatLng? = LatLng(location.latitude, location.longitude)
this#MapsFragment.coordinatesUbication = coordinates
// 3) Set camera position.
CoroutineScope(Dispatchers.IO).launch {
cameraPosition(coordinatesUbication, Constants.CAMERA_ZOOM)
}
}
}
} else if (!isGPSPermissions()) {
myUbication = true
showDialogPermissions()
} else {
myUbication = true
}
}
I have finished the product I was developing, but currently, we track the users ( passenger ) location as well as the drivers too slowly.
This is the code I use to track and update the map with the passangers/ drivers icon as it moves :
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import android.os.Looper
import android.util.Log
import androidx.core.content.ContextCompat
import com.google.android.gms.location.*
import mobi.audax.tupi.motorista.bin.task.GeoDecodeTask
import mobi.audax.tupi.passageiro.util.Prefs
class IntermitentLocationThread(val context: Context, val onLocationUpdate: (location: Location?) -> Unit) : LocationListener {
private var UPDATE_INTERVAL = (1000 * 10).toLong() // 10 segundos de intervalo
private val MAX_WAIT_TIME = UPDATE_INTERVAL * 2 // 20 segundos
private var bestLocation: Location? = null
fun requestLocation() {
this.locationService()
}
private fun locationService() {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
val locationRequest = LocationRequest.create()
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationRequest.fastestInterval = 1000
locationRequest.interval = UPDATE_INTERVAL
locationRequest.maxWaitTime = MAX_WAIT_TIME
locationRequest.smallestDisplacement = 15f
val fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context)
fusedLocationProviderClient.lastLocation.addOnSuccessListener { location -> onLocationChanged(location) }
fusedLocationProviderClient.requestLocationUpdates(locationRequest, object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
onLocationChanged(locationResult.lastLocation)
}
}, Looper.myLooper()!!)
}
}
override fun onLocationChanged(location: Location) {
try {
Log.v("IntermitentLocationThread", "onLocationChanged")
if (location != null) {
val commons = LocationCommons()
// if (!commons.isMock(context, location) && commons.isBetterLocation(location, bestLocation)) {
Log.v("IntermitentLocationThread", "isBetter true")
val prefs = Prefs(context)
prefs.latitude = location.latitude.toFloat()
prefs.longitude = location.longitude.toFloat()
prefs.precisao = location.accuracy
prefs.velocidade = location.speed * 3.6f
prefs.bearing = location.bearing
if (location.extras.containsKey("satellites")) {
prefs.satellites = location.extras.getInt("satellites")
}
GeoDecodeTask(context, location).decoder { }
bestLocation = location
onLocationUpdate(bestLocation)
} else {
Log.v("IntermitentLocationThread", "isBetter false")
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
and heres how i implement it in my Activity:
private void handleLocationUpdates() {
if (isLocationEnabled()) {
loadMapScene();
IntermitentLocationThread thread = new IntermitentLocationThread(this, location -> {
Log.e(TAG, "handleLocationUpdates: "+"pegado localização" );
clearPassageiroMapMarker();
addPassageiroMarker(new GeoCoordinates(location.getLatitude(),
location.getLongitude()), R.drawable.ic_passageiro);
passageiro.setLat(location.getLatitude());
passageiro.setLongitude(location.getLongitude());
SharedPreferences.Editor editor = this.getSharedPreferences(Constantss.PREFERENCES, MODE_PRIVATE).edit();
mapView.getCamera().lookAt(new GeoCoordinates(passageiro.getLat(), passageiro.getLongitude()));
return null;
});
thread.requestLocation();
} else {
Toast.makeText(this, "Por favor" + "ative sua localização...", Toast.LENGTH_LONG).show();
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
}
at the moment, I get passengers/drivers' locations in a very weird and odd consistency, it's not as " fluid " as uber does it. right now, my marker jumps from one point to another (because I clear the marker list and set another one in place, still working on having only one marker in a heremaps map) in a range of 5 to 15 seconds and everybody else on the internet seems to use this google engine to track one's location.
How is it possible to track users' location in a faster / smoother way?
i was able to find a better/fast tracking by using Google's Location Manager...
yes.. LocationManager.
The code i was using before that wasnt really accurate at all, with Googles Location Manager i was able to set a specific timer and keep it accurate.
More on : https://developer.android.com/reference/android/location/LocationManager
thanks for looking by. As the title suggests I'm facing a rather "weird" issue in my current Kotlin project. In my Application the user can click on a button (or textinput working as a button) and then a new intent pops up where the user can type for a location.
A small snippet here :
R.id.et_location -> {
try{
// This is the list of fields that need to be passed
val fields = listOf(Place.Field.ID, Place.Field.NAME, Place.Field.LAT_LNG, Place.Field.ADDRESS)
// Start the autocomplete intent with a unique request code.
val intent = Autocomplete.IntentBuilder(AutocompleteActivityMode.FULLSCREEN, fields).build(this#AddHappyPlaceActivity)
startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE)
}catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, "Couldnt load it", Toast.LENGTH_SHORT).show()}
All that works kinda fine, so the Window pops up but when I start typing something like "Dub" (for Dubai as example) it shows a few entries, but if i type 1-2 characters more it suddenly says "Can't load search results". Sometimes the search doesn't work at all. I've googled that issue and people suggested there could be something wrong with the API Key, but if something was to be wrong with the Key it wouldn't work in the first place and just close the Intent (i tried).
I've tried a few things with the API Key. To begin with i had issues with the first key i created that it didn't work at all. The second key i generated worked partly (my current situation). The first key is restricted to my package/sha1.
The second key as you see has no restrictions at all but it works better than the first one. Im now not sure if its my API Key that causes that issue or my code. Below I'll share code thats relevant.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.sampa.happyPlaces">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Several permissions -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<activity
android:name=".activities.HappyPlaceDetailActivity"
android:label="HAPPY PLACE DETAILS"
android:screenOrientation="portrait"
android:theme="#style/CustomNoActionBarTheme" />
<activity
android:name=".activities.AddHappyPlaceActivity"
android:label="ADD HAPPY PLACE"
android:screenOrientation="portrait"
android:theme="#style/CustomNoActionBarTheme" />
<activity android:name=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
google_maps_api.xml (I deleted my SHA1key and API Key before posting here)
<resources>
<!--
TODO: Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:
https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=60:BF:DE:FE:3C:6F:DA:3B:56:09:E7:B7:BB:5F:FC:F8:AE:00:DC:0D%3Beu.sampa.happyPlaces.activities
You can also add your credentials to an existing key, using these values:
Package name:
eu.sampa.happyPlaces.activities
SHA-1 certificate fingerprint:
MY SHA1 KEY
Alternatively, follow the directions here:
https://developers.google.com/maps/documentation/android/start#get-key
Once you have your key (it starts with "AIza"), replace the "google_maps_key"
string in this file.
-->
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">MY API KEY</string>
</resources>
And now at last my Activity where all necessary stuff is processed (sorry for the length... but I've commented all functions regarding this issue with the comment "// For the Places API", just use the search function if necessary)
AddHappyPlaceActivity.kt
package eu.sampa.happyPlaces.activities
import android.Manifest
import android.app.Activity
import android.app.DatePickerDialog
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.provider.Settings
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.android.libraries.places.api.Places
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.widget.Autocomplete
import com.google.android.libraries.places.widget.model.AutocompleteActivityMode
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import eu.sampa.happyPlaces.R
import eu.sampa.happyPlaces.database.DatabaseHandler
import eu.sampa.happyPlaces.models.HappyPlaceModel
import kotlinx.android.synthetic.main.activity_add_happy_place.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStream
import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.*
class AddHappyPlaceActivity : AppCompatActivity(), View.OnClickListener {
// Creates a variable for GALLERY Selection which will be later used in the onActivityResult method.
companion object {
private const val GALLERY = 1
private const val CAMERA = 2
private const val IMAGE_DIRECTORY = "HappyPlacesImages"
private const val PLACE_AUTOCOMPLETE_REQUEST_CODE = 3
}
private var saveImageToInternalStorage : Uri? = null
private var mLatitude : Double = 0.0
private var mLongitude : Double = 0.0
// For the swipe feature
private var mHappyPlaceDetails : HappyPlaceModel? = null
// Creating the variables of Calender Instance and DatePickerDialog listener to use it for date selection
// A variable to get an instance calendar using the default time zone and locale.
private var cal = Calendar.getInstance()
/* A variable for DatePickerDialog OnDateSetListener.
* The listener used to indicate the user has finished selecting a date. It will be initialized later. */
private lateinit var dateSetListener : DatePickerDialog.OnDateSetListener
// Used to increment when someone clicks on the Add Photo button see below in onClick function
private var addButtonClicked = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_happy_place)
// Adds the back button on the ActionBar
setSupportActionBar(toolbar_add_place)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
toolbar_add_place.setNavigationOnClickListener {
onBackPressed()
}
// For the Places API
if(!Places.isInitialized()) {
Places.initialize(this#AddHappyPlaceActivity, resources.getString(R.string.google_maps_key))
}
if(intent.hasExtra(MainActivity.EXTRA_PLACE_DETAILS)) {
mHappyPlaceDetails = intent.getParcelableExtra(MainActivity.EXTRA_PLACE_DETAILS) as HappyPlaceModel
}
// Initialize the DatePicker and sets the selected date
// https://www.tutorialkart.com/kotlin-android/android-datepicker-kotlin-example/
dateSetListener = DatePickerDialog.OnDateSetListener{
_, year, month, dayOfMonth ->
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, month)
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
updateDateInView()
}
// Automatically sets the current date
updateDateInView()
// Uses functionality in the onClick function below
et_date.setOnClickListener(this)
tv_add_image.setOnClickListener(this)
btn_save.setOnClickListener(this)
et_location.setOnClickListener(this)
if(mHappyPlaceDetails != null) {
supportActionBar?.title = "Edit Happy PLace"
et_title.setText(mHappyPlaceDetails!!.title)
et_description.setText(mHappyPlaceDetails!!.description)
et_date.setText(mHappyPlaceDetails!!.date)
et_location.setText(mHappyPlaceDetails!!.location)
mLatitude = mHappyPlaceDetails!!.latitude
mLongitude = mHappyPlaceDetails!!.longitude
saveImageToInternalStorage = Uri.parse(mHappyPlaceDetails!!.image)
iv_place_image.setImageURI(saveImageToInternalStorage)
btn_save.text = "UPDATE"
}
}
// This is a override method after extending the onclick listener interface (gets created automatically)
override fun onClick(v: View?) {
when (v!!.id) {
R.id.et_date -> {
DatePickerDialog(this#AddHappyPlaceActivity, dateSetListener,
cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()
}
R.id.tv_add_image -> {
val pictureDialog = AlertDialog.Builder(this)
pictureDialog.setTitle("Select Action")
val pictureDialogItems = arrayOf("Select photo from gallery", "Capture photo from camera")
pictureDialog.setItems(pictureDialogItems) {
_, which ->
when(which) {
0 -> choosePhotoFromGallery()
1 -> takePhotoFromCamera()
}
}
pictureDialog.show()
/* Used to display the Dialog to get to the menu after the user
* denied access 2 or more times */
addButtonClicked += 1
if (addButtonClicked > 2) {
if (ContextCompat.checkSelfPermission(this#AddHappyPlaceActivity,
Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
showRationalDialogForPermissions()
}
if (ContextCompat.checkSelfPermission(this#AddHappyPlaceActivity,
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
showRationalDialogForPermissions()
}
if (ContextCompat.checkSelfPermission(this#AddHappyPlaceActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
showRationalDialogForPermissions()
}
}
}
R.id.btn_save -> {
when {
et_title.text.isNullOrEmpty() -> {
Toast.makeText(this, "Please enter title", Toast.LENGTH_SHORT).show()
}
et_description.text.isNullOrEmpty() -> {
Toast.makeText(this, "Please enter description", Toast.LENGTH_SHORT)
.show()
}
et_location.text.isNullOrEmpty() -> {
Toast.makeText(this, "Please select location", Toast.LENGTH_SHORT)
.show()
}
saveImageToInternalStorage == null -> {
Toast.makeText(this, "Please add image", Toast.LENGTH_SHORT).show()
}
else -> {
// Assigning all the values to data model class.
val happyPlaceModel = HappyPlaceModel(
if(mHappyPlaceDetails == null) 0 else mHappyPlaceDetails!!.id,
et_title.text.toString(),
saveImageToInternalStorage.toString(),
et_description.text.toString(),
et_date.text.toString(),
et_location.text.toString(),
mLatitude,
mLongitude
)
// Here we initialize the database handler class.
val dbHandler = DatabaseHandler(this)
if (mHappyPlaceDetails == null) {
val addHappyPlace = dbHandler.addHappyPlace(happyPlaceModel)
if (addHappyPlace > 0) {
setResult(Activity.RESULT_OK)
finish() // Gets us back to MainActivity
}
} else{
val updateHappyPlace = dbHandler.updateHappyPlace(happyPlaceModel)
// greater than zero indicates that everything worked out
if (updateHappyPlace > 0) {
setResult(Activity.RESULT_OK)
finish() // Gets us back to MainActivity
}
}
}
}
}
// For the Places API
R.id.et_location -> {
try{
// This is the list of fields that need to be passed
val fields = listOf(Place.Field.ID, Place.Field.NAME, Place.Field.LAT_LNG, Place.Field.ADDRESS)
// Start the autocomplete intent with a unique request code.
val intent = Autocomplete.IntentBuilder(AutocompleteActivityMode.FULLSCREEN, fields).build(this#AddHappyPlaceActivity)
startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE)
}catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, "Couldnt load it", Toast.LENGTH_SHORT).show()}
}
}
}
// Method used for taking pictures with the Camera
private fun takePhotoFromCamera() {
// Asking for permissions using DEXTER Library
Dexter.withContext(this).withPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
).withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
// Here after all the permission are granted launch the Camera to capture an image
val galleryIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(galleryIntent, CAMERA )
}
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
token?.continuePermissionRequest()
}
}).onSameThread().check()
}
// Method used for image selection from GALLERY/PHOTOS
private fun choosePhotoFromGallery() {
// Asking for permissions using DEXTER Library
Dexter.withContext(this).withPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
).withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
// Here after all the permission are granted, launch the gallery to select and image.
val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(galleryIntent,
GALLERY
)
}
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
token?.continuePermissionRequest()
}
}).onSameThread().check()
}
// Message to be shown if user denies access and possibly send him to the settings
private fun showRationalDialogForPermissions() {
AlertDialog.Builder(this).setMessage("It looks like you have turned off " +
"permissions required for this feature").setPositiveButton("GO TO SETTINGS")
{ _, _ ->
try{
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
}
}.setNegativeButton("Cancel") { dialog, _ ->
dialog.dismiss()
}.show()
}
// Handles the chosen Image from the startActivityResult from choosePhotoFromGallery and takePhotoFromCamera
#RequiresApi(Build.VERSION_CODES.P)
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK) {
if(requestCode == GALLERY) {
if(data != null) {
val contentURI = data.data
// For more info go to https://stackoverflow.com/questions/56651444/deprecated-getbitmap-with-api-29-any-alternative-codes
try {
if(Build.VERSION.SDK_INT < 28) {
// Here this is used to get an bitmap from URI
val selectedImageBitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, contentURI)
// Saving an image which is selected from GALLERY. And printed the path in logcat
saveImageToInternalStorage = saveImageToInternalStorage(selectedImageBitmap)
Log.e("Saved image: ", "Path :: $saveImageToInternalStorage")
iv_place_image!!.setImageBitmap(selectedImageBitmap) // Set the selected image from GALLERY to imageView
} else {
val selectedImageBitmapSource = contentURI?.let { ImageDecoder.createSource(this.contentResolver, it) }
val selectedImageBitmap = selectedImageBitmapSource?.let { ImageDecoder.decodeBitmap(it) }
// Saving an image which is selected from GALLERY. And printed the path in logcat
saveImageToInternalStorage = selectedImageBitmap?.let { saveImageToInternalStorage(it) }
Log.e("Saved image: ", "Path :: $saveImageToInternalStorage")
iv_place_image.setImageBitmap(selectedImageBitmap)
}
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(this#AddHappyPlaceActivity, "Failed to load the Image!", Toast.LENGTH_SHORT).show()
}
}
// Camera result will be received here
} else if(requestCode == CAMERA){
val thumbNail : Bitmap = data!!.extras!!.get("data") as Bitmap // Bitmap from camera
// Saving an image which is selected from CAMERA. And printed the path in logcat
saveImageToInternalStorage = saveImageToInternalStorage(thumbNail)
Log.e("Saved image: ", "Path :: $saveImageToInternalStorage")
iv_place_image.setImageBitmap(thumbNail) // Set to the imageView
// For the Places API
} else if(requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE) {
val place : Place = Autocomplete.getPlaceFromIntent(data!!)
et_location.setText(place.address)
mLatitude = place.latLng!!.latitude
mLongitude = place.latLng!!.longitude
}
}
}
// A function to update the selected date in the UI with selected format.
private fun updateDateInView() {
val myFormat = "dd.MM.yyyy"
val sdf = SimpleDateFormat(myFormat, Locale.getDefault())
et_date.setText(sdf.format(cal.time).toString())
}
/* https://android--code.blogspot.com/2018/04/android-kotlin-save-image-to-internal.html
Uri gives us the location back */
private fun saveImageToInternalStorage(bitmap: Bitmap):Uri {
// Get the context wrapper instance
val wrapper = ContextWrapper(applicationContext)
// This line returns a directory in the internal storage
var file = wrapper.getDir(IMAGE_DIRECTORY, Context.MODE_PRIVATE)
// First we give the location and then we generate a random Name for the Image
file = File(file, "${UUID.randomUUID()}.jpg")
//
try {
val stream : OutputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG,100, stream)
stream.flush()
stream.close()
}catch (e: IOException) {
e.printStackTrace()
}
// Return the saved image uri
return Uri.parse(file.absolutePath)
}
}
The behavior behind inconsistent results for Google Places AutoComplete is you are missing of Billing Account.
Login Google Cloud Platform
Head to Billing menu
If you have a previous billing account, you can assign it to your Google Cloud project. If not, create a new billing account using your debit card.
4)Try billing account API KEY, it will work fine
Check this solution, and I think it would work perfectly and your results will be consistent.
Happy Coding 🤓
thanks for looking by! I've pretty much finished my little application i've been working on. I tried the app on my phone and found an error that I wasn't aware of when using the emulator (rookie mistake I suppose). So when the app is started the user can add like a "happy place" that looks the following:
the issue starts when the user wants to add a picture that he wants to take with the Camera itself. (That itself works). Taking the picture and also displaying it in that small box works (partly). But here is also my issue. After trying the app out on the phone i realised that the taken picture had terrible quality. I googled that issue and found out that i have used a method that only get's me the thumbnail on not the actual full sized picture. I googled more to find out how i can get that to work, unfortnunately i only found the documentation and i couldn't really figure it out either way what i had to change.
Here is my code that contains all info regarding the camera functionality:
package eu.sampa.happyPlaces.activities
import android.Manifest
import android.app.Activity
import android.app.DatePickerDialog
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.location.Location
import android.location.LocationManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Looper
import android.provider.MediaStore
import android.provider.Settings
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.android.gms.location.*
import com.google.android.libraries.places.api.Places
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.widget.Autocomplete
import com.google.android.libraries.places.widget.model.AutocompleteActivityMode
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import eu.sampa.happyPlaces.R
import eu.sampa.happyPlaces.database.DatabaseHandler
import eu.sampa.happyPlaces.models.HappyPlaceModel
import eu.sampa.happyPlaces.utils.GetAddressFromLatLng
import kotlinx.android.synthetic.main.activity_add_happy_place.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStream
import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.*
class AddHappyPlaceActivity : AppCompatActivity(), View.OnClickListener {
// Creates a variable for GALLERY Selection which will be later used in the onActivityResult method.
companion object {
private const val GALLERY = 1
private const val CAMERA = 2
private const val IMAGE_DIRECTORY = "HappyPlacesImages"
private const val PLACE_AUTOCOMPLETE_REQUEST_CODE = 3
}
private var saveImageToInternalStorage : Uri? = null
private var mLatitude : Double = 0.0
private var mLongitude : Double = 0.0
// For the swipe feature
private var mHappyPlaceDetails : HappyPlaceModel? = null
// Will be initialized later for the get current position functionality
// https://medium.com/#droidbyme/get-current-location-using-fusedlocationproviderclient-in-android-cb7ebf5ab88e
private lateinit var mFusedLocationClient: FusedLocationProviderClient
// Creating the variables of Calender Instance and DatePickerDialog listener to use it for date selection
// A variable to get an instance calendar using the default time zone and locale.
private var cal = Calendar.getInstance()
/* A variable for DatePickerDialog OnDateSetListener.
* The listener used to indicate the user has finished selecting a date. It will be initialized later. */
private lateinit var dateSetListener : DatePickerDialog.OnDateSetListener
// Used to increment when someone clicks on the Add Photo button see below in onClick function
private var addButtonClicked = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_happy_place)
// Adds the back button on the ActionBar
setSupportActionBar(toolbar_add_place)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
toolbar_add_place.setNavigationOnClickListener {
onBackPressed()
}
// For the Places API
if(!Places.isInitialized()) {
Places.initialize(this#AddHappyPlaceActivity, resources.getString(R.string.google_maps_key))
}
if(intent.hasExtra(MainActivity.EXTRA_PLACE_DETAILS)) {
mHappyPlaceDetails = intent.getParcelableExtra(MainActivity.EXTRA_PLACE_DETAILS) as HappyPlaceModel
}
// Initialize the DatePicker and sets the selected date
// https://www.tutorialkart.com/kotlin-android/android-datepicker-kotlin-example/
dateSetListener = DatePickerDialog.OnDateSetListener{
_, year, month, dayOfMonth ->
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, month)
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
updateDateInView()
}
// Automatically sets the current date
updateDateInView()
// Uses functionality in the onClick function below
et_date.setOnClickListener(this)
tv_add_image.setOnClickListener(this)
btn_save.setOnClickListener(this)
et_location.setOnClickListener(this)
tv_select_current_location.setOnClickListener(this)
if(mHappyPlaceDetails != null) {
supportActionBar?.title = "Edit Happy PLace"
et_title.setText(mHappyPlaceDetails!!.title)
et_description.setText(mHappyPlaceDetails!!.description)
et_date.setText(mHappyPlaceDetails!!.date)
et_location.setText(mHappyPlaceDetails!!.location)
mLatitude = mHappyPlaceDetails!!.latitude
mLongitude = mHappyPlaceDetails!!.longitude
saveImageToInternalStorage = Uri.parse(mHappyPlaceDetails!!.image)
iv_place_image.setImageURI(saveImageToInternalStorage)
btn_save.text = "UPDATE"
}
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}
// This is a override method after extending the onclick listener interface (gets created automatically)
override fun onClick(v: View?) {
when (v!!.id) {
R.id.et_date -> {
DatePickerDialog(this#AddHappyPlaceActivity, dateSetListener,
cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show()
}
R.id.tv_add_image -> {
val pictureDialog = AlertDialog.Builder(this)
pictureDialog.setTitle("Select Action")
val pictureDialogItems = arrayOf("Select photo from gallery", "Capture photo from camera")
pictureDialog.setItems(pictureDialogItems) {
_, which ->
when(which) {
0 -> choosePhotoFromGallery()
1 -> takePhotoFromCamera()
}
}
pictureDialog.show()
/* Used to display the Dialog to get to the menu after the user
* denied access 2 or more times */
addButtonClicked += 1
if (addButtonClicked > 2) {
if (ContextCompat.checkSelfPermission(this#AddHappyPlaceActivity,
Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
showRationalDialogForPermissions()
}
if (ContextCompat.checkSelfPermission(this#AddHappyPlaceActivity,
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
showRationalDialogForPermissions()
}
if (ContextCompat.checkSelfPermission(this#AddHappyPlaceActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
showRationalDialogForPermissions()
}
}
}
R.id.btn_save -> {
when {
et_title.text.isNullOrEmpty() -> {
Toast.makeText(this, "Please enter title", Toast.LENGTH_SHORT).show()
}
et_description.text.isNullOrEmpty() -> {
Toast.makeText(this, "Please enter description", Toast.LENGTH_SHORT)
.show()
}
et_location.text.isNullOrEmpty() -> {
Toast.makeText(this, "Please select location", Toast.LENGTH_SHORT)
.show()
}
saveImageToInternalStorage == null -> {
Toast.makeText(this, "Please add image", Toast.LENGTH_SHORT).show()
}
else -> {
// Assigning all the values to data model class.
val happyPlaceModel = HappyPlaceModel(
if(mHappyPlaceDetails == null) 0 else mHappyPlaceDetails!!.id,
et_title.text.toString(),
saveImageToInternalStorage.toString(),
et_description.text.toString(),
et_date.text.toString(),
et_location.text.toString(),
mLatitude,
mLongitude
)
// Here we initialize the database handler class.
val dbHandler = DatabaseHandler(this)
if (mHappyPlaceDetails == null) {
val addHappyPlace = dbHandler.addHappyPlace(happyPlaceModel)
if (addHappyPlace > 0) {
setResult(Activity.RESULT_OK)
finish() // Gets us back to MainActivity
}
} else{
val updateHappyPlace = dbHandler.updateHappyPlace(happyPlaceModel)
// greater than zero indicates that everything worked out
if (updateHappyPlace > 0) {
setResult(Activity.RESULT_OK)
finish() // Gets us back to MainActivity
}
}
}
}
}
// For the Places API
R.id.et_location -> {
try{
// This is the list of fields that need to be passed
val fields = listOf(Place.Field.ID, Place.Field.NAME, Place.Field.LAT_LNG, Place.Field.ADDRESS)
// Start the autocomplete intent with a unique request code.
val intent = Autocomplete.IntentBuilder(AutocompleteActivityMode.FULLSCREEN, fields).build(this#AddHappyPlaceActivity)
startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE)
}catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, "Couldnt load it", Toast.LENGTH_SHORT).show()}
}
R.id.tv_select_current_location -> {
if (!isLocationEnabled()) {
Toast.makeText(this, "Your location provider is turned off. Please turn it on.", Toast.LENGTH_SHORT).show()
// This will redirect the user to settings from where you need to turn on the location provider.
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivity(intent)
} else {
// https://www.androdocs.com/kotlin/getting-current-location-latitude-longitude-in-android-using-kotlin.html
Dexter.withActivity(this)
.withPermissions(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION).withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
if (report!!.areAllPermissionsGranted()) {
requestNewLocationData()
}
}
override fun onPermissionRationaleShouldBeShown(
permissions: MutableList<PermissionRequest>?,
token: PermissionToken?
) {
showRationalDialogForPermissions()
}
}).onSameThread()
.check()
}
}
}
}
// https://stackoverflow.com/questions/51313359/get-current-location-android-kotlin
private fun isLocationEnabled(): Boolean {
val locationManager: LocationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER )
}
// Method used for taking pictures with the Camera
private fun takePhotoFromCamera() {
// Asking for permissions using DEXTER Library
Dexter.withContext(this).withPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
).withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
// Here after all the permission are granted launch the Camera to capture an image
val galleryIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(galleryIntent, CAMERA )
}
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
token?.continuePermissionRequest()
}
}).onSameThread().check()
}
// Method used for image selection from GALLERY/PHOTOS
private fun choosePhotoFromGallery() {
// Asking for permissions using DEXTER Library
Dexter.withContext(this).withPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
).withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
// Here after all the permission are granted, launch the gallery to select and image.
val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(galleryIntent,
GALLERY
)
}
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
token?.continuePermissionRequest()
}
}).onSameThread().check()
}
// Message to be shown if user denies access and possibly send him to the settings
private fun showRationalDialogForPermissions() {
AlertDialog.Builder(this).setMessage("It looks like you have turned off " +
"permissions required for this feature").setPositiveButton("GO TO SETTINGS")
{ _, _ ->
try{
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
}
}.setNegativeButton("Cancel") { dialog, _ ->
dialog.dismiss()
}.show()
}
// Handles the chosen Image from the startActivityResult from choosePhotoFromGallery and takePhotoFromCamera
#RequiresApi(Build.VERSION_CODES.P)
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK) {
if(requestCode == GALLERY) {
if(data != null) {
val contentURI = data.data
// For more info go to https://stackoverflow.com/questions/56651444/deprecated-getbitmap-with-api-29-any-alternative-codes
try {
if(Build.VERSION.SDK_INT < 28) {
// Here this is used to get an bitmap from URI
val selectedImageBitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, contentURI)
// Saving an image which is selected from GALLERY. And printed the path in logcat
saveImageToInternalStorage = saveImageToInternalStorage(selectedImageBitmap)
Log.e("Saved image: ", "Path :: $saveImageToInternalStorage")
iv_place_image!!.setImageBitmap(selectedImageBitmap) // Set the selected image from GALLERY to imageView
} else {
val selectedImageBitmapSource = contentURI?.let { ImageDecoder.createSource(this.contentResolver, it) }
val selectedImageBitmap = selectedImageBitmapSource?.let { ImageDecoder.decodeBitmap(it) }
// Saving an image which is selected from GALLERY. And printed the path in logcat
saveImageToInternalStorage = selectedImageBitmap?.let { saveImageToInternalStorage(it) }
Log.e("Saved image: ", "Path :: $saveImageToInternalStorage")
iv_place_image.setImageBitmap(selectedImageBitmap)
}
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(this#AddHappyPlaceActivity, "Failed to load the Image!", Toast.LENGTH_SHORT).show()
}
}
// Camera result will be received here
} else if(requestCode == CAMERA){
val thumbNail : Bitmap = data!!.extras!!.get("data") as Bitmap // Bitmap from camera
// Saving an image which is selected from CAMERA. And printed the path in logcat
saveImageToInternalStorage = saveImageToInternalStorage(thumbNail)
Log.e("Saved image: ", "Path :: $saveImageToInternalStorage")
iv_place_image.setImageBitmap(thumbNail) // Set to the imageView
// For the Places API
} else if(requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE) {
val place : Place = Autocomplete.getPlaceFromIntent(data!!)
et_location.setText(place.address)
mLatitude = place.latLng!!.latitude
mLongitude = place.latLng!!.longitude
}
}
}
// A function to update the selected date in the UI with selected format.
private fun updateDateInView() {
val myFormat = "dd.MM.yyyy"
val sdf = SimpleDateFormat(myFormat, Locale.getDefault())
et_date.setText(sdf.format(cal.time).toString())
}
/* https://android--code.blogspot.com/20 18/04/android-kotlin-save-image-to-internal.html
Uri gives us the location back */
private fun saveImageToInternalStorage(bitmap: Bitmap):Uri {
// Get the context wrapper instance
val wrapper = ContextWrapper(applicationContext)
// This line returns a directory in the internal storage
var file = wrapper.getDir(IMAGE_DIRECTORY, Context.MODE_PRIVATE)
// First we give the location and then we generate a random Name for the Image
file = File(file, "${UUID.randomUUID()}.jpg")
//
try {
val stream : OutputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG,100, stream)
stream.flush()
stream.close()
}catch (e: IOException) {
e.printStackTrace()
}
// Return the saved image uri
return Uri.parse(file.absolutePath)
}
private fun requestNewLocationData() {
var mLocationRequest = LocationRequest()
mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
mLocationRequest.interval = 1000
mLocationRequest.numUpdates = 1
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper())
}
private val mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
val mLastLocation: Location = locationResult.lastLocation
mLatitude = mLastLocation.latitude
Log.e("Current Latitude", "$mLatitude")
mLongitude = mLastLocation.longitude
Log.e("Current Longitude", "$mLongitude")
val addressTask = GetAddressFromLatLng(this#AddHappyPlaceActivity, mLatitude, mLongitude)
addressTask.setAddressListener(object : GetAddressFromLatLng.AddressListener {
override fun onAddressFound(address: String?) {
Log.e("Address ::", "" + address)
et_location.setText(address) // Address is set to the edittext
}
override fun onError() {
Log.e("Get Address ::", "Something is wrong...")
}
})
addressTask.executeGetAddress()
}
}
}