I am working on android App using Kotlin Language that is similar to Pokemon Go,
There is no problems in the App but when I installed it in my phone , only an empty Map is shown and the person is not going to my location How can I solve that ?
MapsActivity.tkt
package ahmedchtn.pockemontn
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Build
import android.support.v4.app.FragmentActivity
import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.widget.Toast
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.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
class MapsActivity : FragmentActivity(), OnMapReadyCallback {
//WORK WITH USER LOCATION
private var mMap: GoogleMap? = null
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)
checkPermmison()
LoadPockemon()
}
var ACCESSLOCATION = 123
fun checkPermmison() {
if (Build.VERSION.SDK_INT >= 23) {
if (ActivityCompat.
checkSelfPermission(this,
android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), ACCESSLOCATION)
return
}
}
GetUserLocation()
}
fun GetUserLocation() {
Toast.makeText(this, "User location access on", Toast.LENGTH_LONG).show()
//TODO: Will implement later
var myLocation = MylocationListener()
var locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3, 3f, myLocation)
var mythread = myThread()
mythread.start()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
ACCESSLOCATION -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
GetUserLocation()
} else {
Toast.makeText(this, "We cannot access to your location", Toast.LENGTH_LONG).show()
}
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
/**
* 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
}
var location: Location? = null
//Get user location
inner class MylocationListener : LocationListener {
constructor() {
location = Location("Start")
location!!.longitude = 0.0
location!!.longitude = 0.0
}
override fun onLocationChanged(p0: Location?) {
location = p0
}
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
//TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onProviderEnabled(p0: String?) {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onProviderDisabled(p0: String?) {
//TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
var oldLocation: Location? = null
inner class myThread : Thread {
constructor() : super() {
oldLocation = Location("Start")
oldLocation!!.longitude = 0.0
oldLocation!!.longitude = 0.0
}
override fun run() {
while (true) {
try {
if (oldLocation!!.distanceTo(location) == 0f) {
continue
}
oldLocation = location
runOnUiThread {
mMap!!.clear()
// show me
val sydney = LatLng(location!!.latitude, location!!.longitude)
mMap!!.addMarker(MarkerOptions()
.position(sydney)
.title("Me")
.snippet(" here is my location")
.icon(BitmapDescriptorFactory.fromResource(R.drawable.naruto)))
mMap!!.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 14f))
// show pockemons
for (i in 0..listPockemons.size - 1) {
var newPockemon = listPockemons[i]
if (newPockemon.IsCatch == false) {
val pockemonLoc = LatLng(newPockemon.location!!.latitude, newPockemon.location!!.longitude)
mMap!!.addMarker(MarkerOptions()
.position(pockemonLoc)
.title(newPockemon.name!!)
.snippet(newPockemon.des!! + ", power:" + newPockemon!!.power)
.icon(BitmapDescriptorFactory.fromResource(newPockemon.image!!)))
if (location!!.distanceTo(newPockemon.location) < 2) {
newPockemon.IsCatch = true
listPockemons[i] = newPockemon
playerPower += newPockemon.power!!
Toast.makeText(applicationContext,
"You catch new pockemon your new pwoer is " + playerPower,
Toast.LENGTH_LONG).show()
}
}
}
}
Thread.sleep(1000)
} catch (ex: Exception) {
}
}
}
}
var playerPower = 0.0
var listPockemons = ArrayList<Pockemon>()
fun LoadPockemon() {
listPockemons.add(Pockemon(R.drawable.charmandertn,
"Charmander", "Charmander living in japan", 55.0, 35.687997, 10.085267))
listPockemons.add(Pockemon(R.drawable.bulbasaurtn,
"Bulbasaur", "Bulbasaur living in usa", 90.5, 35.687657, 10.084838))
listPockemons.add(Pockemon(R.drawable.squirtletn,
"Squirtle", "Squirtle living in iraq", 33.5, 35.687552, 10.084623))
}
}
Pockemon.tkt
package ahmedchtn.pockemontn
import android.location.Location
/**
* Created by Ahmed on 17-06-2017.
*/
class Pockemon{
var name:String?=null
var des:String?=null
var image:Int?=null
var power:Double?=null
var location:Location?=null
var IsCatch:Boolean?=false
constructor(image:Int,name:String,des:String,power:Double,lat:Double,log:Double){
this.name=name
this.des=des
this.image=image
this.power=power
this.location= Location(name)
this.location!!.latitude=lat
this.location!!.longitude=log
this.IsCatch=false
}
}
AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ahmedchtn.pockemontn">
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the 'MyLocation' functionality.
-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<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">
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<activity
android:name=".MapsActivity"
android:label="#string/title_activity_maps">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Thanks in advance !
Check your permissions in settings - If the app does not have permission to use your location, it might show up blank.
Get a Google Map Api key.
Make sure you select the Map template when creating your app.
After creating your app, change your app from debug to release.
Now go to google_maps_api.xml and insert your api key into a
specified potion in the file that tells you to replace the
"google_maps_key" there.
Rebuild and run your app. The map should be showing now.
To make the Pokemons show, Your LoadPokemon() function call should be in the checkPermission(){} function after GetUserLocation() and not in the onCreate() function.
Related
I'm trying to apply a tutorial about request permission when the app start, but when I tested on my real device android 12, it's not show the permission dialog it show a snack bar that request from user to go to setting and grant it manually
Manifiest file
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- TODO: Step 1 add in permissions for fine location and background-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<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">
<activity android:name=".HuntMainActivity"
android:label="#string/title_activity_hunt"
android:launchMode="singleInstance"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".GeofenceBroadcastReceiver"/>
</application>
</manifest>
this my code
class HuntMainActivity : AppCompatActivity() {
private lateinit var binding: ActivityHuntMainBinding
private lateinit var geofencingClient: GeofencingClient
private lateinit var viewModel: GeofenceViewModel
private val runningQOrLater = android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.Q
// A PendingIntent for the Broadcast Receiver that handles geofence transitions.
// TODO: Step 8 add in a pending intent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_hunt_main)
viewModel = ViewModelProvider(this, SavedStateViewModelFactory(this.application,
this)).get(GeofenceViewModel::class.java)
binding.viewmodel = viewModel
binding.lifecycleOwner = this
// TODO: Step 9 instantiate the geofencing client
// Create channel for notifications
createChannel(this )
}
override fun onStart() {
super.onStart()
checkPermissionsAndStartGeofencing()
}
/*
* When we get the result from asking the user to turn on device location, we call
* checkDeviceLocationSettingsAndStartGeofence again to make sure it's actually on, but
* we don't resolve the check to keep the user from seeing an endless loop.
*/
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// TODO: Step 7 add code to check that the user turned on their device location and ask
// again if they did not
}
/*
* When the user clicks on the notification, this method will be called, letting us know that
* the geofence has been triggered, and it's time to move to the next one in the treasure
* hunt.
*/
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
val extras = intent?.extras
if(extras != null){
if(extras.containsKey(GeofencingConstants.EXTRA_GEOFENCE_INDEX)){
viewModel.updateHint(extras.getInt(GeofencingConstants.EXTRA_GEOFENCE_INDEX))
checkPermissionsAndStartGeofencing()
}
}
}
/*
* In all cases, we need to have the location permission. On Android 10+ (Q) we need to have
* the background permission as well.
*/
#SuppressLint("MissingSuperCall")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
Log.d(TAG, "onRequestPermissionResult")
if (
grantResults.isEmpty() ||
grantResults[LOCATION_PERMISSION_INDEX] == PackageManager.PERMISSION_DENIED ||
(requestCode == REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE &&
grantResults[BACKGROUND_LOCATION_PERMISSION_INDEX] ==
PackageManager.PERMISSION_DENIED))
{
Snackbar.make(
binding.activityMapsMain,
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE
)
.setAction(R.string.settings) {
startActivity(Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.show()
} else {
checkDeviceLocationSettingsAndStartGeofence()
}
}
/**
* This will also destroy any saved state in the associated ViewModel, so we remove the
* geofences here.
*/
override fun onDestroy() {
super.onDestroy()
removeGeofences()
}
/**
* Starts the permission check and Geofence process only if the Geofence associated with the
* current hint isn't yet active.
*/
private fun checkPermissionsAndStartGeofencing() {
if (viewModel.geofenceIsActive()) return
if (foregroundAndBackgroundLocationPermissionApproved()) {
checkDeviceLocationSettingsAndStartGeofence()
} else {
requestForegroundAndBackgroundLocationPermissions()
}
}
/*
* Uses the Location Client to check the current state of location settings, and gives the user
* the opportunity to turn on location services within our app.
*/
private fun checkDeviceLocationSettingsAndStartGeofence(resolve:Boolean = true) {
val locationRequest = LocationRequest.create().apply {
priority = LocationRequest.PRIORITY_LOW_POWER
}
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
val settingsClient = LocationServices.getSettingsClient(this)
val locationSettingsResponseTask =
settingsClient.checkLocationSettings(builder.build())
locationSettingsResponseTask.addOnFailureListener { exception ->
if (exception is ResolvableApiException && resolve){
try {
exception.startResolutionForResult(this#HuntMainActivity,
REQUEST_TURN_DEVICE_LOCATION_ON)
} catch (sendEx: IntentSender.SendIntentException) {
Log.d(TAG, "Error getting location settings resolution: " + sendEx.message)
}
} else {
Snackbar.make(
binding.activityMapsMain,
R.string.location_required_error, Snackbar.LENGTH_INDEFINITE
).setAction(android.R.string.ok) {
checkDeviceLocationSettingsAndStartGeofence()
}.show()
}
}
locationSettingsResponseTask.addOnCompleteListener {
if ( it.isSuccessful ) {
addGeofenceForClue()
}
}
}
/*
* Determines whether the app has the appropriate permissions across Android 10+ and all other
* Android versions.
*/
#TargetApi(29)
private fun foregroundAndBackgroundLocationPermissionApproved(): Boolean {
val foregroundLocationApproved = (
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION))
val backgroundPermissionApproved =
if (runningQOrLater) {
PackageManager.PERMISSION_GRANTED ==
ActivityCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
} else {
true
}
return foregroundLocationApproved && backgroundPermissionApproved
}
/*
* Requests ACCESS_FINE_LOCATION and (on Android 10+ (Q) ACCESS_BACKGROUND_LOCATION.
*/
#TargetApi(29 )
private fun requestForegroundAndBackgroundLocationPermissions() {
if (foregroundAndBackgroundLocationPermissionApproved())
return
var permissionsArray = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
val resultCode = when {
runningQOrLater -> {
permissionsArray += Manifest.permission.ACCESS_BACKGROUND_LOCATION
REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE
}
else -> REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE
}
Log.d(TAG, "Request foreground only location permission")
ActivityCompat.requestPermissions(
this#HuntMainActivity,
permissionsArray,
resultCode
)
}
/*
* Adds a Geofence for the current clue if needed, and removes any existing Geofence. This
* method should be called after the user has granted the location permission. If there are
* no more geofences, we remove the geofence and let the viewmodel know that the ending hint
* is now "active."
*/
private fun addGeofenceForClue() {
// TODO: Step 10 add in code to add the geofence
}
/**
* Removes geofences. This method should be called after the user has granted the location
* permission.
*/
private fun removeGeofences() {
// TODO: Step 12 add in code to remove the geofences
}
companion object {
internal const val ACTION_GEOFENCE_EVENT =
"HuntMainActivity.treasureHunt.action.ACTION_GEOFENCE_EVENT"
}
}
private const val REQUEST_FOREGROUND_AND_BACKGROUND_PERMISSION_RESULT_CODE = 33
private const val REQUEST_FOREGROUND_ONLY_PERMISSIONS_REQUEST_CODE = 34
private const val REQUEST_TURN_DEVICE_LOCATION_ON = 29
private const val TAG = "HuntMainActivity"
private const val LOCATION_PERMISSION_INDEX = 0
private const val BACKGROUND_LOCATION_PERMISSION_INDEX = 1
I tested it on my real device, what i excopecting is showing the permission dialog when the app starts
The app is similar to the Pokemon Go app. It's running beautifully in the emulator, however when run in my android phone, my location in not showing.
This is my activity where i used maps:
package com.example.catchem
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
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.MarkerOptions
import com.example.catchem.databinding.ActivityDisplayBinding
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import java.lang.Exception
class Display : AppCompatActivity(), OnMapReadyCallback {
var usr_name=""
var myPower=0.0
var c=0;
var current_lat:Double=0.0
var current_long: Double=0.0
private lateinit var mMap: GoogleMap
private lateinit var binding: ActivityDisplayBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
usr_name=intent.getStringExtra(Data.usr_name).toString()
binding = ActivityDisplayBinding.inflate(layoutInflater)
setContentView(binding.root)
// 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)
checkPermission()
}
var accessLocation=123
fun checkPermission() {
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),accessLocation)
return
}
getLocation()
}
fun getLocation() {
var myLocation=MyLocation()
var locationManager=getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3, 3f, myLocation)
var myThread=updateThread()
myThread.start()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when(requestCode) {
accessLocation ->{
if (grantResults[0]==PackageManager.PERMISSION_GRANTED)
getLocation()
else
Toast.makeText(this, "Cannot access your location", Toast.LENGTH_LONG).show()
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
// Add a marker in Sydney and move the camera
}
var currentLocation: Location?=null
inner class MyLocation: LocationListener {
constructor() {
currentLocation= Location("start")
currentLocation!!.latitude=0.0
currentLocation!!.longitude=0.0
}
override fun onLocationChanged(location: Location) {
currentLocation=location
current_lat=currentLocation!!.latitude
current_long=currentLocation!!.longitude
c++;
if(c==1)
loadPokemons()
}
}
var old_Locattion: Location?=null
inner class updateThread: Thread {
constructor(): super() {
old_Locattion= Location("start")
old_Locattion!!.longitude=0.0
old_Locattion!!.latitude=0.0
}
override fun run() {
while (true) {
try {
if(old_Locattion!!.distanceTo(currentLocation)==0f)
continue
old_Locattion=currentLocation
runOnUiThread() {
//my location
mMap!!.clear()
val sydney = LatLng(currentLocation!!.latitude, currentLocation!!.longitude)
Log.i("me", "lat: ${currentLocation!!.latitude} long: ${currentLocation!!.longitude}")
mMap.addMarker(MarkerOptions().position(sydney).title(usr_name).snippet("Current power is: $myPower").icon(BitmapDescriptorFactory.fromResource(R.drawable.ash1)))
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney,16f))
//pokemon location
for(i in 0..poke_list.size-1) {
var new_poke = poke_list[i]
val sydney = LatLng(new_poke.loc_poke!!.latitude, new_poke.loc_poke!!.longitude)
mMap.addMarker(MarkerOptions().position(sydney).
title(new_poke.name!!).
snippet("Type: ${new_poke.type!!} Power: ${new_poke.power}").
icon(BitmapDescriptorFactory.fromResource(new_poke.img!!)))
Log.i("${new_poke.name}", "lat: ${new_poke.loc_poke!!.latitude} long: ${new_poke.loc_poke!!.longitude}")
if (currentLocation!!.distanceTo(new_poke.loc_poke!!)<50) {
myPower+=new_poke.power!!
new_poke.loc_poke!!.latitude=rand_lat()
new_poke.loc_poke!!.longitude=rand_long()
poke_list[i]=new_poke
Toast.makeText(applicationContext, "Your caught a ${new_poke.name} Your current power is $myPower", Toast.LENGTH_LONG).show()
}
}
}
Thread.sleep(2000)
}catch (e:Exception) {Log.i("Exception: ", "$e")}
}
}
}
var poke_list=ArrayList<Pokemons>()
fun loadPokemons() {
poke_list.add(Pokemons("Bulbasaur", "Grass", 20.0, rand_lat(), rand_long(),R.drawable.bulbasaur))
poke_list.add(Pokemons("Charmander", "Fire", 21.0, rand_lat(), rand_long(),R.drawable.charmander))
poke_list.add(Pokemons("Darkrai", "Dark", 37.0, rand_lat(), rand_long(),R.drawable.darkrai))
poke_list.add(Pokemons("Eevee", "Normal", 22.0, rand_lat(), rand_long(),R.drawable.eevee))
poke_list.add(Pokemons("Pikachu", "Electric", 22.0, rand_lat(), rand_long(),R.drawable.pikachu))
poke_list.add(Pokemons("Raichu", "Electric", 32.0, rand_lat(), rand_long(),R.drawable.raichu))
poke_list.add(Pokemons("Squirtle", "Water", 20.0, rand_lat(), rand_long(),R.drawable.squirtle))
poke_list.add(Pokemons("Umbreon", "Dark", 33.0, rand_lat(), rand_long(),R.drawable.umbreon))
}
fun rand_lat(): Double {
var x:Int=(Math.random()*10).toInt()
var y=(Math.random()/100)
if(x%2==0)
return (current_lat+y)
else
return (current_lat-y)
}
fun rand_long(): Double {
var x:Int=(Math.random()*10).toInt()
var y=(Math.random()/100)
if(x%2==0)
return (current_long+y)
else
return (current_long-y)
}
}
This is my AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.catchem" >
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the "MyLocation" functionality.
-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="#mipmap/logo"
android:label="#string/app_name"
android:roundIcon="#mipmap/logo_round"
android:supportsRtl="true"
android:theme="#style/Theme.Catchem" >
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<activity
android:name=".Display"
android:exported="true"
android:label="#string/title_activity_display" />
<activity
android:name=".MainActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
And this is my xml file of the activity:
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/map"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Display" />
Also, this is the error that shows up when i run the app on my phone:
Run Screenshot
Finally, Found out the problem!!!!
Used NETWORK_PROVIDER instead of GPS_PROVIDER and its working like a charm!
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
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 🤓
I've written a unit test that use a mock location provider. The test passes when a location update is received, or fails when it times out.
The test passes fine running on emulated Pixel 3's, Android M through Android O. It times out on Android P and on Q. I also tested on a physical Pixel 3 with Q, still fails.
I've been beating my head against this for a while and can't figure out what's going on.
Test:
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.location.Location
import android.location.LocationManager
import androidx.test.rule.GrantPermissionRule
import design.inhale.androidtestutils.InstrumentedTest
import design.inhale.datasource.observer.NullDataSourceListener
import design.inhale.testutils.Latch
import design.inhale.utils.locationManager
import design.inhale.utils.mock
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.core.Is
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
class DeviceLocationSourceTest: InstrumentedTest() {
private val mockProviderName = "MockProvider"
private val locationManager: LocationManager
get() = appContext.locationManager
#get:Rule
val permissionRule: GrantPermissionRule = grant(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION)
#Before
fun init() {
addMockLocationProvider()
}
#After
fun deinit() {
instrumentation.waitForIdleSync()
removeMockLocationProvider()
}
#Test(timeout = 10_000)
fun receiveLocationUpdate() {
val latch = Latch()
val listener = object: LocationSourceListener {
override fun onDataUpdated(data: Location) {
with(data) {
assertThat(latitude, Is(equalTo(0.0)))
assertThat(longitude, Is(equalTo(0.0)))
}
latch.release()
}
}
mockLocationSource(listener).start()
instrumentation.waitForIdleSync() // in case we're hitting race conditions?
updateMockLocation(0.0, 0.0)
latch.await()
}
#Suppress("SameParameterValue")
private fun updateMockLocation(latitude: Double, longitude: Double) {
val location = Location(mockProviderName).mock(latitude, longitude)
locationManager.setTestProviderLocation(mockProviderName, location)
}
private fun mockLocationSource(listener: LocationSourceListener = NullDataSourceListener()) =
DeviceLocationSource(appContext, mockProviderName, listener)
private fun addMockLocationProvider() {
with(locationManager) {
try {
addTestProvider(
mockProviderName,
false,
false,
false,
false,
true,
true,
true,
0,
5)
} catch (e: IllegalArgumentException) {
// If this is caught, the mock provider already exists
}
setTestProviderEnabled(mockProviderName, true)
}
}
private fun removeMockLocationProvider() = locationManager.removeTestProvider(mockProviderName)
}
Manifest:
<manifest package="design.inhale.locationapi"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>
androidTest/Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="design.inhale.locationapi"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
</manifest>
Location.mock():
#SuppressLint("ObsoleteSdkInt")
fun Location.mock(latitude: Double = 0.0, longitude: Double = 0.0, accuracy: Float = 0f): Location {
this.latitude = latitude
this.longitude = longitude
this.accuracy = accuracy
this.time = currentTimeMillis()
if (SDK_INT > JELLY_BEAN) this.elapsedRealtimeNanos = elapsedRealtimeNanos()
return this
}
Turns out I needed to add permission for background location access (I guess the test counts as running in the background?).
Added permission to the test manifest.
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>`
and changed the test to grant it:
val permissionRule: GrantPermissionRule = grant(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION)
Note that you'll want to make sure you don't add ACCESS_BACKGROUND_LOCATION to your production manifest. Google will pull your app if they find you're using this permission without a good reason.