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.
Related
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
}
}
After I updated my phone to Android 12, my app (TargetSDKVersion 29) stopped to get location updates. So I have to update the app to API 31.
For this I want to write some instrumented tests for the location updates. I started with the simple test for requestLocationUpdates(). But in this simple test I don't get any location update.
Just in case I also adjusted the timestamps to 5s intervals to simulate some time between the updates - but no luck.
package de.leo.android.buddytracker_lib
import android.content.Context
import android.location.*
import android.os.Looper
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
private const val MOCK_PROVIDER = "MockLocationProvider"
private const val LOG_TAG ="**** Test ****"
class LocationHandlerAndroidTests : LocationListener {
private lateinit var context: Context
private lateinit var locationManager: LocationManager
private var locationUpdateCount = 0
private var currentLocation: Location? = null
override fun onLocationChanged(location: Location) {
locationUpdateCount++
currentLocation = location
}
#Before
fun init() {
context = InstrumentationRegistry.getInstrumentation().context
locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.addTestProvider(
MOCK_PROVIDER,
false,
false,
false,
false,
false,
false,
false,
Criteria.POWER_LOW,
Criteria.ACCURACY_FINE
)
locationManager.setTestProviderEnabled(MOCK_PROVIDER, true)
assertTrue(locationManager.isLocationEnabled)
locationUpdateCount = 0
currentLocation = null
Thread.sleep(5000L)
}
#After
fun tearDown() {
locationManager.removeTestProvider(MOCK_PROVIDER)
}
#Test
fun requestLocationUpdatesTest() {
Log.d(LOG_TAG, "Requesting Location Updates")
locationManager.requestLocationUpdates(MOCK_PROVIDER, 4000, 10.0f, this, Looper.getMainLooper())
val location1 = Location(MOCK_PROVIDER)
location1.latitude = 10.0
location1.longitude = 20.0
location1.accuracy = 2.0f
location1.time = 1000
location1.elapsedRealtimeNanos = 10000000000
Log.d(LOG_TAG, "set location 1")
locationManager.setTestProviderLocation(MOCK_PROVIDER, location1)
Thread.sleep(5000L)
val location2 = Location(MOCK_PROVIDER)
location2.latitude = 11.0
location2.longitude = 21.0
location2.accuracy = 2.0f
location2.time = 5000
location2.elapsedRealtimeNanos = 15000000000
Log.d(LOG_TAG, "set location 2")
locationManager.setTestProviderLocation(MOCK_PROVIDER, location2)
Thread.sleep(5000L)
// Check if your listener reacted the right way
assertEquals("Got location updates", 2, locationUpdateCount)
assertEquals(11.0, currentLocation?.latitude)
assertEquals(21.0, currentLocation?.longitude)
}
}
Any tips what I made wrong here?
Iam and AR android app. I can load my .sfb files from asset. i want to load from direct server in order to secure my assets. Its loading from asset folder. not from direct server. iam using below code pls help me to solve this.
package com.example.a320_ar
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import com.google.ar.core.Anchor
import com.google.ar.core.Plane
import com.google.ar.sceneform.AnchorNode
import com.google.ar.sceneform.HitTestResult
import com.google.ar.sceneform.SkeletonNode
import com.google.ar.sceneform.animation.ModelAnimator
import com.google.ar.sceneform.rendering.ModelRenderable
import com.google.ar.sceneform.ux.ArFragment
import com.google.ar.sceneform.ux.TransformableNode
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
class MainActivity : AppCompatActivity() {
lateinit var arFragment: ArFragment
private lateinit var model: Uri
private var rendarable: ModelRenderable?=null
private var animator: ModelAnimator? = null
//private var modellink:String = "A320_Anim.sfb"
private var modellink:String = "http://10.0.0.193:90/fbx/A320_Anim.sfb"
#RequiresApi(Build.VERSION_CODES.N)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
arFragment = sceneform_fragment as ArFragment
arFragment.setOnTapArPlaneListener { hitResult, plane, motionEvent ->
if (plane.type != Plane.Type.HORIZONTAL_UPWARD_FACING) {
return#setOnTapArPlaneListener
}
var anchor = hitResult.createAnchor()
btnStart.setOnClickListener {
placeObject(
arFragment,
anchor,
Uri.parse(modellink)
)
}
}
}
#RequiresApi(Build.VERSION_CODES.N)
private fun animateModel(name: String) {
animator?.let { it->
if(it.isRunning){
it.end()
}
}
rendarable?.let { modelRenderable ->
val data = modelRenderable.getAnimationData(name)
animator = ModelAnimator(data,modelRenderable)
animator?.start()
}
}
#RequiresApi(Build.VERSION_CODES.N)
private fun placeObject(arFragment: ArFragment, anchor: Anchor?, model: Uri?) {
ModelRenderable.builder()
.setSource(arFragment.context,model)
.build()
.thenAccept{
rendarable = it
addtoScene(arFragment, anchor, it)
}
.exceptionally {
val builder = AlertDialog.Builder(this)
builder.setMessage( it.message).setTitle("Error")
val dialog = builder.create()
dialog.show()
return#exceptionally null
}
}
private fun addtoScene(arFragment: ArFragment, anchor: Anchor?, it: ModelRenderable?) {
val anchorNode = AnchorNode(anchor)
val skeletonNode = SkeletonNode()
skeletonNode.renderable = rendarable
Toast.makeText(this,"inside add scene",Toast.LENGTH_SHORT).show()
val node = TransformableNode(arFragment.transformationSystem)
node.addChild(skeletonNode)
node.setParent(anchorNode)
node.setOnTapListener { v: HitTestResult?, event: MotionEvent? ->
//msgText.text = "Tapped me...$anchorNode --- $anchor --- $skeletonNode"
// var bt = findViewById<Button>(R.id.btnDel)
//bt.visibility = View.VISIBLE
//removeAnchorNode(anchorNode)
//bt.setOnClickListener { removeAnchorNode(anchorNode) }
}
arFragment.arSceneView.scene.addChild(anchorNode)
}
}
its working fine no errors. but its not showing my object on fragment.
private var modellink:String = "http://10.0.0.193:90/fbx/A320_Anim.sfb" (not loading .sfb)
private var modellink:String = "A320_Anim.sfb" (Loading the .sfb- woring fine)
please help me to load the model directly from server. i used all the permissions correctly.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
Thanks in advance,
Syed Abdul Rahim
Are you still using the old 1.6 version of Sceneform? You could try the new maintained version and also discard the sfb format and switch to glTF. The maintained version is up to date related to android dependencies and ARCore/Filament.
Well to your second question, if you want to secure your assets you have to serve it from a password protected API-Endpoint, but you have to host your own server, maybe some simpler solutions exists. Strings or text files can be directly secured with on board libraries (https://developer.android.com/guide/topics/security/cryptography)
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 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.