I have an activity, that is trigered by an event and I use this code, to be able to display it while my device is locked.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
setTurnScreenOn(true)
val pm=getSystemService(Context.POWER_SERVICE)as PowerManager
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"FireApp:test2").acquire(1000000)
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this, null)
}else{
this.window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
}
It is working fine with my device being locked, until i want to display the LocationSettingsRequest dialog using this code:
fun displayLocationSettingsRequest(context: Context) {
val googleApiClient = GoogleApiClient.Builder(context)
.addApi(LocationServices.API).build()
googleApiClient.connect()
val locationRequest = LocationRequest.create()
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
locationRequest.setInterval(5000)
locationRequest.setFastestInterval(2000)
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
builder.setAlwaysShow(true)
val result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build())
result.setResultCallback (object: ResultCallback<LocationSettingsResult> {
override fun onResult(result: LocationSettingsResult) {
val status = result.status
if (status.statusCode == LocationSettingsStatusCodes.SUCCESS) {
Log.d("Martin", "Lokacija je vključena!")
}
if (status.statusCode == LocationSettingsStatusCodes.RESOLUTION_REQUIRED) {
try {
status.startResolutionForResult(this#medAlarmom2, 0x1)
} catch (e: IntentSender.SendIntentException) {
Log.d("Martin", "PendingIntent unable to execute request.")
}
}
if (status.statusCode == LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE) Log.d("Martin", "Location settings are inadequate, and cannot be fixed here. Dialog "+"not created.")
}
})
}
I can't find a good solution, to be able to display this dialog without unlocking the device. Anyone has a solution for this one?
Thanks a milion!
Martin
Related
GPS service is not enabled on Android versions 12 and above.
Everything worked correctly on earlier versions
I have read this documentation,
but I'm not sure if I have a problem with android:foregroundServiceType since I basically don't use it in the application.
So here is some of my code AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
And code of my vm:
private fun enableGps(activity: FragmentActivity) {
val manager = activity.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && hasGPSDevice(manager)) {
locationManager.enableGps(activity)
}
}
And here is the implementation of the method itself:
override fun enableGps(activity: FragmentActivity) {
if (googleApiClient == null) {
googleApiClient = GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks {
override fun onConnected(bundle: Bundle?) {}
override fun onConnectionSuspended(i: Int) {
googleApiClient?.connect()
}
})
.addOnConnectionFailedListener { connectionResult ->
Timber.e("${connectionResult.errorCode}")
}.build()
googleApiClient?.connect()
}
val locationRequest = LocationRequest.create()
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationRequest.interval = 10000
locationRequest.fastestInterval = 5000
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
builder.setAlwaysShow(true)
googleApiClient?.let {
val result: PendingResult<LocationSettingsResult> =
LocationServices.SettingsApi.checkLocationSettings(it, builder.build())
result.setResultCallback { result ->
val status: Status = result.status
when (status.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
// Show the dialog by calling startResolutionForResult(),
status.startResolutionForResult(
activity,
REQUEST_LOCATION
)
} catch (e: IntentSender.SendIntentException) {
Timber.e(e)
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
activity.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
}
}
}
}
}
UPD
Here is full code of my DeviceLocationManager, maybe I 'm requesting my permissions somehow wrong here:
https://gist.github.com/mnewlive/0c0a0c1f7ccb26fe58fd6b0fa5dd1dda
I rewrote the following method because it was deprecated:
override fun isLocationProviderActive(): Boolean {
val providerInfo = Settings.Secure.getString(
context.contentResolver,
Settings.Secure.LOCATION_PROVIDERS_ALLOWED
)
return providerInfo?.isNotEmpty() == true
}
to
override fun isLocationStateEnabled(): Boolean {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
return LocationManagerCompat.isLocationEnabled(locationManager)
}
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.
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())
}
}
}
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.
I'm trying to implement iOS callkit behavior on Android. I'm receiving a push notification from firebase and I want to show "incoming call" screen to the user. To do it I use ConnectionService from android.telecom package and other classes.
Here is my call manager class:
class CallManager(context: Context) {
val telecomManager: TelecomManager
var phoneAccountHandle:PhoneAccountHandle
var context:Context
val number = "3924823202"
init {
telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
this.context = context
val componentName = ComponentName(this.context, CallConnectionService::class.java)
phoneAccountHandle = PhoneAccountHandle(componentName, "Admin")
val phoneAccount = PhoneAccount.builder(phoneAccountHandle, "Admin").setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED).build()
telecomManager.registerPhoneAccount(phoneAccount)
val intent = Intent()
intent.component = ComponentName("com.android.server.telecom", "com.android.server.telecom.settings.EnableAccountPreferenceActivity")
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
}
#TargetApi(Build.VERSION_CODES.M)
fun startOutgoingCall() {
val extras = Bundle()
extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true)
val manager = context.getSystemService(TELECOM_SERVICE) as TelecomManager
val phoneAccountHandle = PhoneAccountHandle(ComponentName(context.packageName, CallConnectionService::class.java!!.getName()), "estosConnectionServiceId")
val test = Bundle()
test.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
test.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_BIDIRECTIONAL)
test.putParcelable(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras)
try {
manager.placeCall(Uri.parse("tel:$number"), test)
} catch (e:SecurityException){
e.printStackTrace()
}
}
#TargetApi(Build.VERSION_CODES.M)
fun startIncomingCall(){
if (this.context.checkSelfPermission(Manifest.permission.MANAGE_OWN_CALLS) == PackageManager.PERMISSION_GRANTED) {
val extras = Bundle()
val uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null)
extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri)
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true)
val isCallPermitted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
telecomManager.isIncomingCallPermitted(phoneAccountHandle)
} else {
true
}
Log.i("CallManager", "is incoming call permited = $isCallPermitted")
telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
}
}
}
And my custom ConnectionService implementation:
class CallConnectionService : ConnectionService() {
override fun onCreateOutgoingConnection(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?): Connection {
Log.i("CallConnectionService", "onCreateOutgoingConnection")
val conn = CallConnection(applicationContext)
conn.setAddress(request!!.address, PRESENTATION_ALLOWED)
conn.setInitializing()
conn.videoProvider = MyVideoProvider()
conn.setActive()
return conn
}
override fun onCreateOutgoingConnectionFailed(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?) {
super.onCreateOutgoingConnectionFailed(connectionManagerPhoneAccount, request)
Log.i("CallConnectionService", "create outgoing call failed")
}
override fun onCreateIncomingConnection(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?): Connection {
Log.i("CallConnectionService", "onCreateIncomingConnection")
val conn = CallConnection(applicationContext)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
conn.connectionProperties = Connection.PROPERTY_SELF_MANAGED
}
conn.setCallerDisplayName("test call", TelecomManager.PRESENTATION_ALLOWED)
conn.setAddress(request!!.address, PRESENTATION_ALLOWED)
conn.setInitializing()
conn.videoProvider = MyVideoProvider()
conn.setActive()
return conn
}
override fun onCreateIncomingConnectionFailed(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?) {
super.onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, request)
Log.i("CallConnectionService", "create outgoing call failed ")
}
}
And my Connection implementation is like that:
class CallConnection(ctx:Context) : Connection() {
var ctx:Context = ctx
val TAG = "CallConnection"
override fun onShowIncomingCallUi() {
// super.onShowIncomingCallUi()
Log.i(TAG, "onShowIncomingCallUi")
val intent = Intent(Intent.ACTION_MAIN, null)
intent.flags = Intent.FLAG_ACTIVITY_NO_USER_ACTION or Intent.FLAG_ACTIVITY_NEW_TASK
intent.setClass(ctx, IncomingCallActivity::class.java!!)
val pendingIntent = PendingIntent.getActivity(ctx, 1, intent, 0)
val builder = Notification.Builder(ctx)
builder.setOngoing(true)
builder.setPriority(Notification.PRIORITY_HIGH)
// Set notification content intent to take user to fullscreen UI if user taps on the
// notification body.
builder.setContentIntent(pendingIntent)
// Set full screen intent to trigger display of the fullscreen UI when the notification
// manager deems it appropriate.
builder.setFullScreenIntent(pendingIntent, true)
// Setup notification content.
builder.setSmallIcon(R.mipmap.ic_launcher)
builder.setContentTitle("Your notification title")
builder.setContentText("Your notification content.")
// Use builder.addAction(..) to add buttons to answer or reject the call.
val notificationManager = ctx.getSystemService(
NotificationManager::class.java)
notificationManager.notify("Call Notification", 37, builder.build())
}
override fun onCallAudioStateChanged(state: CallAudioState?) {
Log.i(TAG, "onCallAudioStateChanged")
}
override fun onAnswer() {
Log.i(TAG, "onAnswer")
}
override fun onDisconnect() {
Log.i(TAG, "onDisconnect")
}
override fun onHold() {
Log.i(TAG, "onHold")
}
override fun onUnhold() {
Log.i(TAG, "onUnhold")
}
override fun onReject() {
Log.i(TAG, "onReject")
}
}
According to the document to show user incoming calcustomon UI - I should do some actions in onShowIncomingCallUi() method. But it just does not called by the system.
How can I fix it?
I was able to get it to work using a test app and Android Pie running on a Pixel 2 XL.
From my testing the important parts are to ensure:
That Connection.PROPERTY_SELF_MANAGED is set on the connection. Requires a minimum of API 26.
You have to register your phone account.
You have to set PhoneAccount.CAPABILITY_SELF_MANAGED in your capabilities when registering the phone account. That is the only capability that I set. Setting other capabilities caused it to throw an exception.
Finally, you need to ensure that you have this permission set in AndroidManifest.xml. android.permission.MANAGE_OWN_CALLS
So, I would check your manifest to ensure you have the permissions and also ensure the capabilities are set correctly. It looks like everything else was set correctly in your code above.
Hope that helps!