Android AppWidget - sometimes the onDataSetChanged() method doesn't get called - android

I noticed strange behavior of the widget in my app. Sometimes the onDataSetChanged() method from RemoteViewsFactory class is not called (of course when the app is in the background and widget is visible) even though the notifyAppWidgetViewDataChanged() from AppWidgetManager class works correctly. I don't see any specific situation where this is happening - just sometimes it works, sometimes it doesn't. I spent a lot of time on debugging - everything looks like it's working fine.
val ids: IntArray = appWidgetManager.getAppWidgetIds(ComponentName(context, MyWidgetProvider::class.java))
appWidgetManager.notifyAppWidgetViewDataChanged(ids, R.id.appwidget_list_view)
I noticed that when I change it to:
ids.forEach { id ->
appWidgetManager.notifyAppWidgetViewDataChanged(id, R.id.appwidget_my_access_list_view)
}
the problem occurs less frequently, but maybe it's coincidence.
Significant code snippets:
MyWidgetProvider: AppWidgetProvider()
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
appWidgetIds.forEach { id ->
/* *** */
val intent = Intent(context, MyWidgetRemoteViewsService::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id)
data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
}
val remoteViews = RemoteViews(context.packageName, R.layout.my_widget).apply {
setRemoteAdapter(R.id.my_widget_list_view, intent)
/* *** */
}
appWidgetManager.apply {
updateAppWidget(id, remoteViews)
}
}
}
MyWidgetRemoteViewsService: RemoteViewsService()
#AndroidEntryPoint
class MyWidgetRemoteViewsService : RemoteViewsService() {
#Inject lateinit var itemStore: LockStore
override fun onGetViewFactory(intent: Intent) = MyAccessRemoteViewsFactory(applicationContext, itemStore)
}
AndroidManifest
<receiver
android:name=".MyWidgetProvider"
android:label="My app">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/appwidget_info"/>
</receiver>
<service
android:name=".MyWidgetRemoteViewsService"
android:permission="android.permission.BIND_REMOTEVIEWS"/>

Related

Why is my PendingIntent not broadcasting/receiving from Android widget setOnClickPendingIntent?

AppWidget.kt:
override fun onEnabled(context: Context) {
// register price and widget button update receivers
val filters = IntentFilter()
filters.addAction(BROADCAST_PRICE_UPDATED)
filters.addAction(BROADCAST_WIDGET_UPDATE_BUTTON_CLICK)
LocalBroadcastManager.getInstance(context.applicationContext).registerReceiver(br, filters)
}
override fun onDisabled(context: Context) {
// unregister price and widget button update receivers
LocalBroadcastManager.getInstance(context.applicationContext).unregisterReceiver(br)
}
private val br = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { ... }
Notes: local broadcast receiver receives all other registered broadcasts, except from piWidgetUpdateButtonClicked. Using LocalBroadcastManager is the only way I've been able to send/receive broadcasts, as apparently using global broadcast system filters out broadcasts from my app's package?
internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
// Construct the RemoteViews object
val views = RemoteViews(context.packageName, R.layout.app_widget)
// Create an Intent to launch MainActivity when widget background touched
val piLaunchMainActiv: PendingIntent = PendingIntent.getActivity(context,0,
Intent(context.applicationContext, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
views.setOnClickPendingIntent(R.id.widget_background_layout, piLaunchMainActiv)
// create intent to update widget when button clicked TODO this not working
val piWidgetUpdateButtonClicked =
PendingIntent.getBroadcast(context, appWidgetId,
Intent(BROADCAST_WIDGET_UPDATE_BUTTON_CLICK), PendingIntent.FLAG_UPDATE_CURRENT
or PendingIntent.FLAG_IMMUTABLE
)
views.setOnClickPendingIntent(R.id.widget_update_button, piWidgetUpdateButtonClicked)
Notes: *piLauchMainActiv *pending intent works fine, but piWidgetUpdateButtonClicked does nothing. I've tried using context.applicationContext, also does nothing. Also I've tried explicitly setting receiving class: Intent.setClass(context, AppWidget::class.java), nothing.
Apparently, latest Google Android releases want you to register broadcast receivers programmatically, as I've done above, rather than in the manifest, which I've tried too, but also doesn't work:
AndroidManifest.xml:
<receiver
android:name=".AppWidget"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="org.bitanon.bitcointicker.BROADCAST_PRICE_UPDATED" />
</intent-filter>
<intent-filter>
<action android:name="org.bitanon.bitcointicker.BROADCAST_WIDGET_UPDATE_BUTTON_CLICK" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/app_widget_info" />
</receiver>
After days and days of reading through other related posts on stackoverflow, nothing has helped. I'm completely stumped, please send help!

Application not getting RECEIVE_BOOT_COMPLETED broadcast

I have tried every possible answer on SO but nothing has helped.
I want to reset alarms using AlarmManager on device reboot and the code to do exactly that works, but it doesn't when I put it inside the receiver.
I had tried creating a service but that didn't seem to work at all which that was probably incompetence of my part, however I just can't see why this code isn't working.
AndroidManifest:
<manifest
(...)
<uses-permission android:name = "android.permission.RECEIVE_BOOT_COMPLETED" />
(...)
<application
(...)
<receiver
android:name = ".utils.DeviceRebootReceiver">
<intent-filter>
<action android:name = "android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
DeviceRebootReceiver:
class DeviceRebootReceiver : BroadcastReceiver() {
override fun onReceive(context : Context?, intent : Intent?) {
resetAlarms(context)
}
}
fun resetAlarms(context:Context):
fun resetAlarms(context : Context?) {
suspend fun resetAlarmsCoroutine(context : Context) {
val reminders = NotesDatabase.getInstance(context).notesDatabaseDAO.getAllActiveReminders()
reminders.forEach {
if (it.reminderTime > System.currentTimeMillis()) {
createAlarm(it.reminderTime, it.reminderMessage,null,context)
}
}
}
CoroutineScope(Dispatchers.IO).launch {
if (context != null) {
resetAlarmsCoroutine(context)
}
}
}
No need to show the createAlarm() function cause it works fine, BUT since I had seen that AlarmManager could cause problems on reboot I do instantiate it there like so:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

JobIntentService job ID globally unique or scoped by the class?

Documentation implies it's scoped per the service class:
A unique job ID for scheduling; must be the same value for all work
enqueued for the same class.
However, I have two services with their JOB_ID, and enqueWork() as:
companion object {
private const val JOB_ID = 1
fun enqueueWork(context: Context, action: String) {
enqueueWork(context, Svc1::class.java, JOB_ID, Intent(action))
}
}
and
companion object {
private const val JOB_ID = 1
fun enqueueWork(context: Context, action: String) {
enqueueWork(context, Svc2::class.java, JOB_ID, Intent(action))
}
}
when I start both the services at boot (probably around the same time) Svc2 runs 2x. Changing Svc2's JOB_ID to 2 solves the problem.
If it's NOT scoped by the service class, that's awful as it means every service needs to be aware of the implementation of every other service in the same app.
?

Oreo: How to listen unlock event?

I want to do some work when user unlock their phone
I define a receiver in AndroidManifest.xml
<receiver
android:enabled="true"
android:name=".service.ScreenReceiver">
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
and Receiver
class ScreenReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
}
private fun checkClearSavedNote(context: Context) {
AppPref.getInstance(context).putString(AppPref.KEY_ID_CURRENT_NOTE, "")
Log.e("Quang", "clear note")
}
}
But it was not called when fired
I've tried using Service and registerBroadcastReceiver inside
and start it when application start
class MyApplication : MultiDexApplication() {
override fun onCreate() {
super.onCreate()
instance = this
MultiDex.install(applicationContext)
try {
startService(Intent(this, NoteService::class.java))
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}
}
but it work only with Android API < 8.0 because background execution limit
You can set your targetSdk < 26 to leverage implicit broadcasts. Otherwise you have to change your design according to latest list of exempted broadcasts.

NotificationListenerService not created

Maybe stupid question, but I have already spent to many hours on this.
I have my Kotlin listener:
package pl.bmideas.michal.bmnotifier
public class MyNotificationListener : NotificationListenerService() {
private var apiService :BackendApi? = null;
override fun onCreate() {
Log.i("MyNotificationListener" , "Creating NotificationListenerService service")
super.onCreate()
(.........SOMETHING ELSE..............)
}
override fun onDestroy() {
super.onDestroy()
Log.i(TAG, "DESTROING")
(.........SOMETHING ELSE..............)
}
override fun onNotificationRemoved(sbn: StatusBarNotification) {
val sbnInfo = StatusBarNotificationExtended(sbn)
Log.i(TAG, "REMOVED")
}
override fun onNotificationPosted(sbn: StatusBarNotification) {
Log.i(TAG, "RECIVED`")
(.........SOMETHING ELSE..............)
}
companion object {
var TAG = "MyNotificationListener"
}
}
and my config looks looks this:
<service
android:enabled="true"
android:name="pl.bmideas.michal.bmnotifier.MyNotificationListener"
android:label="#string/service_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
I'm not doing anything special in Activity.
Yes - I've checked security option and my app has access to notifications.
Yes - I've tried pointing to service by dot instead of full package
In logcat I can only see:
12-23 12:56:54.989 889-889/? V/NotificationListeners: enabling notification listener for 0:
ComponentInfo{pl.bmideas.michal.bmnotifier/pl.bmideas.michal.bmnotifier.MyNotificationListener}
I cant get instance unless i will bidn to this service in Activity wchich creates the service but still I get no info in logcat about notifications.
Can you guys help?
Holly....
after rewriting this code to pure Java it works..... but why?

Categories

Resources