Getting fenceKey null in Awareness Api - android

Hi i´m working in app that have to notificate when the user starts driving, i used Neura Api but it needs a fixed notification, so i´m trying it with Awareness Api.
I need the broadcast in the AndroidManifest.xml because i want to trigger the event even the app is not in background.
The Fence is registered fine, the broadcast is triggered but i can´t get the fenceKey and fenceStatus , i´m trying with different events for test.
In the AndroidManifest.xml i added permissions, api key and declared the broadcast.
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<receiver
android:name=".usescase.receivers.FenceReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.safycab.safycab.FENCE_RECEIVER_ACTION" />
</intent-filter>
</receiver>
<meta-data
android:name="com.google.android.awareness.API_KEY"
android:value="#string/awareness_key" />
This is my FenceReceiver.kt, here is the problem when i got the event of headphones fence i try to get the fenceKey and fenceStatus but i got fenceKey = null and fenceStatus = 0
class FenceReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val fenceState = FenceState.extract(intent)
context.showLocalNotification("fence key " + fenceState.fenceKey + " fence state" + fenceState.currentState)
}
}
Here is where i register the Fences, here all permissions are checked and accepted, the register is working good
class FenceApiUtils(var activity: BaseActivity<*, *>) {
var drivingFence = DetectedActivityFence.starting(DetectedActivityFence.IN_VEHICLE)
var walkingFence = DetectedActivityFence.starting(DetectedActivityFence.ON_FOOT)
val headPhoneFence = HeadphoneFence.during(HeadphoneState.PLUGGED_IN)
fun createFences() {
val intent = Intent(activity, FenceReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
activity.applicationContext, 0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
Awareness.getFenceClient(activity).updateFences(
FenceUpdateRequest.Builder()
.addFence(VEHICLE_FENCE_KEY, drivingFence, pendingIntent)
.addFence(WALKING_FENCE_KEY, walkingFence, pendingIntent)
.addFence(HEADPHONE_FENCE, headPhoneFence, pendingIntent)
.build()
).addOnSuccessListener {
log("Fence was successfully registered.")
}.addOnFailureListener {
log("Fence could not be registered: ${it.message}")
}
}
}
If i do this method i can check that the fence is correctly registered
fun queryFence(key: String) {
Awareness.getFenceClient(requireActivity())
.queryFences(FenceQueryRequest.forFences(listOf(key))).addOnSuccessListener {
val map = it.fenceStateMap
for (fenceKey in map.fenceKeys) {
val fenceState = map.getFenceState(fenceKey)
requireContext().showLocalNotification(
"Fence " + fenceKey + ": "
+ fenceState?.currentState
+ ", was="
+ fenceState?.previousState
)
}
}.addOnFailureListener {
log(it.message)
}
}
And if i do this i got the user activity correctly
Awareness.getSnapshotClient(requireActivity()).detectedActivity.addOnSuccessListener {
val act = it.activityRecognitionResult
val dtc = act.mostProbableActivity
val conf = dtc.confidence
val activityStr = dtc.toString()
requireContext().showLocalNotification("Activity: $activityStr, Confidence: $conf/100")
}.addOnFailureListener {
log(it.message)
log(it.localizedMessage)
}

Change the pending intent flag from FLAG_IMMUTABLE to FLAG_MUTABLE:
val pendingIntent = PendingIntent.getBroadcast(
activity.applicationContext, 0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
Unfortunately the documentation doesn't show how to create the mPendingIntent in https://developers.google.com/awareness/android-api/fence-register, but for me this did the trick.

Related

Android Alarm Manager doesn't fire alarm when application is closed

guys, am currently building an application with compose, and i need to fire a notification at a given time to users, so whenever users click on a button on my list item, i want to get the time of that item and set an alarm pending intent for it, and fire it when the times reaches,
So am working with android alarm manager, but whenever i close my application the alarm doesn't broadcast my notification, but if i leave it open, it fires the alarm notification successfully, i dont know what i am doing wrong cause from what i know my application isnt responsible for firing the alarm, cause the android system does this for us,
so here is my code that i have tried with Alarm Manager
Funtion to Schedule Notification
`
#RequiresApi(api = Build.VERSION_CODES.M)
fun scheduleNotification(calendar: Calendar, context: Context, taskInfo:FixtureAlarm) {
val alarmManager = context.getSystemService(ALARM_SERVICE) as AlarmManager
// adding intent and pending intent to go to AlarmReceiver Class in future
val intent = Intent(context, FixtureNotificationReceiver::class.java)
intent.putExtra("fixture", taskInfo)
val pendingIntent = PendingIntent.getBroadcast(context, taskInfo.id, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
// when using setAlarmClock() it displays a notification until alarm rings and when pressed it takes us to mainActivity
val mainActivityIntent = Intent(context, MainActivity::class.java)
val basicPendingIntent = PendingIntent.getActivity(context, taskInfo.id, mainActivityIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
// creating clockInfo instance
val clockInfo = AlarmManager.AlarmClockInfo(Calendar.getInstance().also { it.add(Calendar.SECOND,10) }.timeInMillis, basicPendingIntent)
// setting the alarm
alarmManager.setExact(AlarmManager.RTC_WAKEUP,Calendar.getInstance().also { it.add(Calendar.SECOND,10) }.timeInMillis, pendingIntent)
Toast.makeText(context, "Scheduled ", Toast.LENGTH_LONG).show()
}
`
My BroadCast Receiver
class FixtureNotificationReceiver() : BroadcastReceiver() {
private var notificationManager: NotificationManagerCompat? = null
override fun onReceive(p0: Context?, p1: Intent?) {
val taskInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
p1?.getParcelableExtra("fixture",FixtureAlarm::class.java)
} else {
p1?.getParcelableExtra("fixture") as? FixtureAlarm
}
Log.d("SVTRECIEVED","ALARTM RECIEVED ${taskInfo?.homeTeam.toString()}")
// tapResultIntent gets executed when user taps the notification
if(taskInfo!=null) {
val tapResultIntent = Intent(p0, MainActivity::class.java)
tapResultIntent.putExtra("fixture", taskInfo)
tapResultIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
val pendingIntent: PendingIntent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getActivity(p0, 0, tapResultIntent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
} else {
getActivity(p0, 0, tapResultIntent, PendingIntent.FLAG_UPDATE_CURRENT)
}
val notification = p0?.let {
NotificationCompat.Builder(it, FIXTURESCHANNEL_ID)
.setContentTitle("Fixture Reminder")
.setContentText("${taskInfo?.awayTeam} VS ${taskInfo.homeTeam} is about to start, open SportVectru and get live updates ")
.setSmallIcon(R.mipmap.ic_launcher)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setDefaults(NotificationCompat.DEFAULT_SOUND)
.setDefaults(NotificationCompat.DEFAULT_VIBRATE)
.setPriority(NotificationCompat.PRIORITY_HIGH).build()}
val notificationtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
val r = RingtoneManager.getRingtone(p0, notificationtone)
r.play()
notificationManager = p0?.let { NotificationManagerCompat.from(it) }
notification?.let { taskInfo.let { it1 -> if (ActivityCompat.checkSelfPermission(
p0,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
Toast.makeText(p0, "Permission for showing notification is disabled", Toast.LENGTH_SHORT).show()
return
}
notificationManager?.notify(it1.id, it)
}
}
}
}
}
Added it to my Manifest
`
<receiver android:name=".dormain.notifications.pendingNotification.FixtureNotificationReceiver"
android:enabled="true"
/>
`
I even granted Permission
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
But it still doesnt work, notification is never shown when application is closed.
I have also disabled battery Optimization for my application, doesnt work.

list unique notifications in android

I am creating an app that will have permission to read user notifications.
So I have implemented NotificationListenerService as shown below
class NotifListener : NotificationListenerService() {
override fun onNotificationPosted(sbn: StatusBarNotification?) {
var context = this
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
var packageName = sbn?.packageName ?: ""
var notifId=sbn?.id?:0; //doesn't seem to be unique
val notification: Notification = sbn!!.notification
var extras: Bundle? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
extras = notification.extras
for (key in extras!!.keySet()) {
var title: String = extras.get("android.title").toString()
var text: String = extras.get("android.text").toString()
CoroutineScope(Dispatchers.IO).launch {
NotificationDBBuilder.getInstance(context).notificationDao()
.insertNotification(
NotificationEntity(notifId, title, text,packageName,Date().time)
)
}
}
}
extras?.get("android.title")
// Log.d(TAG, "onNotificationPosted: " + extras?.get("android.title")+" "+extras?.get("android.text")+" "+extras?.get("android.infoText"))
}
}
}
and I have added it in manifest
<service android:name=".services.NotifListener"
android:label="Notif"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
Now when I logged the notifications that I received, it seems that the onNotificaitionPosted is being called multiple times for the same notification for some applications or some situations. I want to list only Unique notifications. Which parameter should I Use from the notifications as a Unique Key?
Or is there some way to prevent multiple calls for the same notification?
or is it possible that the listener is getting registered multiple times?

Android Wear Geofence calling addGeofences-method returns Exception: 1000

I am playing around with Wear OS on a Fossil Falster 3 (it is running API 28). Also please know that I am just starting with Kotlin, so the code below is not good to say the least.
I want to create a Geofence using broadcast receiver.
In my MainActivity.kt I have the following:
override fun onStart() {
super.onStart()
requestForegroundAndBackgroundLocationPermissions()
val locations = arrayOf(Location("tgh", 58.798233, 11.123959))
val geofences = locations.map {
Geofence.Builder()
.setRequestId(it.name)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setCircularRegion(it.latitude, it.longitude, 100F)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_DWELL)
.setLoiteringDelay(10)
.build()
}
val geofencingRequest = GeofencingRequest.Builder().apply {
setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
addGeofences(geofences)
}.build()
val geofencePendingIntent: PendingIntent by lazy {
val intent = Intent(this, GeofenceReceiver::class.java)
PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
val geofencingClient = LocationServices.getGeofencingClient(this)
geofencingClient.addGeofences(geofencingRequest, geofencePendingIntent).run {
addOnFailureListener {
// display error
exception -> Log.d(TAG, "addOnFailureListener Exception: $exception")
}
addOnSuccessListener {
// move on
Log.d(TAG, "addOnSuccessListener, move on")
}
}
}
The GeofenceReceiver is in the same file:
class GeofenceReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
Log.d(TAG, "geofencingEvent.hasError()")
} else {
geofencingEvent.triggeringGeofences.forEach {
val geofence = it.requestId
Log.d(TAG, "location at: " + geofence)
}
}
}
}
The manifest has the following snippet:
<receiver android:name=".GeofenceReceiver"
android:enabled="true"
android:exported="true"/>
That is pretty much it. When I run the code, addOnFailureListener is triggered and the error message is printed and it is resulting in a 1000 exception.
Exception 1000 according to google documentation means GEOFENCE_NOT_AVAILABLE. I have enabled location on both the watch and the phone to its highest level on phone (Improve Location Accuracy is ON). On the watch I have set to use location from both watch and phone (highest level). Still I keep getting the 1000 error code.

Android Kotlin Foregeground Service + Notifications, Why works wrong?

In my application, I need a foreground service that will check a certain condition every minute, and if it is correct, it triggers a notification reminder. The user determines in advance what time and day he would like to have the reminder to. Data is saved in the database. Then the service every minute checks if it has a reminder for a given hour and day and if so sends a notification. The service must work when the user uses the application, when the application runs in the background and when it is closed. Could someone tell me why this code works on one phone but not on others? So-called, if I set a reminder, up to 20 minutes, it works (in all 3 states that I wrote about earlier), but once I set the reminder for the next days, it doesn't work anymore. I am surprised that sometimes the reminder on another phone works and sometimes it doesn't. I checked, the permission for the application to run in the background is selected in the settings. Please help.
Manifest
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="false"
android:icon="#drawable/pills"
android:label="#string/nameOfApplications"
android:roundIcon="#drawable/icon"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<service
android:name=".ForegroundService"
android:enabled="true"
android:exported="true"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
ForegroundService
class ForegroundService : Service() {
companion object {
val CHANNEL_ID = "ForegroundServiceChannel"
val CHANNEL_ID_CHILD = "ForegroundServiceChannelCHILD"
private var isRunning = false
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
val input = intent.getIntExtra("time",15)
createNotificationChannel()
val notificationIntent = Intent(this, Menu::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0, notificationIntent, 0
)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("MotherNotification")
.setContentText("Message")
.setOnlyAlertOnce(true)
.build()
startForeground(1, notification)
isRunning = true
val context = this
val intent = Intent(this, ShowAll::class.java)
val pendingIntentNotification = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
doAsync {
while(isRunning)
{
var message : String = createReminderMessage(context)
//SystemClock.sleep(input * 10_000L)
SystemClock.sleep(50000)
uiThread {
if(isRunning && (message != "Nadszedł czas by zażyć: ")) {
val notification = NotificationCompat.Builder(context, CHANNEL_ID_CHILD)
.setContentTitle("Title")
.setContentText(message)
.setContentIntent(pendingIntentNotification)
.setAutoCancel(true)
.build()
with(NotificationManagerCompat.from(context)) {
notificationManager.notify(2, notification)
}
}
}
}
}
return START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onDestroy() {
super.onDestroy()
isRunning = false
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
val serviceChannel2 = NotificationChannel(
CHANNEL_ID_CHILD,
"Foreground Service ChannelChild ",
NotificationManager.IMPORTANCE_DEFAULT
//NotificationManager.IMPORTANCE_LOW
)
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(serviceChannel)
manager.createNotificationChannel(serviceChannel2)
}
}
fun reminderForNow(context: Context) : ArrayList<Reminder> {
var listOfReminder : ArrayList<Reminder> = ArrayList()
var timetoday = takeTimeNow()
var dateToday = takeTodayDate()
val dbHelper = SQLConector(context)
val allRemindersList = dbHelper.getAllReminders()
for (i: Reminder in allRemindersList) {
if (i.reminderDate == dateToday && i.ReminderTime == timetoday) {
var reminder = Reminder(
i.id,
i.Name,
i.reminderDate,
i.ReminderTime
)
listOfReminder.add(reminder)
}
}
return listOfReminder
}
private fun createReminderMessage(p0: Context) : String{
var message : String = "title : "
var listOfReminders = reminderForNow(p0)
if(listOfReminders.count() > 0){
for (i: Reminder in listOfReminders) {
message += i.Name + ", "
}
}
return message
}
private fun takeTodayDate():String{
val current = LocalDateTime.now()
val formatDate = DateTimeFormatter.ofPattern("yyyy-MM-dd")
var dateResult = current.format(formatDate).toString()
return dateResult
}
private fun takeTimeNow() : String{
val current = LocalDateTime.now()
val formatTime = DateTimeFormatter.ofPattern("HH:mm")
var timeResult = current.format(formatTime).toString()
return timeResult
}
}
Main Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.buttonStart.setOnClickListener { startService() }
binding.buttonStop.setOnClickListener {stopService() }
startService()
}
private fun startService() {
val serviceIntent = Intent(this, ForegroundService::class.java)
serviceIntent.putExtra("time", 1)
ContextCompat.startForegroundService(this, serviceIntent)
}
private fun stopService() {
val serviceIntent = Intent(this, ForegroundService::class.java)
stopService(serviceIntent)
}
}
The correct way to handle tasks which require exact fire time is to use the AlarmManagerCompat class.
You can use setExactAndAllowWhileIdle(...) method to force the alarm to start your service even when the device is in Doze mode and you will need a BroadcastReceiver to re-schedule the alarms if the device is rebooted.
You can find some references online on how to implement that.

E/FirebaseInstanceId: Token retrieval failed: PHONE_REGISTRATION_ERROR in Android Kitkat and below

Edit : I forgot to tell, i already try to make a breakpoint on the onRefreshToken and make a Log in that function, but when i start the apps, none of them is running (the breakpoint doesn't start, and the Log doesnt showing in AS's console log). So IMO its pure that the service itself doesn't start.
I make a code where my apps gonna show a notification if there is a data incoming in onMessageReceived. Its can run well, no problem at all, until i try it in Android Jelly Bean.
The notification doesn't showing (i am sure the notification is sent, because the other non JellyBean device is show the notification at the same time.) and then i trying it in KitKat Device, the result is same.
I then try to debug it and no error showing in Android Studio ( I bet because the device got Chinese rom and doesn't have Google play service built in).
Later i change my method and try it in Emulator, and the E/FirebaseInstanceId: Token retrieval failed: PHONE_REGISTRATION_ERROR is showing. I already try it in emulator that have KitKat and JellyBean image installed, the result is same, the same message appear.
This is my code:
AndroidManifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.appname">
///Some activity and meta data here
<service
android:name=".fcm.MyFirebaseInstanceIdService"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<service
android:name=".fcm.MyFirebaseMessagingService"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</manifest>
MyFirebaseInstanceIdService
class MyFirebaseInstanceIdService : FirebaseInstanceIdService() {
val TAG = "PushNotifService"
lateinit var name: String
override fun onTokenRefresh() {
val token = FirebaseInstanceId.getInstance().token
}
}
MyFirebaseMessagingService
class MyFirebaseMessagingService : FirebaseMessagingService() {
private val session = SessionManagement(this)
#SuppressLint("LongLogTag")
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (remoteMessage.data != null){
val data = remoteMessage.data
val title = data["title"]
val body = data["body"]
showNotification(title, body)
}
}
#SuppressLint("LongLogTag")
override fun onNewToken(token: String?) {
session.updateFCMToken(token)
}
fun subscribeTopic(topic: String?){
//the topic in here is a param send by other activity when the apps lunch
FirebaseMessaging.getInstance().subscribeToTopic(topic).addOnCompleteListener { task ->
if (!task.isSuccessful) {
} else {
}
}
}
//This sendMessageTrainer() is called and run from other activity
fun sendMessageTrainer(){
val contentType = "application/json"
val authorizationKey = ServerHelper.FCMServerKey
val data = "{\"to\": \"/topics/sometopic\",\"data\": {\"title\":\"Request Update\",\"body\":\"New Request.\"}}"
Fuel.post(ServerHelper.FCMServer).header("Content-Type" to contentType, "Authorization" to "key=$authorizationKey").body(data).responseJson{
_, _, result ->
result.failure {
sendMessageTrainer()
}
result.success {
}
}
}
//showNotification() is run if there is a new data/notification from onMessageReceived
private fun showNotification(title: String?, body: String?) {
val intent = Intent(this, LauncherActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
val channelName = getString(R.string.app_name)
val channelID = "default"
val alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notifyID = 1
val notification = NotificationCompat.Builder(this, channelID)
.setSmallIcon(R.drawable.ic_logo)
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)
.setSound(alarmSound)
.setContentIntent(pendingIntent)
.build()
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(channelID, channelName, importance)
val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mNotificationManager.createNotificationChannel(mChannel)
mNotificationManager.notify(notifyID , notification)
}
Build.VERSION.SDK_INT <= Build.VERSION_CODES.N -> {
val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mNotificationManager.notify(notifyID, notification)
}
}
}
}
Thanks and regards,
vl14b

Categories

Resources