Altbeacon library not able to find/trace transmitting beacon near device - android

I am using altbeacon library for contact tracing. But i am not able to find/trace my device when it comes near my other scanning phone. I have two mobiles basically, one i am using for scanning and other as beacon transmitter. I am able to transmit as beacon from my phone number 2. I tested also in Locate Beacon app. It showed my phone in that. But when i am testing on my phone number 1, its not working. I am not able to see any beacon on any logs even though my didDetermineStateForRegion and onBeaconServiceConnect is being called.
Here is my application class below:
package com.example.mybeaconprojectaye
import android.app.*
import android.bluetooth.le.AdvertiseCallback
import android.bluetooth.le.AdvertiseSettings
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.RemoteException
import android.util.Log
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.altbeacon.beacon.*
import org.altbeacon.beacon.startup.BootstrapNotifier
import org.altbeacon.beacon.startup.RegionBootstrap
import java.util.*
class MyApplication : Application(), BootstrapNotifier, BeaconConsumer {
val CHANNEL_ID = "myproximityservice"
val CHANNEL_NAME = "My Proximity Service Channel"
val backgroundBetweenScanPeriod = 6200L
val backgroundScanPeriod = 3000L
val TAG: String = "xoxo"
val REGIONID = "rangeid"
val uuidString: String= "id1"
lateinit var beaconManager: BeaconManager
private var regionBootstrap: RegionBootstrap? = null
override fun onCreate() {
super.onCreate()
beaconManager = BeaconManager.getInstanceForApplication(this)
setupBeaconScanning()
beaconManager.bind(this)
}
override fun onBeaconServiceConnect() {
Log.e(TAG, "Service connected ")
val rangeNotifier = RangeNotifier { beacons, region ->
if (beacons.size > 0) {
Log.e(TAG, "found new beacons " + beacons.size)
for (beacon: Beacon in beacons){
Log.e(TAG,"New Beacon before condition check=${beacon.id2}-${beacon.id3}-${beacon.id1}")
GlobalScope.launch {
try {
val deviceUUID: String = beacon.id1.toString()
Log.e(TAG, " before condition check=${deviceUUID}")
Log.e(
TAG,
"New Beacon=${beacon.id2}/${beacon.id3}/${beacon.id1}"
)
Log.e("xoxo","${beacon.id2}/${beacon.id3}/${beacon.id1} + "+ beacon.distance.toLong())
if (beacon.distance.toInt() < 2) {
/* val intentNotification = Intent(this#BeaconApp, HomeActivity::class.java)
intentNotification.putExtra(Constants.DeviceConstants.IS_VIBRATOR, true)
intentNotification.flags =
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intentNotification)*/
//sendSafetyNotification()
}
}catch (ex: Exception){
Log.e(TAG, " EXCEPTION: "+ex.toString())
}
}
}
// sendBroadcast(Intent(NEW_DEVICE_ACTION))
}
}
try {
beaconManager.startRangingBeaconsInRegion(
Region(
REGIONID,
null,
null,
null
)
)
beaconManager.addRangeNotifier(rangeNotifier)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun didDetermineStateForRegion(state: Int, p1: Region?) {
Log.e("xoxo", "didDetermineStateForRegion state: "+state )
}
override fun didEnterRegion(p0: Region?) {
Log.e("xoxo", "i just saw a beacon")
}
override fun didExitRegion(p0: Region?) {
}
fun setupBeaconScanning() {
beaconManager.beaconParsers.clear()
val altbeaconParser =
BeaconParser().setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25")
altbeaconParser.setHardwareAssistManufacturerCodes(intArrayOf(0x0118))
beaconManager.beaconParsers
.add(altbeaconParser)
val iBeaconParser =
BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24")
iBeaconParser.setHardwareAssistManufacturerCodes(intArrayOf(0x004c))
beaconManager.beaconParsers
.add(iBeaconParser)
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout(BeaconParser.URI_BEACON_LAYOUT))
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout(BeaconParser.EDDYSTONE_TLM_LAYOUT))
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout(BeaconParser.EDDYSTONE_UID_LAYOUT))
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout(BeaconParser.EDDYSTONE_URL_LAYOUT))
/* beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"))
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout("s:0-1=feaa,m:2-2=00,p:3-3:-41,i:4-13,i:14-19"))
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout("x,s:0-1=feaa,m:2-2=20,d:3-3,d:4-5,d:6-7,d:8-11,d:12-15"))
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout("s:0-1=feaa,m:2-2=10,p:3-3:-41,i:4-20v"))
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"))
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"))
beaconManager.beaconParsers
.add(BeaconParser().setBeaconLayout("m:0-3=4c000215,i:4-19,i:20-21,i:22-23,p:24-24"))
*/
BeaconManager.setDebug(false)
val builder = Notification.Builder(this)
builder.setSmallIcon(R.drawable.ic_launcher_background)
builder.setContentTitle("Proximity Service Running")
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
)
builder.setContentIntent(pendingIntent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT
)
channel.description = "Used for scanning near by device"
val notificationManager = getSystemService(
Context.NOTIFICATION_SERVICE
) as NotificationManager
notificationManager.createNotificationChannel(channel)
builder.setChannelId(channel.id)
}
beaconManager.enableForegroundServiceScanning(builder.build(), 456)
// For the above foreground scanning service to be useful, you need to disable
// JobScheduler-based scans (used on Android 8+) and set a fast background scan
// cycle that would otherwise be disallowed by the operating system.
beaconManager.setEnableScheduledScanJobs(false)
beaconManager.backgroundBetweenScanPeriod = backgroundBetweenScanPeriod
beaconManager.backgroundScanPeriod = backgroundScanPeriod
Log.d(TAG, "setting up background monitoring for beacons and power saving")
// wake up the app when a beacon is seen
// wake up the app when a beacon is seentitle getting
val region = Region(
REGIONID,
null, null, null
)
regionBootstrap = RegionBootstrap(this, region)
}
fun startAdvertising(listener: AdvertiseListener):Boolean {
val result = BeaconTransmitter.checkTransmissionSupported(this)
Log.e("xoxo", "BLE TRANSMITTER STATUS " +(result== BeaconTransmitter.SUPPORTED).toString())
if (BeaconTransmitter.SUPPORTED != result)
return false
val beacon = Beacon.Builder()
.setId1("2f234454-cf6d-4a0f-adf2-f4911ba9ffa6")
.setId2("1")
.setId3("2")
.setManufacturer(0x0118) // Radius Networks. Change this for other beacon layouts
.setTxPower(-59)
.setDataFields(Arrays.asList(*arrayOf(0L))) // Remove this for beacon layouts without d: fields
.build()
// Change the layout below for other beacon types
val beaconParser = BeaconParser()
.setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25")
val beaconTransmitter =
BeaconTransmitter(applicationContext, beaconParser)
beaconTransmitter.startAdvertising(beacon, object : AdvertiseCallback() {
override fun onStartFailure(errorCode: Int) {
Log.e(TAG, "Advertisement start failed with code: $errorCode")
listener.onAdvertiseStatus(false)
}
override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
Log.e(TAG, "Advertisement start succeeded. uuid"+uuidString)
listener.onAdvertiseStatus(true)
}
})
return true
}
interface AdvertiseListener{
fun onAdvertiseStatus(success:Boolean)
}
}
and my MainActivity:
package com.example.mybeaconprojectaye
import android.Manifest
import android.app.AlertDialog
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
import org.altbeacon.beacon.BeaconTransmitter
class MainActivity : AppCompatActivity() {
companion object {
private const val PERMISSION_REQUEST_FINE_LOCATION = 1
private const val PERMISSION_REQUEST_BACKGROUND_LOCATION = 2
}
val TAG = "xoxo"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var result : Int = BeaconTransmitter.checkTransmissionSupported(this#MainActivity)
Log.e("xoxo", "result: "+result)
btn.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
requestPerms()
}
})
}
fun requestPerms() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED
) {
val builder =
AlertDialog.Builder(this)
builder.setTitle("Location is off")
builder.setMessage("Please allow location permission.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener {
requestPermissions(
arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
PERMISSION_REQUEST_BACKGROUND_LOCATION
)
}
builder.show()
} else startAdvertiseBeacons()
} else startAdvertiseBeacons()
} else {
requestPermissions(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
),
PERMISSION_REQUEST_FINE_LOCATION
)
}
} else startAdvertiseBeacons()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
when (requestCode) {
PERMISSION_REQUEST_FINE_LOCATION -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "fine location permission granted")
requestPerms()
} else {
val builder =
AlertDialog.Builder(this)
builder.setTitle("Functionality limited")
builder.setMessage("Since location access has not been granted, this app will not be able to discover devices.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener { }
builder.show()
}
return
}
PERMISSION_REQUEST_BACKGROUND_LOCATION -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "background location permission granted")
requestPerms()
} else {
val builder =
AlertDialog.Builder(this)
builder.setTitle("Functionality limited")
builder.setMessage("Since background location access has not been granted, this app will not be able to discover devices when in the background.")
builder.setPositiveButton(
android.R.string.ok,
DialogInterface.OnClickListener { dialog, which ->
dialog.cancel()
requestPerms()
})
builder.setOnDismissListener {
requestPerms()
}
builder.show()
}
return
}
}
}
private fun startAdvertiseBeacons() {
(application as MyApplication).startAdvertising(object : MyApplication.AdvertiseListener {
override fun onAdvertiseStatus(success: Boolean) {
}
})
}
}
And here are all the logs that are being printed:
2020-06-07 05:20:25.566 23822-23822/? E/libc: Access denied finding property "persist.vendor.sys.activitylog"
2020-06-07 05:20:26.468 23822-23822/com.example.mybeaconprojectaye E/xoxo: result: 0
2020-06-07 05:20:26.810 23822-23822/com.example.mybeaconprojectaye E/xoxo: Service connected
2020-06-07 05:20:26.848 23822-23822/com.example.mybeaconprojectaye E/xoxo: didDetermineStateForRegion state: 0
Can anyone please tell me what i am doing wrong or if some step is missing. I tried looking into other answers on stackoverflow, but this is the only library where i am seeing new classes and interfaces everywhere in all answers. Too much confusion.
P.S.
I am trying to make contact tracing app. Any other library or something you can suggest will also be appreciated.

Try reversing phones 1 and 2 for your tests. If you use BeaconScope to scan, can it see the transmissions of both phones 1&2? If you use BeaconScope to transmit, can your app on either phone 1 or 2 see the beacon tranamission?
If you cannot detect beacon scope on one or both phones, check app permissions to confirm that location permission has been granted to your app. Go to Settings -> Applications -> Your App and check the granted permissions.
Also check that location is enabled globally on the phone and that Bluetooth is on.

Related

Upload recorded file to AWS S3 storage or google drive

I have created an app to record the audio in the android wear but I am not able to transfer that recorded file to mobile phone as there is no dedicated file manager in Samsung galaxy watch 4. So for that I need to upload the the recorded file to the AWS S3 STORAGE.
Here is the code for audio recording.
package com.example.watch
import android.Manifest.permission
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Intent
import android.content.pm.PackageManager
import android.media.MediaRecorder
import android.net.Uri
import android.os.*
import android.provider.MediaStore
import android.util.Log
import android.view.WindowManager
import android.widget.Button
import android.widget.Chronometer
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.amplifyframework.core.Amplify
import com.amplifyframework.storage.StorageException
import com.amplifyframework.storage.result.StorageUploadFileResult
import com.example.watch.databinding.ActivityMicrophoneBinding
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.*
#Suppress("DEPRECATION")
class Microphone : AppCompatActivity() {
// Initializing all variables..
private var Stop: Button? = null
private var Start: Button? = null
private var statusTV:TextView? = null
private var Chronometer:TextView?=null
var fileName: String? = null
var audioRecorder: MediaRecorder? = null
var audiouri: Uri? = null
var file: ParcelFileDescriptor? = null
lateinit var status:TextView
private var binding: ActivityMicrophoneBinding? = null
private lateinit var chronometer: Chronometer
private var audioFile: File? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_microphone)
if (!CheckPermissions())
RequestPermissions()
AmplifyInit().intializeAmplify(this#Microphone)
this.window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
)
supportActionBar!!.hide()
// initialize all variables with their layout items.
Start=binding!!.accelerometerStartButton
Stop=binding!!.accelerometerStopButton
status=binding!!.Status
chronometer=binding!!.chronometer
Start!!.setOnClickListener { // start recording method will
// start the recording of audio.
startRecording()
Start!!.isEnabled=false
status.text="Recording..."
chronometer.base = SystemClock.elapsedRealtime()
chronometer.start()
startService()
}
Stop!!.setOnClickListener { // pause Recording method will
// pause the recording of audio.
pauseRecording()
Start!!.isEnabled=true
status.text="Recording Stopped"
chronometer.stop()
uploadFile()
stopService()
}
}
private fun startRecording() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
val values = ContentValues(4)
values.put(MediaStore.Audio.Media.TITLE, fileName)
values.put(
MediaStore.Audio.Media.DATE_ADDED,
SimpleDateFormat("yyyyMMddHHmmss").format(Date())
)
values.put(MediaStore.Audio.Media.RELATIVE_PATH, "Music/Recordings/")
audiouri = contentResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values)
file = contentResolver.openFileDescriptor(audiouri!!, "w")
if (file != null) {
audioRecorder = MediaRecorder()
audioRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
audioRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
audioRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
audioRecorder!!.setOutputFile(file!!.fileDescriptor)
audioRecorder!!.setAudioChannels(1)
audioRecorder!!.prepare()
audioRecorder!!.start()
audioRecorder!!.setOutputFile(getAudioFile().absolutePath)
}
}
else {
RequestPermissions()
}
}
private fun getAudioFile(): File {
if (audioFile == null) {
audioFile = File(getExternalFilesDir(null), "Music/Recordings/")
}
return audioFile!!
}
#SuppressLint("MissingSuperCall")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray,
) {
// this method is called when user will
// grant the permission for audio recording.
when (requestCode) {
REQUEST_AUDIO_PERMISSION_CODE -> {
if (grantResults.isNotEmpty()) {
val permissionToRecord = grantResults[0] == PackageManager.PERMISSION_GRANTED
val permissionToStore = grantResults[1] == PackageManager.PERMISSION_GRANTED
if (permissionToRecord && permissionToStore) {
Toast.makeText(applicationContext, "Permission Granted", Toast.LENGTH_LONG)
.show()
} else {
Toast.makeText(applicationContext, "Permission Denied", Toast.LENGTH_LONG)
.show()
}
}
}
}
}
private fun CheckPermissions(): Boolean {
// this method is used to check permission
val result = ContextCompat.checkSelfPermission(applicationContext, permission.WRITE_EXTERNAL_STORAGE)
val result1 = ContextCompat.checkSelfPermission(applicationContext, permission.RECORD_AUDIO)
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED
}
private fun RequestPermissions() {
// this method is used to request the
// permission for audio recording and storage.
ActivityCompat.requestPermissions(this,
arrayOf(permission.RECORD_AUDIO, permission.WRITE_EXTERNAL_STORAGE),
REQUEST_AUDIO_PERMISSION_CODE
)
}
private fun pauseRecording() {
Start!!.isEnabled=true
stopService(Intent(this, ForegroundService::class.java))
// below method will stop
// the audio recording.
try {
audioRecorder!!.stop()
} catch (stopException: RuntimeException) {
// handle cleanup here
}
}
fun startService() {
val serviceIntent = Intent(this, ForegroundService::class.java)
serviceIntent.putExtra("inputExtra", "Foreground Service Example in Android")
ContextCompat.startForegroundService(this, serviceIntent)
}
fun stopService() {
val serviceIntent = Intent(this, ForegroundService::class.java)
stopService(serviceIntent)
}
companion object {
// string variable is created for storing a file name
private var mFileName: String? = null
var permissionAccepted = false
var permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO,
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
// constant for storing audio permission
const val REQUEST_AUDIO_PERMISSION_CODE = 1000
}
private fun uploadFile() {
Amplify.Storage.uploadFile(
"Audio/audio.mp3",
getAudioFile(),
{ result: StorageUploadFileResult ->
Log.i(
"MyAmplifyApp",
"Successfully uploaded: " + result.key
)
Toast.makeText(this, "File has Successfully Uploaded:" , Toast.LENGTH_SHORT).show()
}
) { storageFailure: StorageException? ->
Log.e(
"MyAmplifyApp",
"Upload failed",
storageFailure
)
Toast.makeText(this, "Upload failed", Toast.LENGTH_SHORT).show()
}
}
}
Now how can i modify my uploadfile function to upload the fiole to AWS S3, As i am getting this error.
E/amplify:aws-s3-storage:SinglePartUploadWorker: SinglePartUploadWorker failed with exception: kotlinx.coroutines.JobCancellationException: Parent job is Cancelling; job=JobImpl{Cancelled}#9b17179
I/WM-WorkerWrapper: Worker result RETRY for Work [ id=48cd3651-35e2-456e-9329-81930f5277a5, tags={ UPLOAD, awsS3StoragePlugin, 9, com.amplifyframework.storage.s3.transfer.worker.RouterWorker } ]
I am using dependecies:
implementation 'com.amplifyframework:aws-api:2.1.0'
implementation 'com.amplifyframework:aws-datastore:2.1.0'
implementation 'com.amplifyframework:aws-storage-s3:2.1.0'
implementation 'com.amplifyframework:aws-auth-cognito:2.1.0'
Library:
<uses-library
android:name="com.google.android.wearable"
android:required="true" />

Kotlin Android Full Sized Camera instead of Thumbnail?

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()
}
}
}

"<App> is having trouble with Google Play Services. Please try again" while usiing Google NearBy Messages API

While using google NearBy Messages API I am getting error " is having trouble with Google Play Services. Please try again"
Please guide me for this issue.
Below is import for Google Messaging API
implementation 'com.google.android.gms:play-services-nearby:17.0.0'
Here is how I am subscribing using code
val options = SubscribeOptions.Builder()
.setStrategy(Strategy.BLE_ONLY)
.build()
Nearby.getMessagesClient(
this, MessagesOptions.Builder()
.setPermissions(NearbyPermissions.BLE)
.build())
Nearby.getMessagesClient(this).subscribe(getPendingIntent(), options)
I resolved it.
Nearby suggest using activity, on activty, the function will work better (https://developers.google.com/android/reference/com/google/android/gms/nearby/messages/MessagesClient#subscribe(android.app.PendingIntent,%20com.google.android.gms.nearby.messages.SubscribeOptions))
All of the Messages APIs should be used from a foreground Activity,
with the exception of the variants of subscribe that take a
PendingIntent parameter. Your Activity should publish(Message) or
subscribe(MessageListener) either in onStart() or in response to a
user action in a visible Activity, and you should always symmetrically
unpublish(Message) or unsubscribe(MessageListener) in onStop().
When subcribe, if using activity, it will ask to grant permission to bluetooth, location, microphone, if using service it will not ask
So if you use the service, you must combine using the activity.
When you subscribe in mainActivity, if another activity appears on top (then MainActivty will be onStop), a notification will appear.
Therefore, when subcribe, you must click OK to allow the another activity to be displayed
This is sample:
MainActivity.tk
private val mMessageListener: MessageListener = object : MessageListener() {
override fun onFound(message: Message) {
Log.d(TAG, "onFound message:"+ String(message.content))
}
override fun onLost(message: Message) {
Log.d(TAG, "Lost sight of message: " + String(message.content))
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val sharedPref: SharedPreferences = getSharedPreferences("MyPref", Context.MODE_PRIVATE)
val isFirstTime = sharedPref.getBoolean("FIRST_TIME", true)
if(isFirstTime) {
Nearby.getMessagesClient(this).subscribe(mMessageListener).addOnCompleteListener(this, OnCompleteListener {
requestPermissionFirstTime()
}).addOnCanceledListener(this, OnCanceledListener {
requestPermissionFirstTime()
})
} else {
requestPermissionCapture()
checkPermissionAccessibility()
startService(Intent(this, NearbyMessageService::class.java))
}
}
private fun requestPermissionFirstTime() {
val sharedPref: SharedPreferences = getSharedPreferences(Utils.IAMHERE_PREF, Context.MODE_PRIVATE)
val editor = sharedPref.edit()
editor.putBoolean("FIRST_TIME", false)
editor.apply()
Nearby.getMessagesClient(this).unsubscribe(mMessageListener)
requestPermissionCapture()
checkPermissionAccessibility()
}
NearbyMessageService.tk
class NearbyMessageService: IntentService("NearbyMessageService") {
private val mMessageListener: MessageListener = object : MessageListener() {
override fun onFound(message: Message) {
Log.d(TAG, "onFound message:"+ String(message.content))
}
override fun onLost(message: Message) {
Log.d(TAG, "Lost sight of message: " + String(message.content))
}
}
override fun onCreate() {
super.onCreate()
startForeground()
Nearby.getMessagesClient(this).subscribe(mMessageListener)
}
private fun startForeground() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "002"
val channelName = "Nearby Service Channel"
val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE)
channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
val notification: Notification = Notification.Builder(applicationContext, channelId)
.setOngoing(true)
.setCategory(Notification.CATEGORY_SERVICE)
.setContentTitle(getString(R.string.app_name))
.build()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(Utils.NOTICATION_ID_NEARBY, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION)
} else {
startForeground(Utils.NOTICATION_ID_NEARBY, notification)
}
} else {
startForeground(Utils.NOTICATION_ID_NEARBY, Notification())
}
}
}

Android app return 0 devices after scan for ble

For the past months I have been developing an app which a feature of it implements BLE connection from the app to a device. Suddenly today, after a few scans, the app returns everytime 0 device despite obtaining before like 5 or 6.
I have been thinking that it was a permission problem related with coarse location, however, the changes I made it was so little that it should not be affected, but that does not matter
As I followed the bluetooth guide that Google offers amongst other guides, this is my code:
onCreate method:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_guide)
handler = Handler()
devicesResult = ArrayList()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
// Android M Permission check
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
val builder = AlertDialog.Builder(this)
builder.setTitle("This app needs location access")
builder.setMessage("Please grant location access so this app can detect beacons.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener { requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), PERMISSION_REQUEST_COARSE_LOCATION) }
builder.show()
}else{
if (areLocationServicesEnabled(this))
{
if (!packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))
{
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show()
finish()
}
bluetoothAdapter?.takeIf { it.isDisabled }?.apply {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, 1)
}
//scanLeDevice(true)
}
}
}
}
onRequestPermissionsResult method:
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>, grantResults: IntArray
) {
when (requestCode) {
PERMISSION_REQUEST_COARSE_LOCATION -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//Log.d(FragmentActivity.TAG, "coarse location permission granted")
} else {
val builder = AlertDialog.Builder(this)
builder.setTitle("Functionality limited")
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener { }
builder.show()
}
return
}
}
}
areLocationServicesEnabled method:
private fun areLocationServicesEnabled(context:Context):Boolean {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
return try {
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} catch (e:Exception) {
e.printStackTrace()
false
}
}
onResume method:
override fun onResume() {
super.onResume()
if (bluetoothAdapter != null && !bluetoothAdapter!!.isEnabled)
{
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, 1)
}
mLEScanner = bluetoothAdapter!!.bluetoothLeScanner
scanLeDevice(true)
}
scanLeDevice method:
private fun scanLeDevice(enable:Boolean) {
if (enable)
{
handler!!.postDelayed({
mLEScanner!!.stopScan(mScanCallback)
//selectDevice()
}, SCAN_PERIOD)
mLEScanner!!.startScan(mScanCallback)
}
else
{
mLEScanner!!.stopScan(mScanCallback)
}
}
mScanCallback attribute:
private val mScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
Log.i("callbackType", callbackType.toString())
Log.i("result", result.toString())
devicesResult!!.add(result)
}
override fun onBatchScanResults(results: List<ScanResult>) {
for (sr in results) {
Log.i("ScanResult - Results", sr.toString())
}
}
override fun onScanFailed(errorCode: Int) {
Log.e("Scan Failed", "Error Code: $errorCode")
}
}
As I said before, I expected to show and save on an Array called deviceResult every one of them, however, suddenly, no devices seems to appear after each new scan.
First of all, sorry for the big delay to answer muy question.
After trying not to use the scan more than 6 times between 30 seconds as #Emil pointed out on the last comment fo my question, I finally could make my project work, thanks #Emil.

RxAndroidBLE do not find devices on android 4.3

Hello I'm using RxAndroidBLE to detect a BLE device. On android 6 >= everything seems to work okay but not on a 4.3 device.
My app can only discover the desirable BLE device only once at start. After the device has been discovered no more new discoveries at all until I restart the app. Any advice would be highly appreciated.
Below minimum (not)working code example:
MainActivity
import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.util.Log
import com.polidea.rxandroidble.RxBleClient
import com.polidea.rxandroidble.exceptions.BleScanException
import com.polidea.rxandroidble.scan.ScanResult
import com.polidea.rxandroidble.scan.ScanSettings
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startLeScan(applicationContext)
}
private var rxBleClient: RxBleClient? = null
private var scanSubscription: Subscription? = null
private var handler: Handler? = null
private var timer: Timer? = null
private var timerTask: TimerTask? = null
private var delay: Int = 0
private fun isScanning(): Boolean {
return scanSubscription != null
}
fun startLeScan(context: Context) {
rxBleClient = MyaPP.getRxBleClient(context)
if (isScanning()) {
scanSubscription?.unsubscribe()
} else {
scanSubscription = rxBleClient?.scanBleDevices(
com.polidea.rxandroidble.scan.ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build())
?.observeOn(AndroidSchedulers.mainThread())
//?.doOnNext(this::newDevicesFound)
?.doOnUnsubscribe(this::clearSubscription)
?.subscribe(this::newDevicesFound, this::onScanFailure)
}
if(handler == null) {
handler = Handler()
timer = Timer(false)
timerTask = object : TimerTask() {
override fun run() {
handler?.post {
if (delay > 7) {
delay = 0
val service = Executors.newSingleThreadExecutor()
service.submit(Runnable {
//startLeScan(context)
})
} else {
delay = delay + 1
}
}
}
}
timer?.scheduleAtFixedRate(timerTask, 0, 300)
}
}
private fun newDevicesFound(devices: ScanResult) {
Log.d("WHYY??", devices.bleDevice.name)
}
fun stopScan() {
scanSubscription?.unsubscribe()
destroy()
}
private fun clearSubscription() {
scanSubscription = null
}
private fun onScanFailure(throwable: Throwable) {
if (throwable is BleScanException) {
handleBleScanException(throwable)
}
}
private fun handleBleScanException(bleScanException: BleScanException) {
val text: String
when (bleScanException.reason) {
BleScanException.BLUETOOTH_NOT_AVAILABLE -> text = "Bluetooth is not available"
BleScanException.BLUETOOTH_DISABLED -> text = "Enable bluetooth and try again"
BleScanException.LOCATION_PERMISSION_MISSING -> text = "On Android 6.0 location permission is required. Implement Runtime Permissions"
BleScanException.LOCATION_SERVICES_DISABLED -> text = "Location services needs to be enabled on Android 6.0"
BleScanException.SCAN_FAILED_ALREADY_STARTED -> text = "Scan with the same filters is already started"
BleScanException.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED -> text = "Failed to register application for bluetooth scan"
BleScanException.SCAN_FAILED_FEATURE_UNSUPPORTED -> text = "Scan with specified parameters is not supported"
BleScanException.SCAN_FAILED_INTERNAL_ERROR -> text = "Scan failed due to internal error"
BleScanException.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES -> text = "Scan cannot start due to limited hardware resources"
BleScanException.UNDOCUMENTED_SCAN_THROTTLE -> text = String.format(
Locale.getDefault(),
"Android 7+ does not allow more scans. Try in %d seconds",
secondsTill(bleScanException.retryDateSuggestion)
)
BleScanException.UNKNOWN_ERROR_CODE, BleScanException.BLUETOOTH_CANNOT_START -> text = "Unable to start scanning"
else -> text = "Unable to start scanning"
}
Log.w("EXCEPTION", text, bleScanException)
}
private fun secondsTill(retryDateSuggestion: Date?): Long {
if (retryDateSuggestion != null) {
return TimeUnit.MILLISECONDS.toSeconds(retryDateSuggestion.time - System.currentTimeMillis())
}
return 0
}
private fun destroy() {
timer?.cancel()
handler?.removeCallbacks(timerTask)
handler = null
timerTask = null
timer = null
}
}
MyaPP
import android.app.Application
import android.content.Context
import com.polidea.rxandroidble.RxBleClient
import com.polidea.rxandroidble.internal.RxBleLog
class MyaPP: Application() {
private var rxBleClient: RxBleClient? = null
companion object {
fun getRxBleClient(context: Context): RxBleClient? {
val application = context.applicationContext as MyaPP
return application.rxBleClient
}
}
override fun onCreate() {
super.onCreate()
rxBleClient = RxBleClient.create(this)
RxBleClient.setLogLevel(RxBleLog.DEBUG)
}
}
build.gradle
compile "com.polidea.rxandroidble:rxandroidble:1.5.0"
implementation 'io.reactivex:rxandroid:1.2.1'
manifest
<application
android:name=".MyaPP"
Your code looks a lot like the library's sample app (version 1.5.0, branch master-rxjava1). I have checked that recently on Android 4.4.4 which is the oldest I have and it worked fine. There were no API changes between 4.3 and 4.4.
What you may be experiencing is a behaviour specific to your device (feel free to share your phone model) in which it only callbacks for the first time it scans a particular peripheral. There are some threads about this topic already like this one.

Categories

Resources