Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing Layout Items from inside Widget AppWidgetProvider

I am starting to go insane trying to figure this out. It seems like it should be very easy, I'm starting to wonder if it's possible.

What I am trying to do is create a home screen widget, that only contains an ImageButton. When it is pressed, the idea is to change some setting (like the wi-fi toggle) and then change the Buttons image.

I have the ImageButton declared like this in my main.xml

<ImageButton android:id="@+id/buttonOne"
        android:src="@drawable/button_normal_ringer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

my AppWidgetProvider class, named ButtonWidget

*** note that the RemoteViews class is a locally stored variable. this allowed me to get access to the RViews layout elements... or so I thought.

@Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {

    remoteViews = new RemoteViews(context.getPackageName(),
            R.layout.main);

    Intent active = new Intent(context, ButtonWidget.class);
    active.setAction(VIBRATE_UPDATE);
    active.putExtra("msg","TESTING");
    PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context,
            0, active, 0);
    remoteViews.setOnClickPendingIntent(R.id.buttonOne,
            actionPendingIntent);

    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();
    Log.d("onReceive",action);
    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(VIBRATE_UPDATE)) {
            String msg = "null";
            try {
                msg = intent.getStringExtra("msg");
            } catch (NullPointerException e) {
                Log.e("Error", "msg = null");
            }
            Log.d("onReceive",msg);
            if(remoteViews != null){

                Log.d("onReceive",""+remoteViews.getLayoutId());
                remoteViews.setImageViewResource(R.id.buttonOne, R.drawable.button_pressed_ringer);

                Log.d("onReceive", "tried to switch");
            }
            else{
                Log.d("F!", "--naughty language used here!!!--");
            }
        }
        super.onReceive(context, intent);
    }
}

so, I've been testing this and the onReceive method works great, I'm able to send notifications and all sorts of stuff (removed from code for ease of reading)

the one thing I can't do is change any properties of the view elements.

To try and fix this, I made RemoteViews a local and static private variable. Using log's I was able to see that When multiple instances of the app are on screen, they all refer to the one instance of RemoteViews. perfect for what I'm trying to do

The trouble is in trying to change the image of the ImageButton.

I can do this from within the onUpdate method using this.

remoteViews.setImageViewResource(R.id.buttonOne, R.drawable.button_pressed_ringer);

that doesn't do me any good though once the widget is created. For some reason, even though its inside the same class, being inside the onReceive method makes that line not work.

That line used to throw a Null pointer as a matter of fact, until I changed the variable to static. now it passes the null test, refers to the same layoutId as it did at the start, reads the line, but it does nothing.

Its like the code isn't even there, just keeps chugging along.

SO......

Is there any way to modify layout elements from within a widget after the widget has been created!? I want to do this based on the environment, not with a configuration activity launch.

I've been looking at various questions and this seems to be an issue that really hasn't been solved, such as link text and link text

oh and for anyone who finds this and wants a good starting tutorial for widgets, this is easy to follow (though a bit old, it gets you comfortable with widgets) .pdf link text

hopefully someone can help here. I kinda have the feeling that this is illegal and there is a different way to go about this. I would LOVE to be told another approach!!!!

Thanks

like image 379
cam4mav Avatar asked Nov 05 '22 10:11

cam4mav


1 Answers

You can redraw the screen using another layout with only the ImageButton modified. This sounds like a hackish solution but it works for me.

// Your appWidgetProvider class should track which layout it's currently on
private static int layoutId;

// onReceive should be something like this
@Override
public void onReceive(Context context, Intent intent) {
    Bundle bundle = intent.getExtras();

    if (bundle != null) {
        int newLayoutId = bundle.getInt("layout_id");
        // Change the current layout id to the layout id contained in the bundle
        layoutId = newLayoutId;
        initViews(context);
    } else {
        initViews(context);
    }
}

// Initialize the view according to the chosen layout
private void initViews(Context context) {
    RemoteViews views = null;

    // Set the initial layout   
    if (layoutId == 0) {
        Intent layoutIntent = new Intent("android.appwidget.action.APPWIDGET_UPDATE");
        Bundle layoutBundle = new Bundle();

        // I put in the layoutId of the next layout. Note that the layoutId is not
        // from R. I just made up some easy to remember number for my layoutId
        layoutBundle.putInt("layout_id", 1);

        PendingIntent lPendingIntent = PendingIntent.getBroadcast(context, 0,
                layoutIntent, PendingIntent.FLAG_ONE_SHOT);

        // Set the layout to the first layout
        views = new RemoteViews(context.getPackageName(), R.layout.layout_zero);

        // I used buttons to trigger a layout change
        views.setOnClickPendingIntent(R.id.btnNext, lPendingIntent);
    } // Else if there's some trigger to change the layout...
    else if (layoutId == 1) {
        Intent layoutIntent = new Intent("android.appwidget.action.APPWIDGET_UPDATE");
        Bundle layoutBundle = new Bundle();

        // Since I only have two layouts, I put here the id of the previous layout
        layoutBundle.putInt("layout_id", 0);

        PendingIntent lPendingIntent = PendingIntent.getBroadcast(context, 0,
                layoutIntent, PendingIntent.FLAG_ONE_SHOT);

        // Set the layout to the second layout
        // This layout should be almost the same as the first layout except for the
        // part that you want to change
        views = new RemoteViews(context.getPackageName(), R.layout.layout_one);
        views.setOnClickPendingIntent(R.id.btnPrev, lPendingIntent);
    }
}

As you can see, I used two different layouts which the user is able to switch via button clicks. You can use different triggers to change the layout depending on what you need. A similar solution can be found link text. Pardon me for putting the code in onReceive instead of onUpdate :)

like image 160
avendael Avatar answered Nov 11 '22 08:11

avendael