ListView widget with multiple pending intents - android

I'm using widget with listview that has 3 types of items. For each type of item I should use different pending intents. Currently I'm using following code:
public class MyWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager widgetManager, int[] widgetIds) {
for (int widgetId : widgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_view);
bindEmptyView(remoteViews);
bindRemoteAdapter(context, widgetId, remoteViews);
bindIntentTemplate(context, widgetId, remoteViews);
widgetManager.updateAppWidget(widgetId, remoteViews);
}
}
private void bindEmptyView(RemoteViews remoteViews) {
remoteViews.setEmptyView(android.R.id.list, android.R.id.empty);
}
private void bindRemoteAdapter(Context context, int widgetId, RemoteViews remoteViews) {
Intent intent = new Intent(context, MyViewService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
remoteViews.setRemoteAdapter(android.R.id.list, intent);
}
private void bindIntentTemplate(Context context, int widgetId, RemoteViews remoteViews) {
Intent intent = new Intent(context, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent template = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setPendingIntentTemplate(android.R.id.list, template);
}
}
From this point I don't understand how can I invoke specific intent for different list items.

Hopefully I understood your issue well, so I'll try to explain in detail what's happening and how to handle clicks on list items on the widget.
I assume you already know that you have to implement a class which:
extends BroadcastReceiver implements RemoteViewsService.RemoteViewsFactory
This will serve as the "adapter" for your widget's ListView(let's call it MyListRemoteViewFactory).
If you want to handle item clicks on a widget's listView, you do the following things:
1) set a setPendingIntentTemplate in your AppWidgetProvider class
2) set a setOnClickFillInIntent in the MyListRemoteViewFactory overridden getViewAt(int position) method
NOW:
Doing step 1), you might wanna do something like:
final Intent serviceIntent = new Intent(context, MyListRemoteViewFactory.class);
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
views.setRemoteAdapter(R.id.widget_list_view, serviceIntent);
} else {
views.setRemoteAdapter(widgetId, R.id.widget_list_view, serviceIntent);
}
// Individuals items of a collection cannot set up their own pending intents. Instead, the collection as a whole sets up a pending intent template and the individual
// items set a fillInIntent to create unique behavior on an item-by-item basis.
Intent listItemClickIntent = new Intent(context, MyWidgetProvider.class); // This is the name of your AppWidgetProvider class
// Set the action for the intent. When the user touches a particular view, it will have the effect of broadcasting an action
listItemClickIntent.setAction(context.getString("com.example.list.item.click"));
listItemClickIntent.setData(Uri.parse(listItemClickIntent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent clickPendingIntent = PendingIntent.getBroadcast(context, 0, listItemClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setPendingIntentTemplate(R.id.widget_list_view, clickPendingIntent);
You can place the above code snippet after wherever you initialize your RemoteViews object:
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.layout_widget);)
So right now you have your pendingIntentTemplate ready. One other thing to do is to implement the class' onReceive method, so you can decide what to do when an action for the above case occurred. So you'll do something like:
#Override
public void onReceive(Context context, Intent intent) {
// Called on every broadcast and before each of the above callback methods.
super.onReceive(context, intent);
ComponentName name = new ComponentName(context, WidgetProvider.class);
int[] appWidgetIds = AppWidgetManager.getInstance(context).getAppWidgetIds(name);
if (appWidgetIds == null || appWidgetIds.length == 0) {
return;
}
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
if (intent.getAction().equals("com.example.list.item.click") {
// This is where you have to decide what you'll do depending on which list item was tapped. BUT this depends on the fill Intent set in the MyListRemoteViewFactory class's getViewAt() method
// I'll jump straight to the logic here, but once you're done reading this post, get back here to understand the logic because this is the key here. But simple as hell.
int simpleDecidingFactor = intent.getIntExtra("SELECTED_ITEM", 0)
if (simpleDecidingFactor != 0) {
if (simpleDecidingFactor == 1) {
// Do something for the first case. Probably open activity2
} else if (simpleDecidingFactor == 2) {
// Do something for the second case. Probably open activity2
} else if (simpleDecidingFactor == 3) {
// Do something for the second case. Probably open activity3
}
}
}
}
[Damn this'll be long]
DOING STEP 2) (from now on we talk about implementations in the MyListRemoteViewFactory class)
If you need 3 different items in the list, first you have to add this method to the MyListRemoteViewFactory(you're forced to override it anyway, the key is to return the number of views you have):
#Override
public int getViewTypeCount() {
return 3;
}
In the getViewAt() method you add your logic based on what, you decide what to display depending on your position. Something like:
#Override
public RemoteViews getViewAt(int position) {
if (position >= mItems.size()) {
return null;
}
RemoteViews views;
if (mItems.get(position).getViewType() == 0) {
views = new RemoteViews(mContext.getPackageName(), R.layout.list_item_first);
setUpItem(views, mItems.get(position), 1); // !!!! the 3rd parameter is very important here, as you'll expect this number in the MyWidgetProvider class' onReceive method. See the simpleDecidingFactor variable there.
} else if (mItems.get(position).getViewType() == 1) {
views = new RemoteViews(mContext.getPackageName(), R.layout.list_item_second);
setUpItem(views, mItems.get(position), 2);
} else {
views = new RemoteViews(mContext.getPackageName(), R.layout.list_item_third);
setUpItem(views, mItems.get(position), 3);
} // Or add whatever logic you have. Here, I supposed I have a field inside my object telling me what type my item is
return views;
}
And the setUpItem method might look something like:
private void setUpItem(RemoteViews views, MyObject object, int viewTypeKey) {
// This is where you set your clickFillInIntent. Without setting it, nothing'll be functional
Bundle extras = new Bundle();
extras.putInt("SELECTED_ITEM", viewTypeKey);
//extras.putParcelable("LIST_ITEM_OBJECT", object); // You may send your object as well if you need it
Intent fillInIntent = new Intent();
fillInIntent.putExtras(extras);
// You have your fillInIntent prepared, you only have to decide on what view to place it.
// I assume you have a Button on all 3 of your list item layouts with the id button_click. Let's place the Intent:
views.setOnClickFillInIntent(R.id.button_click, fillInIntent);
}
You might want to make sure you declared everything in the Manifest file as well. You have to declare your widgetprovider, your receiver for the list, and a service handling your Factoryclass. You should have something like:
<receiver
android:name=".MyWidgetProvider"
android:enabled="true"
android:label="My awesome widget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
<!-- You have to declare your used actions here, so the AppWidgetProvider knows what to listen for-->
<action android:name="com.example.list.item.click"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_provider_info"/>
</receiver>
<!-- The service serving the RemoteViews to the collection widget -->
<service
android:name=".WidgetRemoteViewsService"
android:exported="false"
android:permission="android.permission.BIND_REMOTEVIEWS"/>
<receiver
android:name=".ui.widget.MyListRemoteViewFactory"
android:enabled="true"
android:exported="false">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<!-- You might want to use an action to notify the appwidget provider from the MyListRemoteViewFactory's onReceive method. This class extends a BroadcastReceiver, so you must implement it's onReceive(Context context, Intent intent) method. If you need help on this, let me know and I'll edit my answer with some example for that too -->
<action android:name="com.example.refresh.remote.views"/>
</intent-filter>
</receiver>
Ah, BTW the WidgetRemoteViewsService class should look like this:
public class WidgetNewsRemoteViewsService extends RemoteViewsService {
#Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new MyListRemoteViewFactory();
}
}
I guess this is pretty much it. I hope I didn't skip anything.

Related

How can i run methods in AppWidget?

i learned today how to develop AppWidget and my question is, how can i run methods in the app widget?
example:
I have a button in the xml and i want the button calculate something.
-the part of create the widget works fine and now i want to add some methods to the button.
how does its works in "App Widgets"?
here is my code:
public class WorkClockWidget extends AppWidgetProvider {
MySharedPreferences shared;
RemoteViews views;
private Context context;
private String text;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
for (int i = 0; i < appWidgetIds.length; i++) {
views = new RemoteViews(context.getPackageName(), R.layout.clock_widget);
/// i have button in the "R.layout.clock_widget" .
//what i need to do if i want the button run the "someText()" method?
appWidgetManager.updateAppWidget(appWidgetIds[i], views);
}
}
//The method i want to run when i press on the button.
public String someText(){
System.out.println("Works!!!");
return "Test if this method works";
}
}
One more question:
If i want my widget add data to my database, i have to use contentProvider?
You can't call a method directly, but you can fire an Intent. One way would do this would be to have an Intent send a broadcast, using PendingIntent.getBroadcast. (I'm not sure you need the category in there, but that's how I do that in my own code, so I'm including it in this example.)
Intent intent = new Intent("com.myapp.button_press").addCategory("com.myapp");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.widget_button, pendingIntent);
Next you need a BroadcastReceiver to receive that broadcast. One sneaky way to do that is to use your AppWidgetProvider class as the receiver, since that what an app widget really is. You would have to modify the manifest entry for your app widget to include the Intent that you've created in onUpdate:
<receiver android:name=".widget.MintAppWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="com.myapp.button_press"/>
<category android:name="com.myapp"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/my_provider_info"/>
</receiver>
Also, when you override onReceive, be sure to call super.onReceive if the Intent is not your special Intent, so that the base AppWidgetProvider class can process the normal intent and call onUpdate.
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if ("com.myapp.button_press".equals(action)) {
// First handle your special intent action
someText();
} else {
// otherwise let Android call onUpdate
super.onReceive(context, intent);
}
}

Android widget fails to start activity when clicked first time

I have an android widget which is actually a button to start an activity.The widget works fine using this code but there is a small bug :
When I add the widget to the home screen , clicking it first time doesn't do anything , second time it works. Then it works normally from 1st click. And the same thing happens when the phone is restarted.Can someone help me solve this problem ??
MyWidgetIntentReceiver.java
public class MyWidgetIntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("Start")) {
onUpdate(context);
}
}
private void onUpdate(Context context) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_demo);
Intent configIntent = new Intent(context, WidgetFlash.class);
configIntent.putExtra("widget", 1);
configIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent configPendingIntent = PendingIntent.getActivity(context,
0, configIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.widget_button,
configPendingIntent);
MyWidgetProvider.pushWidgetUpdate(context.getApplicationContext(),
remoteViews);
} }
MyWidgetProvider.java :
public class MyWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_demo);
remoteViews.setOnClickPendingIntent(R.id.widget_button, buildButtonPendingIntent(context));
pushWidgetUpdate(context, remoteViews);
}
public static PendingIntent buildButtonPendingIntent(Context context) {
Intent intent = new Intent();
intent.setAction("Start");
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static void pushWidgetUpdate(Context context, RemoteViews remoteViews) {
ComponentName myWidget = new ComponentName(context, MyWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(myWidget, remoteViews);
}}
Widget in Manifest:
<receiver android:name="com.hybernect.flashlight.MyWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/demo_widget_provider" />
</receiver>
<receiver
android:name="com.hybernect.flashlight.MyWidgetIntentReceiver"
android:label="widgetBroadcastReceiver" >
<intent-filter>
<action android:name="Start" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/demo_widget_provider" />
</receiver>
It looks to me like you might be very new to Android widgets, and are missing some of the basics that you need to know. My advice is to find some more tutorials other than the one you worked off, to compare different ways that widgets are handled.
I wrote a simple widget as a demo tutorial - it contains all the boilerplate code required for a widget, and very little else:
WiFi Widget Demo (github)
WiFi Widget (Play store)
I wrote it in such a way to make it easy for anyone to remove the "wifi" related code, and adapt it to their own widget requirements. It might be perfect for you to look at, and relatively simple to add a single button to it.
You are mixing up the different onUpdate() methods in your MyWidgetIntentReceiver and MyWidgetProvider. Below I will try to explain what is wrong with the code that you gave in your original question.
When you add the widget to the home screen, MyWidgetProvider.onUpdate() is called, which in turn calls buildPendingIntent():
public static PendingIntent buildButtonPendingIntent(Context context) {
Intent intent = new Intent();
intent.setAction("Start");
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
This creates a PendingIntent that will send a broadcast with action set to "Start", when the button is clicked.
And that is what happens. The button is clicked, you send a broadcast with "Start" in it, and this is received by your MyWidgetIntentReceiver.
I do not understand why you have chosen to do it this way, as you could rather just start the activity from this class.
When your MyWidgetIntentReceiver.onReceive() method runs, it calls its own onUpdate() method.
(Note: this is poor practice; try to do as little as possible within a BroadcastReceiver)
This new onUpdate() method sets the click action to launch the activity:
Intent configIntent = new Intent(context, WidgetFlash.class);
configIntent.putExtra("widget", 1);
configIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent configPendingIntent = PendingIntent.getActivity(context,
0, configIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.widget_button,
configPendingIntent);
And so the second time you click the button, the WidgetFlash activity is launched.
To summarise:
your first update calls PendingIntent.getBroadcast(), which sends "Start" to the receiver
your second update calls PendingIntent.getActivity(), which starts the WidgetFlash
I do not understand the need for your MyWidgetIntentReceiver in this code.

Switch status from android widget

I have an app with a widget that has a switch on it and a class that extends service to manage the content on the widget. I'm using RemoteViews to access the views to edit them but there are somethings that I want to do dependant on the sate of the switch. Is there a way to get the state of the switch? I've had a look through the api and couldn't find a way to do it.
Preemptively: I can't use findViewById because its not an activity
I can post code if needed.
For clarity the type of switch I want to use is one like this
Are you following the App Widgets Guide? I don't think the Switch widget is supported in RemoteViews.
Anyways, since you can't get any information on demand, you need to use setOnClickPendingIntent() and have the click send an intent to update your app widget. Stick an extra in the Intent specifying what state the button is in (or what state it would change to when clicked, either one). In the update logic, read the extra, change the UI, and set a new PendingIntent with the extra changed.
public class MyAppWidgetProvider extends AppWidgetProvider {
public static final String ACTION_UPDATE_SWITCH = "MyAppWidgetProvider.UPDATE_SWITCH";
public static final String EXTRA_SWITCH_ON = "MyAppWidgetProvider.EXTRA_SWITCH_ON";
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (ACTION_UPDATE_SWITCH.equals(action)) {
int id = intent.getIntExtra(AppWidgetManager.EXTRA_APPWDIGET_ID, 0);
if (id != 0) {
updateAppWidgetSwitch(context, intent, id);
}
}
super.onReceive(context, intent);
}
private void updateAppWidgetSwitch(Context context, Intent intent, int appWidgetId) {
boolean switchOn = intent.getBooleanExtra(EXTRA_SWITCH_ON, false);
// take some action based on the switch being clicked
RemoteViews views = new RemoteViews (context.getPackageName(), R.layout.app_widget_layout);
// normal RemoteViews stuff
// use switchOn var to set your switch state
// make new on click pending intent
Intent intent = new Intent(ACTION_UPDATE_SWITCH);
intent.putExtra(AppWidgetManager.EXTRA_APPWDIGET_ID, id);
intent.putExtra(EXTRA_SWITCH_ON, !switchOn); // new state
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.switch_id, pendingIntent);
// update widget
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
// other AppWidgetProvider methods ...
}
In your manifest, add this intent filter to your AppWidgetProvider's <receiver> element. Note that if you change the value of the action string above, make sure you change it here as well.
<intent-filter>
<action android:name="MyAppWidgetProvider.EXTRA_SWITCH_ON" />
</intent-filter>

Android Widgets - Reset on screen Orientation change

I have a widget on screen, a button click on widget enables/disables a broadcast reciver ... But when i turn the phone a bit.. all values on widget get reset and gives wrong info to user... I tried most of things to prevent this as below :
added android:configChanges="orientation|screenSize" in manifest
added this method in mainavtivity
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.widget_layout);
}
I hace also see suggestion to use onSaveInstanceState and all, But I cant get textview data from my widget from remoteviews approach to save it .. any other way
---- HERE IS THE CODE ----------
In Manifest...
<receiver android:name="MyWidgetProvider" >
<intent-filter >
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.makelifesimple.autopickmobiletracker.MyWidgetProvider.ACTION_WIDGET_ACTIVATE"/>
<action android:name="com.makelifesimple.autopickmobiletracker.MyWidgetProvider.ACTION_WIDGET_DEACTIVATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_info" />
</receiver>
<activity
android:name="com.makelifesimple.autopickmobiletracker.MainActivity"
android:configChanges="orientation|screenSize"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
--- in MyWidgetProvider ---
public class MyWidgetProvider extends AppWidgetProvider {
public static String ACTION_WIDGET_ACTIVATE = "ActivatePickup";
public static String ACTION_WIDGET_DEACTIVATE = "DeactivatePickup";
RemoteViews remoteViews;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {
Toast.makeText(context, "in onupdate", Toast.LENGTH_SHORT).show();
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
Intent active = new Intent(context, MyWidgetProvider.class);
active.setAction(ACTION_WIDGET_ACTIVATE);
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.button1, actionPendingIntent);
active = new Intent(context, MyWidgetProvider.class);
active.setAction(ACTION_WIDGET_DEACTIVATE);
actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.button2, actionPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
#Override
public void onEnabled(Context context){
Toast.makeText(context, "in enables", Toast.LENGTH_SHORT).show();
context.startService(new Intent(context, MyWidgetProvider.class));
}
#Override
public void onDisabled(Context context){
Toast.makeText(context, "in disable", Toast.LENGTH_SHORT).show();
context.stopService(new Intent(context, MyWidgetProvider.class));
}
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "in onRecive", Toast.LENGTH_SHORT).show();
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
if (intent.getAction().equals(ACTION_WIDGET_ACTIVATE)) {
ComponentName receiver = new ComponentName(context, PhoneCallReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,PackageManager.COMPONENT_ENABLED_STATE_ENABLED,PackageManager.DONT_KILL_APP);
//remoteViews.setTextViewText(R.id.button1,"ACTIVATED");
remoteViews.setTextViewText(R.id.textView2,"ACTIVE");
} else if (intent.getAction().equals(ACTION_WIDGET_DEACTIVATE)) {
ComponentName receiver = new ComponentName(context, PhoneCallReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);
remoteViews.setTextViewText(R.id.textView2,"INACTIVE");
Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 0); // 0 = unplugged 1 = Headset with microphone 2 = Headset without microphone
headSetUnPluggedintent.putExtra("name", "Headset");
context.sendOrderedBroadcast(headSetUnPluggedintent, null);
}
Intent active = new Intent(context, MyWidgetProvider.class);
active.setAction(ACTION_WIDGET_ACTIVATE);
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.button1, actionPendingIntent);
active = new Intent(context, MyWidgetProvider.class);
active.setAction(ACTION_WIDGET_DEACTIVATE);
actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.button2, actionPendingIntent);
ComponentName thisWidget = new ComponentName( context, MyWidgetProvider.class );
AppWidgetManager.getInstance( context ).updateAppWidget(thisWidget, remoteViews );
}
}
what am i missing here.......PLS PLSSSSSSSSSS HELP
It is pretty risky to rely on onUpdate in the widget provider to do more than catch the creation and deleting of the widgets. Everything else should be managed from elsewhere, typically a service, or a thread, started from onUpdate.
You then catch orientation change (through various method, whether from an activity or a Broadcast receiver that triggers the service or other thread) which will need to redo the code you have in onUpdate.
Other places to look:
A good tutorial is in: http://www.vogella.com/articles/AndroidWidgets/article.html, I would only quibble with one point in that tutorial: it's generally advised to not use updatePeriod.
There's a bit more detail in Who (and how) create an instance of AppWidgetProvider?.
Some thoughts on handling services, activities and widgets all in one app are in Service being re-Created by AlarmManager.
There are several errors in this code.
First, your onEnabled and onDisabled methods try to treat the AppWidgetProvider as a service. It isn't: it's a BroadcastReceiver, so these calls will fail, possibly terminating your app.
However, this won't happen. You've overridden onReceive without calling through to the super method, so none of the other methods will be called at all.
Fix these errors before you try to do anything else. Your onEnabled and onDisabled methods should be empty, and your onReceive should look like the following, without the duplicate widget-update code you have there now.
if (ACTION_WIDGET_ACTIVATE.equals(intent.getAction()) {
...
} else if (ACTION_WIDGET_DEACTIVATE.equals(intent.getAction()) {
...
} else {
super.onReceive(context, intent);
}
As posted here: After orientation change buttons on a widget are not responding you can avoid this by applying all widget changes on each update, including button click handlers.
For AppWidgets it is essential to implement onUpdate to redraw the widget's content.

Button click lost on widget when screen is rotated

I have a very simple widget application which consists of a LinearLayout with a background and an ImageButton.
In the AppWidgetProvider onUpdate() method, I register the click of the button to broadcast an intent. When the widget first loads, everything runs fine and the click is captured. The problem occurs when the screen is rotated, and the click is never captured again even if the screen is rotated back.
What do I have to do to re-register the click when the screen rotates?
below is some segments of code I am using.
AppWidgetProvider
#Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
if(intent.getAction().equals("test.CLICK"))
{
CallTestMethod(context);
}
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
final int N = appWidgetIds.length;
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
RemoteViews views=new RemoteViews(context.getPackageName(), R.layout.widget);
Intent clickintent=new Intent("test.CLICK");
PendingIntent pendingIntentClick=PendingIntent.getBroadcast(context, 0, clickintent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.change_mode, pendingIntentClick);
SetInitialLayout(context);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
Manifest
<receiver android:name=".Widget" android:label="#string/widget_name">
<intent-filter>
<action android:name="android.appwidget.action.ACTION_APPWIDGET_CONFIGURE" />
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="test.CLICK" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="#xml/widget_mode_switcher" />
</receiver>
Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/widget_layout"
android:layout_width="140dip"
android:layout_height="140dip"
android:scaleType="fitCenter"
android:orientation="vertical"
android:background="#drawable/background">
<ImageButton
android:id="#+id/change_mode"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
android:orientation="vertical"
android:src="#drawable/none_selected"
android:background="#null"
android:clickable="true" />
</LinearLayout>
Thank you anyone for your help!
This helped me: Android widget ImageButton loses image when screen is rotated
In short, you have to register the clicks (views.setOnClickPendingIntent) before EVERY call to awm.updateAppWidget
I used a solution which requires a service on the widgetapp because it can handle orientation changes to the widgetapp. Unfortunately onReceive or onUpdate doesn't get called by orientation changes, but the service's onConfigurationChanged does get called. You need to have your service constantly running to detect these orientations as well. When the service detects the orientation change, then you proceed to change the remote view.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
context.startService(new Intent(context, MyUpdateService.class));
}
This is the service class that you need to implement. You can look at this if you need more information about the service. http://android-developers.blogspot.com/2009/04/introducing-home-screen-widgets-and.html
public static class MyUpdateService extends Service
{
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
// Update the widget
RemoteViews remoteView = buildRemoteView(this);
// Push update to homescreen
pushUpdate(remoteView);
}
public RemoteViews buildRemoteView(Context context)
{
int layoutID = R.layout.widget;
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
layoutID = R.layout.widget_landscape;
}
//Here is where you set your onclick listeners again since the remote views need to be refreshed/recreated
RemoteViews updateView = new RemoteViews(context.getPackageName(),layoutID);
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(this, yourAndroidActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
intent, 0);
updateView.setOnClickPendingIntent(R.id.yourClickableViewHere, pendingIntent);
return updateView;
}
#Override
public void onConfigurationChanged(Configuration newConfig)
{
RemoteViews remoteView = buildRemoteView(this);
// Push update to home screen
pushUpdate(remoteView);
}
private void pushUpdate(RemoteViews updateViews)
{
ComponentName myWidget = new ComponentName(this, YourAppWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
//This is where you can update your remoteViews
updateViews.setTextViewText(R.id.YOUR_TEXTVIEW_ON_WIDGET, "" + "TEXT THAT WILL SHOW UP");
manager.updateAppWidget(myWidget, updateViews);
}
}
}
As I understand it, Android actually kills and recreates your activity every time the screen is rotated. Yuck, I know.
So anyway, I suspect if you put log statements in all the various lifecycle callbacks, you'll find that update is only called the one time. You probably need to handle listening for clicks in another callback. I couldn't tell you which one without checking some reference material. I'll leave that as an exercise to the reader.
hay have you use
android:configChanges="keyboardHidden|orientation"
with your Activity in Androidmanifest file.
I am new to android, but I am fairly certain that this way of doing it will work.
#Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
if(intent.getAction().equals("test.CLICK"))
{
getIntent().putExtra("Just received click", true);
CallTestMethod(context);
}
}
Then in the onCreate you can see if it should recreate the click event by checking getIntent().getBooleanExtra("Just received click", false) (false refers to the default value. If that code returns true, then the above code did it's job and you should recreate the click event)
This should work because the intent object (and it's extras) are saved, even if your app is temporarily killed. The intent will accept any Serializable extra.

Categories

Resources