After orientation change buttons on a widget are not responding - android

I have two buttons on a widget that change some items in a widget, if an orientation is changed on a phone, buttons do nothing. I read http://developer.android.com/guide/topics/resources/runtime-changes.html but this is all about activity not widget.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
Intent active = new Intent(context, TvWidget.class);
active.setAction(ACTION_WIDGET_RECEIVER);
mDbHelper = new DbAdapter(context);
fillChannelList(context, appWidgetIds[appWidgetIds.length-1]);
Set<Integer> keys = channelsImages.keySet();
Iterator<Integer> iter = keys.iterator();
while(iter.hasNext())
{
if(channelId == 0)
{
channelId = iter.next();
break;
}
}
SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, 0);
Editor edit = settings.edit();
edit.putInt("channelId", channelId);
edit.putInt("appWidgetIds", appWidgetIds[appWidgetIds.length-1]);
edit.commit();
active.putExtra("net.aerosoftware.tvvodic.appWidgetIds", appWidgetIds);
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.button_next, actionPendingIntent);
Intent refresh = new Intent(context, TvWidget.class);
refresh.setAction(ACTION_WIDGET_REFRESH);
refresh.putExtra("net.aerosoftware.tvvodic.appWidgetIds", appWidgetIds);
PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0, refresh, 0);
remoteViews.setOnClickPendingIntent(R.id.button_refresh, refreshPendingIntent);
updateView(context);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
super.onUpdate(context, appWidgetManager, appWidgetIds);
}

I would suggest creating a Service (possibly subclassing this within your AppWidgetProvider) and overriding the onConfigurationChanged() method. Using the service will allow you to delegate your business logic to be handled by the service, build, and update your widget. It will also allow you to manage rotations. And if you're performing any blocking operations then the service would be a good place to spawn a Thread and return the result back to the main UI thread to avoid ANRs.
I would suggest something like the following:
public class MyWidget extends AppWidgetProvider
{
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
context.startService(new Intent(context, MyUpdateService.class));
}
public static class MyUpdateService extends Service
{
#Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
// Update the widget
RemoteView remoteView = buildRemoteView(this);
// Push update to homescreen
pushUpdate(remoteView);
// No more updates so stop the service and free resources
stopSelf();
}
public RemoteViews buildRemoteView(Context context)
{
RemoteView updateView = null;
updateView = new RemoteViews(context.getPackageName(), R.layout.my_widget_layout);
// Your code to build and update the remote view
return updateView;
}
#Override
public void onConfigurationChanged(Configuration newConfig)
{
int oldOrientation = this.getResources().getConfiguration().orientation;
if(newConfig.orientation != oldOrientation)
{
// Update the widget
RemoteView remoteView = buildRemoteView(this);
// Push update to homescreen
pushUpdate(remoteView);
}
}
private void pushUpdate(RemoteView remoteView)
{
ComponentName myWidget = new ComponentName(this, MyWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(myWidget, updateViews);
}
}
}
Also have a look at this link: http://android-developers.blogspot.com/2009/04/introducing-home-screen-widgets-and.html
Also, be sure to indicate that you are interested in receiving notifications on rotation change within your manifest. Something like this will work:
android:configChanges="keyboardHidden|orientation"
declared within your service declaration inside the manifest.
Hope that helps!

Whenever you update the look of your widget (using either an Activity or your Broadcast Receiver [App widget provider]), you must also reassign all the PendingIntents for the click handlers, and then call updateAppWidget() as normal.
Example with setTextViewText():
// This will update the Widget, but cause it to
// stop working after an orientation change.
updateWidget()
{
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
remoteViews.setTextViewText(R.id.widget_text_view, "Updated widget");
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
// This is the correct way to update the Widget,
// so that it works after orientation change.
updateWidget()
{
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
remoteViews.setTextViewText(R.id.widget_text_view, "Updated widget");
Intent intent = new Intent(context, MyWidgetActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, ...);
remoteViews.setOnClickPendingIntent(R.id.widget_click_button, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}

The problem might be due to having two buttons on the Widget, which somehow causes problems. See here:
http://permalink.gmane.org/gmane.comp.handhelds.android.devel/98008
or
http://groups.google.com/group/android-developers/browse_thread/thread/578a4429c369c27c/273a53a96ddd10c5?lnk=gst&q=Widget+does+not+respond+when+phone+orientation+changes#273a53a96ddd10c5
But it's not yet clear what the true solution is (a solution that doesn't need to create a background Service).

If you use
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
to change some state of a specific view inside the remote views object, you need to change the other views too, because the orientation change event will only remember the last RemoteViews object that you used to update your widget. It will recreate the widget with that last used RemoteViews object. That is why you always need to update all the views inside the RemoteViews object, and also all the listeners for your views.
The rest will be handled by the OS automatically.

Related

partiallyUpdateAppWidget on RemoteViews not working

In the RemoteViews for an AppWidget I have an AdapterViewFlipper which is supposed to flip when the user clicks a button in the AppWidget. According to the official documentation this should be done by calling the showNext on the RemoteViews. The AppWdiget should then be updated with partiallyUpdateAppWidget which even has the showNext() function as an example in the documentation. I've tried implementing this and I'm most likely making some silly mistake that I can't seem to figure out.
public class WidgetProvider extends AppWidgetProvider {
public static final String NEXT = "next";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
Intent nextIntent = new Intent(context, WidgetProvider.class);
nextIntent.setAction(NEXT);
views.setOnClickPendingIntent(R.id.next_button,
PendingIntent.getBroadcast(context, appWidgetId, nextIntent,
PendingIntent.FLAG_UPDATE_CURRENT));
views.setRemoteAdapter(R.id.view_flipper, new Intent(context,
WidgetRemoteViewService.class));
appWidgetManager.updateAppWidget(appWidgetId, views);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(NEXT)) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
remoteViews.showNext(R.id.view_flipper);
AppWidgetManager.getInstance(context).partiallyUpdateAppWidget(
AppWidgetManager.getInstance(context).getAppWidgetIds(
new ComponentName(context, WidgetProvider.class)),
remoteViews);
}
super.onReceive(context, intent);
}
}
When replacing partiallyUpdateAppWidget with updateAppWidget the next button is working until the orientation changes or the phone goes to sleep which forces a redraw of the last stored RemoteViews. This leads me to believe the mistake I'm making has to do with partial update but I can't figure out what I'm doing wrong.
In which API level are you testing?
Maybe the problem with withpartiallyUpdateAppWidget is related to this open issue issuetracker.google.com/issues/37136552

AppWidget is not updated even though onUpdate is called

I post a broadcast to update my widget and its onUpdate function is called, I can see it in the logs, but the text remains unchanged - apart from the 1st call to onUpdate, when the widget is created.
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.d("WIDGET", "onUpdate");
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_home);
views.setTextViewText(R.id.text, new Random().nextInt() + "x");
Intent intent = new Intent(context, InfoActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener
// to the button
views.setOnClickPendingIntent(R.id.text, pendingIntent);
appWidgetManager.partiallyUpdateAppWidget(appWidgetIds, views);
Log.d("WIDGET", "updated");
}
you need store the appWidgetId when it create , when you want to update it.
call appWidgetManager.updateAppWidget
im not sure why partiallyUpdateAppWidget
not work. you can read the docs
appWidgetManager.partiallyUpdateAppWidget(appWidgetIds, views);

Android Widget click doesn't do anything after few hours

I have a widget that has a refresh button and a textview. Refresh updates the content and when user clicks on textview it starts a new activity.
Problem is it works fine for a few hours and then onclick and refresh button doesn't do anything. Nothing is captured in logcat. Also If user deletes widget and put a new one it starts working for a few hours and then the same story :(...what am I doing wrong!
Broadcast receiver.
onUpdate
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
long interval = getrefresInterval();
final Intent intent = new Intent(context, UpdateService.class);
final PendingIntent pending = PendingIntent.getService(context, 0, intent, 0);
final AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(pending);
alarm.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(),interval, pending);
// Build the intent to call the service
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget);
// To react to a click we have to use a pending intent as the
// onClickListener is excecuted by the homescreen application
Intent ClickIntent = new Intent(context.getApplicationContext(),widgetHadith.class);
Intent UpdateIntent = new Intent(context.getApplicationContext(),UpdateService.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context.getApplicationContext(), 0, ClickIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntentUpdate = PendingIntent.getService(context.getApplicationContext(), 0, UpdateIntent,
PendingIntent.FLAG_UPDATE_CURRENT); //use this to update text on widget. if use this put UpdateService.class to intent
remoteViews.setOnClickPendingIntent(R.id.widget_textview, pendingIntent);
remoteViews.setOnClickPendingIntent(R.id.widget_refresh, pendingIntentUpdate);
// Finally update all widgets with the information about the click listener
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
// Update the widgets via the service
context.startService(intent);
}
onReceive
public void onReceive(Context context, Intent intent) {
// v1.5 fix that doesn't call onDelete Action
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);
}
}
onDelete
public void onDeleted(Context context, int[] appWidgetIds) {
// Toast.makeText(context, "onDelete", Toast.LENGTH_SHORT).show();
super.onDeleted(context, appWidgetIds);
}
Service onstart where I am updating
#Override
public void onStart(Intent intent, int startId) {
RemoteViews updateViews = new RemoteViews(this.getPackageName(),R.layout.widget);
processDatabase();
Spanned text = LoadHadith();
String hadith = text.toString();
Log.d("BR", "service---> ");
// set the text of component TextView with id 'message'
updateViews.setTextViewText(R.id.widget_textview, text);
//Push update for this widget to the home screen
ComponentName thisWidget = new ComponentName(this, HelloWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
The problem is that you can't do a partiall update for a widget, you must set all the widget features, such as the set of PendingIntent's every time you push a new remoteView.
(Partiall updates are only available for API14 and up...).
The reason your widgets are loosing their pendingIntents is that the android system saves the remoteView, and rebuilds your widget with it, in case it resets the widget (shortage of memmory, TaskManager/taskKiller in use, etc...), so you must set all the update code for the widget in the remoteView in your updateService.
Otherwise, it's just won't set the pendingIntents again.
So just add the code setting the pendingIntents to the service and your problem will be solved =]

How to handle widgets buttons

I'm starting with widgets and got a very nice tutorial on the internet, got the example run perfectly, but when i tried to change somethings I got stuck.
The thing is: I just want to change the image from my imageButton when i press it, I've tried somethings but nothing seems to work. I didn't get how the RemoteView and Intent works exactly. So if someone can explain it shortly I would appreciate it =)
Here's the code:
public class HelloWidget extends AppWidgetProvider {
private ImageButton wifi;
public static String ACTION_WIDGET_CONFIGURE = "ConfigureWidget";
public static String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
Intent configIntent = new Intent(context, ClickOneActivity.class);
configIntent.setAction(ACTION_WIDGET_CONFIGURE);
Intent active = new Intent(context, HelloWidget.class);
active.setAction(ACTION_WIDGET_RECEIVER);
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
PendingIntent configPendingIntent = PendingIntent.getActivity(context, 0, configIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.button_wifi, actionPendingIntent);
remoteViews.setOnClickPendingIntent(R.id.button_two, configPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent) {
// v1.5 fix that doesn't call onDelete Action
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 {
// check, if our Action was called
if (intent.getAction().equals(ACTION_WIDGET_RECEIVER)) {
Toast.makeText(context, "Teste", Toast.LENGTH_LONG).show();
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
remoteViews.setInt(R.id.button_wifi, "toogleOnOff", R.drawable.icon);
}
super.onReceive(context, intent);
}
}
}
There's a lot of the tutorial code i got as you can see =p
Thx since now
Looks like you need to understand a little more about RemoteViews. When you call functions like setOnClickPendingIntent, setInt, etc. on the RemoteViews object it basically just stores these operations and arguments internally. Then when the widget is displayed it just plays those operations back to construct the widget's views. So after giving the RemoteViews to the AppWidgetManager by calling updateAppWidget, you can't change them again unless you rebuild the whole RemoteViews and call updateAppWidget again.
As an answer to your question, you want to use a state list as the background for the button. There's a good example here.

android widget unresponsive

I have a widget that you press and it then it will update the text on the widget. I have set an on click listener to launch another activity to perform the text update, But for some reason it only works temporarily and then it will become unresponsive and not do anything when pressed. Does anyone know why it might be doing that? i have posted my widget code below in case it is helpful.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {
thisWidget = new ComponentName(context, MemWidget.class);
Intent intent = new Intent(context, updatewidget.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
views.setOnClickPendingIntent(R.id.ImageButton01, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current App Widget
appWidgetManager.updateAppWidget(thisWidget, views);
}
#Override
public void onReceive(Context context, Intent intent)
{
appWidgetManager = AppWidgetManager.getInstance(context);
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
thisWidget = new ComponentName(context, MemWidget.class);
// v1.5 fix that doesn't call onDelete Action
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);
}
}
Here is code that is called from my activity
thisWidget = new ComponentName(this, MemWidget.class);
appWidgetManager = AppWidgetManager.getInstance(this);
remoteViews = new RemoteViews(this.getPackageName(), R.layout.widget);
//do work
remoteViews.setTextViewText(R.id.ImageButton01,"setting text here");
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
The onUpdate method there doesn't update any of the data in the RemoteViews other than the PendingIntent, so if that's ever called, the widget will revert to the state defined in R.layout.widget.
Do you have the code that calls updateAppWidget after the user interaction? That might help too.
Also, if the update is inline and doesn't require any UI, you don't need to launch an activity to do that update. It's more efficient and won't disrupt the back stack if your PendingIntent is for a broadcast receiver instead, using PendingIntent.getBroadcast. You can use the same BroadcastReceiver that is your app widget provider. You don't need another one.
Update: (I can't reply below because the text is too long)
I'd make a function like this, and call it from your activity from onUpdate(). You'll need to save text somewhere so you can also pass it in from onUpdate(). Otherwise it will revert the text to the default in R.layout.widget.
void updateWidget(Context context, CharSequence text) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageButton01, pendingIntent);
remoteViews.setTextViewText(R.id.ImageButton01, text);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName thisWidget = new ComponentName(context, MemWidget.class);
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
}

Categories

Resources