android AppWidget buttons unbinding - android

I have a widget that lets you select from 2 sizes, it also has a config. For some reason after some time goes by, the buttons on my widget will unbind and you will not be able to click anything. I dont know why this is happening. Could it be the super.onReceive(context, intent) in my OnRecieve method? Would that cause it to unbind possibly? Also what would the sure fire way to make sure the buttons are ALWAYS binded be?
AppWidgetProvider
public class mWidget extends AppWidgetProvider {
public static String ACTION_WIDGET_REFRESH = "Refresh";
public static final String PREFS_NAME = "mWidgetPrefs";
#Override
public void onEnabled(Context context) {
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews remoteViews = buildLayout(context);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
public static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
RemoteViews views = buildLayout(context);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
public static RemoteViews buildLayout(Context context) {
RemoteViews remoteView = new RemoteViews(context.getPackageName(),
R.layout.widget_4x2);
Intent intent = new Intent(context, mWidget.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
intent, 0);
intent = new Intent(context, mWidget.class);
intent.setAction(ACTION_WIDGET_REFRESH);
pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
remoteView.setOnClickPendingIntent(R.id.refresh, pendingIntent);
return remoteView;
}
#Override
public void onReceive(Context context, Intent intent) {
RemoteViews remoteView = null;
remoteView = new RemoteViews(context.getPackageName(),
R.layout.widget_4x2);
if (intent.getAction().equals(ACTION_WIDGET_REFRESH)) {
Toast.makeText(context, "here", Toast.LENGTH_LONG).show();
} else {
super.onReceive(context, intent);
}
}
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".mwidgetConfig" android:label="#string/app_name"
android:theme="#android:style/Theme.NoTitleBar">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<activity android:name=".mwidgetConfigSmall"
android:label="#string/app_name" android:theme="#android:style/Theme.NoTitleBar">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<!-- BEGIN 4X4 WIDGET -->
<receiver android:label="Test Widget 4x4"
android:name="com.test.mwidget.mWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action
android:name="com.test.mwidget.mWidget.ACTION_WIDGET_REFRESH" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/widget_4x4_provider" />
</receiver>
<!-- BEGIN 4X2 WIDGET -->
<receiver android:label="Test Widget 4x2"
android:name="com.test.mwidget.mWidgetSmall">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action
android:name="com.test.mwidget.mWidgetSmall.ACTION_WIDGET_REFRESH" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/widget_4x2_provider" />
</receiver>
</application>

You should bind your pending intent on each onUpdate event.
You should iterate through all appWidgerIds.
Usually, I implement widget stuff as the following:
public void onUpdate(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
updateAllWidgetsInternal(context, appWidgetManager, appWidgetIds);
}
private static void updateAllWidgetsInternal(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
final RemoteViews views = buildLayout(context);
final int N = appWidgetIds.length;
for (int i=0; i<N; ++i) {
appWidgetManager.updateAppWidget(appWidgetIds[i], views);
}
}
// This function can be executed anywhere in any time to update widgets
public static void updateAllWidgets(Context context) {
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
final int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, mWidget.class));
updateAllWidgetsInternal(context, appWidgetManager, appWidgetIds);
}

I don't understand why you implemented onReceive().
It's only necessary if your widget is configured to catch other broadcasts than the standard widgets broadcast which are ACTION_APPWIDGET_DELETED, ACTION_APPWIDGET_DISABLED, ACTION_APPWIDGET_ENABLED and ACTION_APPWIDGET_UPDATE.
If not necessary, try without onReceive().

Related

onReceive() in widget dosen't fired in api>26 [duplicate]

My widget app is running fine on all android version except 8 Oreo.
I get a W/BroadcastQueue: Background execution not allowed: receiving Intent message.
There is an interesting blog from CommonsWare but I don't fully understand why it applies to my case.
https://commonsware.com/blog/2017/04/11/android-o-implicit-broadcast-ban.html
My case looks pretty simple: I have a widget with a button and I want to change the text's button when it is clicked.
What is the right way to fix this issue?
TestWidget.java
public class TestWidget extends AppWidgetProvider {
private static RemoteViews views;
private static boolean buttonClicked = false;
public static final String ACTION_AUTO_UPDATE = "AUTO_UPDATE";
#Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
if(intent.getAction().equals(ACTION_AUTO_UPDATE))
{
Log.i("TESTWID", "get onReceive");
}
}
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
views = new RemoteViews(context.getPackageName(), R.layout.test_widget);
views.setOnClickPendingIntent(R.id.wid_btn_tst, setButton(context));
appWidgetManager.updateAppWidget(appWidgetId, views);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.i("TESTWID", "onupdate ");
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
public static PendingIntent setButton(Context context) {
Intent intent = new Intent();
intent.setAction("TEST");
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static void pushWidgetUpdate(Context context, RemoteViews remoteViews) {
ComponentName myWidget = new ComponentName(context, TestWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(myWidget, remoteViews);
}
}
TestWidgetReceiver.java
public class TestWidgetReceiver extends BroadcastReceiver{
private static boolean isButtonON = false;
#Override
public void onReceive(Context context, Intent intent) {
Log.i("TESTWID", "onReceive "+intent.getAction());
if(intent.getAction().equals("TEST")){
updateWidgetButton(context, 2);
}
}
private void updateWidgetButton(Context context, int index) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.test_widget);
if(index == 2) {
if(isButtonON) {
remoteViews.setTextViewText(R.id.wid_btn_tst, "Test Off");
isButtonON = false;
}
else{
remoteViews.setTextViewText(R.id.wid_btn_tst, "Test On");
isButtonON = true;
}
}
TestWidget.pushWidgetUpdate(context.getApplicationContext(), remoteViews);
}
}
Manifest.xml:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="Test"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".TestWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="AUTO_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/test_widget_info" />
</receiver>
<receiver
android:name=".TestWidgetReceiver"
android:label="widgetBroadcastReceiver" >
<intent-filter>
<action android:name="TEST" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/test_widget_info" />
</receiver>
</application>
It's subtle, but it is because of the implicit broadcast being used to trigger your TestWidgetReceiver. It is implicit because it is only specifying the action portion of the Intent. Make the broadcast Intent explicit by specifying the receiver class in the constructor:
public static PendingIntent setButton(Context context) {
Intent intent = new Intent(context, TestWidgetReceiver.class);
intent.setAction("TEST");
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
Things are changed now a days in Android. For the security & battery consumption google introduced so many ways & increased some problem for developers.
You haven't passed the Context of the TestWidgetReceiver.class to you intent.
You can do it like that in java
Intent intent = new Intent(context, TestWidgetReceiver.class);
or in kotlin
val intent = Intent(context, TestWidgetReceiver.class);
You can read more changes here
https://developer.android.com/about/versions/oreo/android-8.0-changes
Regards

My App widget's Remoteview's receiver getting call randomly

I am beginner in working with app widget. Here i have made one app widget of app and in its class set receiver on click of widget icon.Its working when user clicks on it.But the issue is receiver also gets called sometimes automatically.
I am not getting why this is happening.Help will be appreciated.
My app widget's class:
MyWidget.java:
public class MyWidget extends AppWidgetProvider {
static Context cont;
static SharedPreferences preferences;
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
preferences = context.getSharedPreferences("pref", Context.MODE_PRIVATE);
cont = context;
Intent intent2 = new Intent(context, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.
getBroadcast(context, 0,
intent2, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
#Override
public void onEnabled(Context context) {
preferences = context.getSharedPreferences("pref", Context.MODE_PRIVATE);
preferences.edit().putBoolean("key", true).commit();
}
#Override
public void onDisabled(Context context) {
preferences = context.getSharedPreferences("pref", Context.MODE_PRIVATE);
preferences.edit().putBoolean("key", false).commit();
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
}
}
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="*************************">
<application
android:allowBackup="true"
android:icon="#drawable/logo"
android:label="#string/app_name"
android:roundIcon="#drawable/logo"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".Home">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".Myservice" />
<receiver android:name=".MyReceiver"></receiver>
<receiver android:name=".MyWidget">
<intent-filter>
<action android:name="**********************" />
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/my_widget_info" />
</receiver>
</application>
</manifest>

RemoteViewsService doesn't execute,

I want to do a widget with a list inside, I have to implement a class that extends of RemoteViewsService, but this class never execute, my code is the following:
MyWidgetProvider
public class MyWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget);
Intent updateIntent = new Intent(context, WidgetService.class);
for (int appWidgetId : appWidgetIds) {
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
views.setRemoteAdapter(appWidgetId, updateIntent);
else
views.setRemoteAdapter(appWidgetId, R.id.events_list, updateIntent);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.events_list);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}
WidgetService
public class WidgetService extends RemoteViewsService {
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new WidgetFactory(getApplicationContext(), intent);
}
}
And the Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.widget.example"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<receiver android:name=".MyWidgetProvider" >
<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=".WidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="false">
</service>
</application>
Edit;
I have found the error, i have to use:
views.setRemoteAdapter(R.id.events_list, updateIntent);
instead of
views.setRemoteAdapter(appWidgetId, updateIntent);
See RemoteViews.html#setRemoteAdapter
The SetRemoteAdapter function with 3 parameters is deprecated in API level 14+, and even if you called the 3 parameters one, the widget id parameter is ignores. So the function always receive only 2 valid parameters, i.e. view_id and intent.
This function basically says I'm going to use the intent as a data source for the view (indicated by view_id).
for me this works
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
for (int widgetId : appWidgetIds) {
RemoteViews mView = initViews(context, appWidgetManager, widgetId);
appWidgetManager.updateAppWidget(widgetId, mView);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
private RemoteViews initViews(Context context,
AppWidgetManager widgetManager, int widgetId) {
RemoteViews mView = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
Intent intent = new Intent(context, WidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
mView.setRemoteAdapter(widgetId, R.id.widgetCollectionList, intent);
return mView;
}

Update widget onClick, start service does not seem to work

I've followed a bunch of tutorials, search on google and on stack overflow and came up with this code to update my widget when i touch it:
public class WidgetService extends Service{
#Override
public void onStart(Intent intent, int startId) {
Log.i("WidgetService", "Called");
String fakeUpdate = null;
Random random = new Random();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this
.getApplicationContext());
int[] appWidgetIds = intent
.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds.length > 0) {
for (int widgetId : appWidgetIds) {
int nextInt = random.nextInt(100);
fakeUpdate = "Random: " + String.valueOf(nextInt);
RemoteViews remoteViews = new RemoteViews(getPackageName(),
R.layout.widget);
remoteViews.setTextViewText(R.id.txt_updated, fakeUpdate);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
stopSelf();
}
super.onStart(intent, startId);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
And for widget provider:
public class Widget extends AppWidgetProvider{
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget);
Intent intent = new Intent(context.getApplicationContext(),
WidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getService(
context.getApplicationContext(), 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.cnt_widget, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
context.startService(intent);
}
}
In manifest:
<receiver
android:label="#string/next_trip_text"
android:name=".widget.Widget" >
<intent-filter >
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_info" />
</receiver>
And xml:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="272dp"
android:minHeight="72dp"
android:updatePeriodMillis="0"
android:initialLayout="#layout/widget"
android:configure="se.webevo.basttrafik.widget.WidgetConfigActivity" >
</appwidget-provider>
The service doesn't seem to be called at all. Any ideas? :)
Thanks!
add this to manifest:
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/-- ur appwidgetprovider location here--">
</meta-data>
also try adding:
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_ENABLED"/>
</intent-filter>
see if it works

Android widget, start intent

I'm trying to make an Android widget, to show some info from my app.
I have succeed to show some info, but know i what to have a feature to "switch page" in my app. It means pressing a button, and new info is showing. But how do i do it? I have tried different thing, and I'm ending up with the following code:
public class HelloWidget extends AppWidgetProvider {
RemoteViews remoteViews;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Toast.makeText(context, "onUpdate", Toast.LENGTH_SHORT).show();
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
Intent intent = new Intent();
intent.setAction(TestReceiver.TEST_INTENT);
intent.setClassName(TestReceiver.class.getPackage().getName(), TestReceiver.class.getName());
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.bbutton1, pendingIntent);
remoteViews.setTextViewText(R.id.widgetTxt, "The textview");
appWidgetManager.updateAppWidget(appWidgetIds[0], remoteViews);
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
public class TestReceiver extends BroadcastReceiver {
public static final String TEST_INTENT = "MyTestIntent";
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "You reached the TestReceiver", Toast.LENGTH_SHORT).show();
if (intent.getAction() == TEST_INTENT) {
remoteViews.setTextViewText(R.id.widgetTxt, "Is changed");
}
}
}
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
final int appWidgetId = intent.getExtras().getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
this.onDeleted(context, new int[] { appWidgetId });
}
} else {
super.onReceive(context, intent);
}
}
#Override
public void onDeleted(Context context, int[] appWidgetIds) {
Toast.makeText(context, "onDelete", Toast.LENGTH_SHORT).show();
super.onDeleted(context, appWidgetIds);
}
}
Toast are showing "onUpdate" and then i remove it at "onDeleted". The textview also change in the onUpdate, but nothing happens when i press the button.
My Manifest look like this:
<receiver android:name=".HelloWidget" android:label="#string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/hello_widget_provider" />
</receiver>
<receiver android:name=".TestReceiver" android:label="#string/app_name">
<intent-filter>
<action android:name="MyTestIntent">
</action>
</intent-filter>
</receiver>
What am i doing wrong?
Thanks
Is your TestReceiver an inner class of HelloWidget?
Then you didn't specify the correct name in the manifest.

Categories

Resources