38

I'm trying to do something which really ought to be quite easy, but it's driving me crazy. I'm trying to launch an activity when a home screen widget is pressed, such as a configuration activity for the widget. I think I've followed word for word the tutorial on the Android Developers website, and even a few unofficial tutorials as well, but I must be missing something important as it doesn't work.

Here is the code:

public class VolumeChangerWidget extends AppWidgetProvider {

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];

        Log.d("Steve", "Running for appWidgetId " + appWidgetId);
        Toast.makeText(context, "Hello from onUpdate", Toast.LENGTH_SHORT);
        Log.d("Steve", "After the toast line");

        Intent intent = new Intent(context, WidgetTest.class);

        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
        views.setOnClickPendingIntent(R.id.button, pendingIntent);

        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

}

When adding the widget to the homescreen, Logcat shows the two debugging lines, though not the Toast. (Any ideas why not?) However, more vexing is that when I then click on the button with the PendingIntent associated with it, nothing happens at all. I know the "WidgetTest" activity can run because if I set up an Intent from within the main activity, it launches fine.

In case it matters, here is the Android Manifest file:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.steve"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".Volume_Change_Program"
              android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity android:name=".WidgetTest"
              android:label="@string/hello">
        <intent_filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent_filter>
    </activity>

    <receiver android:name=".VolumeChangerWidget" >
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data  android:name="android.appwidget.provider"
                    android:resource="@xml/volume_changer_info" />
    </receiver>

</application>
<uses-sdk android:minSdkVersion="3" />

Is there a way to test where the fault is? I.e. is the fault that the button isn't linked properly to the PendingIntent, or that the PendingIntent or Intent isn't finding WidgetTest.class, etc?

Thanks very much for your help!

Steve

Steve Haley
  • 55,374
  • 17
  • 77
  • 85

9 Answers9

22

Bringing this way back from the dead, but I had a similar problem and I think I finally solved it... like you, I had a PendingIntent that I attached to the RemoteView. Sometimes it would work, and sometimes it would fail. It was driving me crazy.

What I found from a tooltip on the PendingIntent.getActivty() was:

Note that the activity will be started outside of the context of an existing activity, so you must use the Intent.FLAG_ACTIVITY_NEW_TASK launch flag in the Intent.

so, I added:

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

No example code I've seen so far does this, but it solved my problem; the Settings activity is now launched reliably.

The full code that's working well...

Intent intent = new Intent(context, Settings.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appId);  // Identifies the particular widget...
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Make the pending intent unique...
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent pendIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.wwwidget);
views.setOnClickPendingIntent(R.id.widget, pendIntent);
appWidgetManager.updateAppWidget(appId,views);
Brent Chartrand
  • 546
  • 1
  • 4
  • 10
10

I was having the same issue. I discovered that the fix is to call an update through the appwidget manager. here is an example of how to do that in onEnabled. It appears it needs to be done in both onEnabled and onUpdated so that when device is powering on your click intent is also intialized - in onUpdated the params already provide the reference to the manager, luckily.

@Override 
    public void onEnabled(Context context) {  
          //Log.v("toggle_widget","Enabled is being called"); 

          AppWidgetManager mgr = AppWidgetManager.getInstance(context); 
          //retrieve a ref to the manager so we can pass a view update 

          Intent i = new Intent(); 
          i.setClassName("yourdoman.yourpackage", "yourdomain.yourpackage.yourclass"); 
          PendingIntent myPI = PendingIntent.getService(context, 0, i, 0); 
          //intent to start service 

        // Get the layout for the App Widget 
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.togglelayout); 

        //attach the click listener for the service start command intent 
        views.setOnClickPendingIntent(R.id.toggleButton, myPI); 

        //define the componenet for self 
        ComponentName comp = new ComponentName(context.getPackageName(), ToggleWidget.class.getName()); 

        //tell the manager to update all instances of the toggle widget with the click listener 
        mgr.updateAppWidget(comp, views); 
} 
mylock
  • 559
  • 4
  • 17
  • Unfortunately no, I haven't worked out how to do this yet. It must be something obscure, as like you I read all the tutorials I could find, but never found something that worked. I just ended up shelving this project and started working on something else until I know how to use Android a bit better. – Steve Haley Jan 09 '10 at 11:33
  • Hey Steve. I think I've discovered the solution. I'm developing on 2.0.1. The key was moving this exact code into the onUpdated, and then calling one additional thing - a reference to AppWidgetManager comes in to the onUpdated via the params. ComponentName comp = new ComponentName(context.getPackageName(), ToggleWidget.class.getName()); appWidgetManager.updateAppWidget(comp, views); This version tells it to update all instances of the widget like this. Only problem now is making onEnabled call onUpdate once so it inits when user is adding widget to home. – mylock Jan 09 '10 at 19:52
  • 1
    +1 thanks. by adding the mgr.updateAppWidget i was able to finally get my widget to do the same. – nategood Jan 13 '10 at 23:51
  • Thanks for the suggestion! I'll give it a go once I finish this other project I'm working on. (Your code looks rather similar to one of the other things I tried, but I'm coding in v1.5 as that's the version on my phone. I hope this isn't an issue of v1.5 not working properly, as per some reports about various workarounds needed for AppWidgets in this version...) – Steve Haley Jan 15 '10 at 11:52
  • onEnabled() is totally irrelevant here. It isn't even called! – IgorGanapolsky May 02 '12 at 16:29
  • @mylock Can you please explain regarding togglewidget class. – Kimmi Dhingra Apr 22 '16 at 06:44
7

This worked for me, based on info here, the word widget sample, and the tutorial here

       Intent intent = new Intent(Intent.ACTION_MAIN, null);
      intent.addCategory(Intent.CATEGORY_LAUNCHER);
      // first param is app package name, second is package.class of the main activity
      ComponentName cn = new ComponentName("com....","com...MainActivity");
      intent.setComponent(cn);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
      PendingIntent myPI = PendingIntent.getActivity(context, 0, intent, 0); 

    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_word); 


    views.setOnClickPendingIntent(R.id.widget, myPI); 

    AppWidgetManager mgr = AppWidgetManager.getInstance(context); 
    mgr.updateAppWidget(comp, views); 
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
kdahlhaus
  • 480
  • 5
  • 8
4

The problem with the Toast not showing is easy, you don't call show(), a mistake I always do too... do

Toast.makeText(context, "Hello from onUpdate", Toast.LENGTH_SHORT).show();

instead of

Toast.makeText(context, "Hello from onUpdate", Toast.LENGTH_SHORT);
JohanS
  • 41
  • 1
0

I know this thread is ancient, but... Other answers describe your burnt Toast problem. As to why your pop-up activity doesn't launch on touch, you may need to enable the "update" action in order to launch and call your onUpdate() method. For that I think you need to add the "APPWIDGET_UPDATE" action like this:

<activity android:name=".WidgetTest" android:label="@string/hello">
    <intent_filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent_filter>
</activity>

Likewise add the APPWIDGET_ENABLED and APPWIDGET_DISABLED actions if you intend to override those methods too.

It seems to be a very unusual API that requires you to declare the overridden methods that you want called. The usual way to get your custom version of a parent is to simply override/implement them. Maybe there's a good reason for this strange pattern, but it is not a Java pattern that I've seen before. I therefore think it is likely to trip up a great deal of app widget authors. As if app widgets were not confusing enough without this mechanism.

Melinda Green
  • 2,100
  • 1
  • 21
  • 32
0

One additional point: The Activity that is called from the Widget needs to be declared in the Manifest file. No exception is thrown, just looks like nothing happens...

Stephan
  • 21
  • 1
0

When adding the widget to the homescreen, Logcat shows the two debugging lines, though not the Toast. (Any ideas why not?)

Don't try launching Toasts from a BroadcastReceiver.

Is there a way to test where the fault is?

Look at LogCat, via adb logcat, DDMS, or the DDMS perspective in Eclipse. You may find warnings about not finding an activity to match the given Intent.

I do not see any obvious problem. You may want to take a peek at one of my book examples and see if that works for you, and if it gives you any idea of what may be afoot.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • LogCat says this when the button is added: DEBUG/Steve(772): Running for appWidgetId 33 DEBUG/Steve(772): After the toast line DEBUG/Launcher(622): dumping extras content=Bundle[{appWidgetId=33}] WARN/InputManagerService(571): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@4388abe0 but then nothing at all when the button is pressed. Thanks for the example program, I'll look through and see if I can find some inidication of where I've gone wrong. – Steve Haley Dec 20 '09 at 23:09
  • Hmmm...it almost feels like it is not registering your click. Are you sure R.id.button is in your widget layout? – CommonsWare Dec 20 '09 at 23:20
  • I think it is. I've got a – Steve Haley Dec 20 '09 at 23:42
0

you must define your configuration activity in res/xml/volume_changer_info.xml. Add this tag and give a fully qualified path to the configuration activity.

android:configure = ""


e.g.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="200dip"
    android:minHeight="100dip"
    android:updatePeriodMillis="60000"
    android:initialLayout="@layout/widget_loading"
    android:configure = "org.raza.ConfigureWidgetActivity"/>
Steve Haley
  • 55,374
  • 17
  • 77
  • 85
Raza
  • 301
  • 1
  • 2
  • 9
  • Hi, I think we're talking about different things when we referred to a "configuration activity". I meant something that could be launched at any time after adding the widget rather than something that would be launched as you added the widget. This issue was solved a while ago but thanks for trying to help! – Steve Haley May 13 '10 at 09:46
-1

I just wanted to note this here somewhere as I have been (quite stupidly) battling this all night. Basically I did all the intent code correctly but nothing would be launched.

The problem was that I accidentally had a call like this

super.onUpdate(context, appWidgetManager, appWidgetIds);

at the end of my Overridden onUpdate() function.

Make sure you DO NOT have this call to super as it will clear out all pending intents you've set up!

Sverrir Sigmundarson
  • 2,453
  • 31
  • 27
  • 1
    That's strange as at least [in recent Android versions AppWidgetProvider.onUpdate() does nothing](http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/appwidget/AppWidgetProvider.java#AppWidgetProvider.onUpdate%28android.content.Context%2Candroid.appwidget.AppWidgetManager%2Cint[]%29). – sschuberth Aug 14 '12 at 08:25
  • Yeah it baffled me as well but removing that line was what did it for me :) – Sverrir Sigmundarson Aug 16 '12 at 08:40