Handling Multiple Instances of a Widget in Android and Identifying each instance with it’s own ID.

By | July 5, 2013

Hey everyone…….Hope all are well…..

This post is about Android Widgets.
This is going to be one of the bigger posts in Coderzheaven.

This posts helps you to handle multiple instances of a Widget.
Many of our problem is to identify different instances of the same widget.

But there is a solution to this problem.

Please check out my other posts in Coderzheaven to get an Idea of Android Widgets and it’s working.

Create new Widget


http://www.coderzheaven.com/2012/06/15/placing-controls-widget-android-listening-events-them/

How to get Notified when a widget is deleted?

Android Multiple Instance Widget

Android Multiple Instance Widget

Android Multiple Instance Widget

Android Multiple Instance Widget

Here is how we start.

First of all you need to keep these things about the Widget in Mind.

    1. Every instance of Same Widget is associated with a Unique ID.
    2. Widgets ID’s are sent through in the activities that has the “android.appwidget.action.APPWIDGET_CONFIGURE” intent-filter.
    3. RESULT_OK should sent back to the widget

Now we will start.

First we will look at the layouts.

This is the Widget Layout.
widget_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/widget_root"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:baselineAligned="false"
    android:orientation="vertical"
    android:background="@drawable/theme0" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Widget Text"
        android:gravity="center"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

Now the widgetprovider XML.

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="250dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="100000"
    android:configure="com.coderzheaven.multiplewidgetinstance.SettingsPage" 
    android:initialLayout="@layout/widget_main"
/> 

Please note that you have to use the exact package name with the class named you used as Configure Intent-filter in the “android:configure” property.

Now the XML for the settings Page.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="20dp"
    tools:context=".MainActivity" 
    android:background="@drawable/theme0">

    <TextView
        android:id="@+id/id_tv"
        android:paddingBottom="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:textStyle="bold"
        android:textSize="18dp"
        android:text="@string/hello_world" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/id_tv"
        android:ems="10" >

        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/button1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/editText1"
            android:textStyle="bold"
        android:text="Save" />

</RelativeLayout>

Please check the above screenshot how it looks like when clicking on the widget.
It is having an edittext in which we will enter the value for the widget.

Our all layout xml are now complete.
Now we will go to the java classes.

We will write the MyAppWidgetProvider class

MyAppWidgetProvider.java

package com.coderzheaven.multiplewidgetinstance;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;

public class MyAppWidgetProvider extends AppWidgetProvider {

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

	/*
	 * This is called when an instance the App Widget is created for the first
	 * time.
	 */
	@Override
	public void onEnabled(Context context) {
		super.onEnabled(context);
	}

	/*
	 * This is called for every broadcast and before each of the above callback
	 * methods.
	 */
	@Override
	public void onReceive(Context context, Intent intent) {
		super.onReceive(context, intent);
	}

	/*
	 * This is called When all instances of App Widget is deleted from the App
	 * Widget host.
	 */
	@Override
	public void onDisabled(Context context) {
		// Unschedule any timers and tasks
		super.onDisabled(context);
	}

	/*
	 * This is called every time an App Widget is deleted from the App Widget
	 * host.
	 */
	@Override
	public void onDeleted(Context context, int[] appWidgetIds) {
		// Unschedule any timers and tasks
		super.onDeleted(context, appWidgetIds);
	}
}

Please make sure you read the comments in this java, so I am providing any description for that.

Now the SettingsPage that edit the widget.

package com.coderzheaven.multiplewidgetinstance;

import android.app.Activity;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RemoteViews;
import android.widget.TextView;

public class SettingsPage extends Activity {

	int thisWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
	public static String ACTION_WIDGET_CONFIGURE = "WIDGET_CONFIGURED";
	SharedPreferences customSharedPreference;
	EditText ed = null;
	Button save = null;
	TextView widgetId = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		ed = (EditText) findViewById(R.id.editText1);
		save = (Button) findViewById(R.id.button1);
		widgetId = (TextView) findViewById(R.id.id_tv);

		save.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				updateWidget();
				if (ed.getText().toString().trim().length() > 0) {
					saveToPreferences("Widget" + thisWidgetId, ed.getText()
							.toString().trim());

					setResultDataToWidget(RESULT_OK);
				} else
					setResultDataToWidget(RESULT_CANCELED);
			}
		});

		getIdOfCurrentWidget(savedInstanceState);

	}

	/** Get the Id of Current Widget from the intent of the Widget **/
	void getIdOfCurrentWidget(Bundle savedInstanceState) {

		setResult(RESULT_CANCELED);

		Bundle extras = getIntent().getExtras();

		if (extras != null) {
			thisWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
					AppWidgetManager.INVALID_APPWIDGET_ID);
			if (getWidgetData("Widget" + thisWidgetId) != null) {
				save.setText("Update");
				ed.append(getWidgetData("Widget" + thisWidgetId));
			}

			widgetId.setText("Widget ID = " + thisWidgetId);
		}

		// If they gave us an intent without the widget id, just bail.
		if (thisWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
			finish();
		}

	}

	/**
	 * Update the Current Widget - This is very important to ensure the widget
	 * is enabled
	 **/
	void updateWidget() {
		AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
		RemoteViews remoteViews = new RemoteViews(getPackageName(),
				R.layout.widget_main);
		Intent clickIntent = getIntent();
		clickIntent.setAction(ACTION_WIDGET_CONFIGURE);
		remoteViews.setTextViewText(R.id.textView1, ed.getText().toString()
				.trim());
		clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, thisWidgetId);
		PendingIntent pendingIntent = PendingIntent.getActivity(this,
				thisWidgetId, clickIntent, 0);
		remoteViews.setOnClickPendingIntent(R.id.widget_root, pendingIntent);
		// update this widget
		appWidgetManager.updateAppWidget(thisWidgetId, remoteViews);
	}

	void setResultDataToWidget(int result) {
		Intent resultValue = new Intent();
		resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, thisWidgetId);
		setResult(result, resultValue);
		finish();
	}

	public void saveToPreferences(String file_name, String data) {
		SharedPreferences myPrefs = getSharedPreferences("Data",
				MODE_WORLD_WRITEABLE);
		SharedPreferences.Editor prefsEditor = myPrefs.edit();
		prefsEditor.putString(file_name, data);
		prefsEditor.commit();
	}

	public String getWidgetData(String file_name) {
		SharedPreferences myPrefs = getSharedPreferences("Data",
				MODE_WORLD_READABLE);
		return (myPrefs.getString(file_name, null));
	}

}

Now the AndroidManifest File.
Make sure you carefully read the Manifest files. Look at the intent filters in the activities and make sure that you have the correct package name in the provider xml file.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.coderzheaven.multiplewidgetinstance"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.coderzheaven.multiplewidgetinstance.SettingsPage"
            android:configChanges="keyboardHidden|orientation"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.Dialog">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>

        <!-- Broadcast Receiver that will process AppWidget Updates -->
        <receiver
            android:name="com.coderzheaven.multiplewidgetinstance.MyAppWidgetProvider"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@layout/my_widget_provider" />
        </receiver>
    </application>

</manifest>

Hooray you multiple instance code is complete.
Here we are saving data for each widget in the preferences with the widgetId as key with which we are reloading the data and editing it.

You can download the complete source code from here.