Android: Home screen Widgets: Use of Broadcast Intents? - android

I am trying to understand an app that communicates with a widget on the home screen. But i do not understand what the following code does within the application:
Intent i = new Intent(this, AppWidget.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.phoneState, pi);
return updateViews;
Full Class:
public class AppWidget extends AppWidgetProvider {
// This is called for every broadcast. We normally don't need to implement this
// method because the default AppWidgetProvider implementation filters all App Widget
// broadcasts and calls the above methods as appropriate
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == null) {
context.startService(new Intent(context, ToggleService.class));
} else {
super.onReceive(context, intent);
}
}
#Override
// This is called to update the App Widget at intervals defined by
// the updatePeriodMillis attribute in the AppWidgetProviderInfo. This method is also called when
// the user adds the App Widget, so it should perform the essential setup, such as define event
// handlers for Views and start a temporary Service, if necessary.
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
context.startService(new Intent(context, ToggleService.class));
}
// This class is used to set up the intent service in order to provide views for
// the widget. It also supports to set up a pending intent. Furthermore, the app widget can be
// updated with a remote adapter.
public static class ToggleService extends IntentService {
public ToggleService() {
super("AppWidget$ToggleService");
}
#Override
protected void onHandleIntent(Intent intent) {
ComponentName me = new ComponentName(this, AppWidget.class);
AppWidgetManager mgr = AppWidgetManager.getInstance(this);
mgr.updateAppWidget(me, buildUpdate(this));
}
private RemoteViews buildUpdate(Context context) {
RemoteViews updateViews = new RemoteViews(context.getPackageName(),
R.layout.widget);
AudioManager audioManager = (AudioManager) context
.getSystemService(Activity.AUDIO_SERVICE);
if (audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
updateViews.setImageViewResource(R.id.phoneState,
R.drawable.phone_state_normal);
audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
} else {
updateViews.setImageViewResource(R.id.phoneState,
R.drawable.phone_state_silent);
audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
}
//KNOW THIS CODE
Intent i = new Intent(this, AppWidget.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.phoneState, pi);
return updateViews;
}
}
}

All it does is open the ToggleService when the user taps on the viewId R.id.phoneState.
It's kinda of a silly way of doing it, the more straight forward would be:
Intent i = new Intent(this, ToggleService.class);
PendingIntent pi = PendingIntent.getService(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.phoneState, pi);
you see, this code you posted calls the BroadcastReceiver that then calls the service. Makes no sense.

Related

How to use Eventbus in a widget?

I'm trying to figure out how I can use the Greenbot Eventbus library in my AppWidgetProvider. I've tried the following, which doesn't work:
public class SimpleWidgetProvider extends AppWidgetProvider {
RemoteViews remoteViews;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int count = appWidgetIds.length;
for (int i = 0; i < count; i++) {
int widgetId = appWidgetIds[i];
remoteViews = new RemoteViews(context.getPackageName(), R.layout.simple_widget);
//set image
remoteViews.setImageViewResource(R.id.piggy_bank, R.drawable.piggy_bank);
Intent intent = new Intent(context, SimpleWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//set refresh button
remoteViews.setOnClickPendingIntent(R.id.refresh_btn, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
#Override
public void onEnabled(Context context) {
super.onEnabled(context);
EventBus.getDefault().register(this);
}
//set total price
#Subscribe
public void onPriceEvent(TotalPriceEvent event) {
double price = event.totalPrice;
remoteViews.setTextViewText(R.id.total_amount, String.valueOf(price));
}
#Override
public void onDisabled(Context context) {
EventBus.getDefault().unregister(this);
super.onDisabled(context);
}
}
Please, let me know if I need to attach more code.
An AppWidgetProvider is just a BroadcastReceiver with a specialized onReceive() method that delegates broadcasts to other methods based on the action. Instances of a manifest-registered BroadcastReceiver aren't meant to live very long. They run just long enough to handle a broadcast and then die, so subscribing one to an event bus isn't going to work as expected, and is kinda pointless, given the overlapping patterns. If you want to notify your SimpleWidgetProvider of something, just send a broadcast to it.
For an example, we define our own action for the SimpleWidgetProvider class, and check for it in the onReceive() method. If it's ours, we'll handle it as needed, and otherwise call the super method to allow AppWidgetProvider to properly delegate it.
public class SimpleWidgetProvider extends AppWidgetProvider {
public static final String MY_SPECIAL_ACTION = "com.mycompany.myapp.SPECIAL_ACTION";
#Override
public void onReceive(Context context, Intent intent) {
if(MY_SPECIAL_ACTION.equals(intent.getAction())) {
// Do your thing
}
else {
// Not our action, so let AppWidgetProvider handle it
super.onReceive(context, intent);
}
}
...
}
We can send a broadcast to it with the usual mechanism.
Intent widgetNotify = new Intent(context, SimpleWidgetProvider.class);
widgetNotify.setAction(SimpleWidgetProvider.MY_SPECIAL_ACTION);
widgetNotify.putExtra(...);
...
context.sendBroadcast(widgetNotify);
I would also mention that the super calls in onEnabled() and onDisabled() are unnecessary, as those methods are empty in AppWidgetProvider.

Settings Activity Not Launching

So far i have coded:
public class WidgetActivity extends AppWidgetProvider
{
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action))
{
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_activity);
Intent settingsIntent = new Intent(context, Info.class);
PendingIntent clickPendIntent = PendingIntent.getActivity
(context, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.Widget, clickPendIntent);
AppWidgetManager.getInstance(context).updateAppWidget(intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS), views);
}
}
}
To no success i click the widget when its on screen and nothing launches. Am I missing anything?
Put the code that makes sure the PendingIntent is set, in the onUpdate() method instead. This makes sure that as soon as the widget is put on the homescreen that the PendingIntent is set.
So:
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgets) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_activity);
Intent intent = new Intent(context, Info.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.Widget, pendingIntent);
Also:
If you would also like to have the info Activity pop up automatically when first adding the widget to the home screen, you should read this userful piece of information.

How do I trigger something from a widget button?

No matter what I do I cannot trigger anything by clicking on a button on a widget. Here is some code I wrote, can anyone tell me why onReceive isn't called when the widget button is clicked?
Furthermore, I want to run a function on button click... based on the code below do I have the right idea?
public class WidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Intent intent = new Intent(context, WidgetProvider.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
views.setOnClickPendingIntent(R.id.toggleButton, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds[0], views);
}
#Override
public void onReceive(Context context, Intent intent) {
// why don't i get here with the button click?
Log.e("!", intent.getAction());
}
}
Try to call the super method of onReceive first.
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
// why don't i get here with the button click?
Log.e("!", intent.getAction());
}
Worked just fine for me!

Android Widget: OnClickPendingIntent won't Work

Why doesn't recieve get called when I click on Button wid??
Code:
public class Widget extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int i = 0; i < appWidgetIds.length; i++) {
int appWidgetId = appWidgetIds[i];
Intent intent = new Intent(context, Widget.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
views.setOnClickPendingIntent(R.id.wid, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.d("ARH","CLICKK");
}
Becuase I need a button to manual refresh the widget but it seems that Log.d("ARH","CLICKK"); only gets called when i add the Widget.
Thanks!
You using a PendingIntent to call an Activity but your Widget class is not an activity.
If you want to update to your widget, then you need to use getBroadcast that sends an APPWIDGET_UPDATE action.

Widget double click

I have a widget (AppWidgetProvider) and i want to know if there is a way to support multiple clicks.Example:
1)If is the first click on the widget, then the ImageButton of the widget changes (for example, changes the color).
2)If is the second time, then open an Activity.
-- There is some way to handle click events inside AppWidgetProvider?
My code:
public class MyWidgetProvider extends AppWidgetProvider
{
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
Intent intent = new Intent(context, MyActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.control_widget);
views.setOnClickPendingIntent(R.id.asdf, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
My widget is working fine. When i click the ImageButton(R.id.asdf), it goes to the activity MyActivity.
Id like to know how can i handle click events on my widget to make a different action (example: change the color of the ImageButton) instead of go to some activity. Is there some other way to some click handle besides setOnClickPendingIntent()?
Maybe this could help. It works for me:
public class WidgetProvider extends AppWidgetProvider {
private static final int DOUBLE_CLICK_DELAY = 500;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
Intent intent = new Intent(context, getClass());
intent.setAction("Click");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.image, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, views);
context.getSharedPreferences("widget", 0).edit().putInt("clicks", 0).commit();
}
#Override
public void onReceive(final Context context, Intent intent) {
if (intent.getAction().equals("Click")) {
int clickCount = context.getSharedPreferences("widget", Context.MODE_PRIVATE).getInt("clicks", 0);
context.getSharedPreferences("widget", Context.MODE_PRIVATE).edit().putInt("clicks", ++clickCount).commit();
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
int clickCount = context.getSharedPreferences("widget", Context.MODE_PRIVATE).getInt("clicks", 0);
if (clickCount > 1) Toast.makeText(context, "doubleClick", Toast.LENGTH_SHORT).show();
else Toast.makeText(context, "singleClick", Toast.LENGTH_SHORT).show();
context.getSharedPreferences("widget", Context.MODE_PRIVATE).edit().putInt("clicks", 0).commit();
}
};
if (clickCount == 1) new Thread() {
#Override
public void run(){
try {
synchronized(this) { wait(DOUBLE_CLICK_DELAY); }
handler.sendEmptyMessage(0);
} catch(InterruptedException ex) {}
}
}.start();
}
super.onReceive(context, intent);
}
}
I did it this way:
1)If is the first click on the widget, then the ImageButton of the widget changes
2)If is the second time, then open an Activity and return to the inicial ImageButton state.
Im handling click events with setOnClickPendingIntent:
private int[] RESOURCES = {R.drawable.button1,R.drawable.button2};
#Override
public void onUpdate(Context context, AppWidgetManager mgr, int[] appWidgetIds) {
ComponentName me = new ComponentName(context, MyWidgetProvider.class);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.my_widget);
Intent widgetIntent = new Intent(context, MyWidgetProvider.class);
Intent myIntent= new Intent(context, MyOtherActivity.class);
widgetIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
widgetIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent;
if(clicks == 0)
{
clicks = 1;
remoteViews.setImageViewResource(R.id.my_image_button, RESOURCES[0]);
pendingIntent = PendingIntent.getBroadcast(context, 0, widgetIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
else if(clicks == 1)
{
clicks = 2;
remoteViews.setImageViewResource(R.id.my_image_button, RESOURCES[1]);
pendingIntent = PendingIntent.getActivity(context, 0, myIntent,0);
}
else //clicks == 2
{
clicks = 0;
remoteViews.setImageViewResource(R.id.my_image_button, RESOURCES[0]);
pendingIntent = PendingIntent.getBroadcast(context, 0, widgetIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
remoteViews.setOnClickPendingIntent(R.id.my_image_button, pendingIntent);
mgr.updateAppWidget(me, remoteViews);
}
#Override
public void onEnabled(Context context) {
clicks = 0;
super.onEnabled(context);
}
Unlike Button and its subclasses, AppWidget doesn't have the concept of an onClickListener. AppWidgets have to provide a PendingIntent for the application that hosts them to fire when the widget is clicked. If you wanted to track multiple clicks, you would need to have a receiver that filters on an intent specific to your widget and keeps track of how many times it has received it.
Slightly-less-relevant: You might reconsider your behavior model. Android is a one-click-to-take-action environment that doesn't have the same "click-once-to-select" concept like you'd find in, say, Windows. By emulating that behavior, your widget won't behave like all of the other UI elements and may cause confusion. Additionally, if there are two of your widgets on the screen and the user taps one and then the other, both will appear "selected," which probably isn't what you want.
Why not just count the clicks yourself?
private class MyAwesomeClickListener implements OnClickListener {
private int clicks = 0;
#Override
public void onClick(View v) {
++clicks;
//do some cool stuff
}
}
Don't count it in a listener (it's ridiculous, you don't know when your widget will be recycled from the memory and you will lose the value of int clicks)
Persist the number of clicks (add +1 each time it is clicked, restart to 0 when you reach the end of the click "cycle", the end of different click behaviors).
You could persist them in a database, with serialization, or with the shared preferences (I guess the preferences are the easiest way)
This is what I'm doing. Works beautifully =]
public class MyWidgetProvider extends AppWidgetProvider {
//Length of allowed time in between clicks in milliseconds
private static final long DOUBLE_CLICK_WINDOW = 400;
private static volatile long firstClickTimeReference;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
long currentSystemTime = System.currentTimeMillis();
if(currentSystemTime - firstClickTimeReference <= DOUBLE_CLICK_WINDOW) {
//double click happened in less than 400 miliseconds
//so let's start our activity
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity( intent );
} else {
firstClickTimeReference = currentSystemTime;
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.control_widget);
Intent intent = new Intent(context, WidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.actionButton, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
}
}
The solution by almisoft provides a double-click feel rather than simply successive clicks. And it works. Unfortunately you also get a lint message telling you that the 'handler class should be static or leaks might occur'. The solution is to use a weak reference and static handler - a generic version is here. Converting almisoft's code gives:
public class WidgetProvider extends AppWidgetProvider {
private static final int DOUBLE_CLICK_DELAY = 500;
private static Context mContext;
private static int mClickCount;
private static class MyHandler extends Handler {
//Using a weak reference means you won't prevent garbage collection
private final WeakReference<WidgetProvider> myClassWeakReference;
public MyHandler(WidgetProvider myClassInstance) {
myClassWeakReference = new WeakReference<WidgetProvider>(myClassInstance);
}
#Override
public void handleMessage(Message msg) {
WidgetProvider myWidget = myClassWeakReference.get();
if (myWidget != null) {
//...do work here as in almisoft original...
int clickCount = mContext.getSharedPreferences("widget", Context.MODE_PRIVATE).getInt("clicks", 0);
if (clickCount > 1) {
Toast.makeText(mContext, "doubleClick", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, "singleClick", Toast.LENGTH_SHORT).show();
}
mContext.getSharedPreferences("widget", Context.MODE_PRIVATE).edit().putInt("clicks", 0).commit();
}
}
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
Intent intent = new Intent(context, getClass());
intent.setAction("Click");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.image, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, views);
context.getSharedPreferences("widget", 0).edit().putInt("clicks", 0).commit();
}
#Override
public void onReceive(final Context context, Intent intent) {
mContext=context;
mIntent=intent;
if (intent.getAction().equals("Click")) {
int clickCount = context.getSharedPreferences("widget", Context.MODE_PRIVATE).getInt("clicks", 0);
context.getSharedPreferences("widget", Context.MODE_PRIVATE).edit().putInt("clicks", ++clickCount).commit();
final Handler handler=new MyHandler(this);
if (clickCount == 1) new Thread() {
#Override
public void run(){
try {
synchronized(this) { wait(DOUBLE_CLICK_DELAY); }
handler.sendEmptyMessage(0);
} catch(InterruptedException ex) {}
}
}.start();
}
super.onReceive(context, intent);
}
}

Categories

Resources