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.
http://www.coderzheaven.com/2012/06/15/placing-controls-widget-android-listening-events-them/
How to get Notified when a widget is deleted?
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.