Category Archives: AppWidgets in Android

Part -4: Handling Multiple Instance of Same AppWidgetProvider


This app widget tutorial is with respect to the app widget present in Nepal Khabar present in the Google play store.

In the previous tutorials, I have written about
Basics of and App Widget
Multiple Sized Widdget
Handling User Interaction in Widgets
Now, I am going to write about Handling Multiple Instances of App Widget
To create multiple instance of an app widget, the most important factor is saving the widget id of a particular widget. When we update the app widget, then we must specify which widget we are updating through the app widget. To be able to distinguish between multiple instances of the same AppWidgetProvider, when registering the “onClick” event (intent) you must add an extra value with the widget ID (appWidgetId).
This can be done in following way:
Save the widget id in the preference.
How do you save the widget id in the preference?
Here is the code snippet that demonstrates how it can be done:

 public static void setWidgetId(Context context,int appWidgetId) {  
           Log.i("From set widget id", appWidgetId+"");  
           Editor editor = context.getSharedPreferences(  
                     PREFS_NAME_ID ,  
                     Context.MODE_PRIVATE).edit();  
           editor.putInt(PREFS_VALUE_ID, appWidgetId);  
           editor.commit();  
      }  

If you have any persitent data, which needs to stick in that particular widget, then save such data in SharedPrefernces.
But how to preserve their uniqueness while saving in shared preferences ?
Here is the code snippet that demonstrates how it can be done:

 public static void setWidgetNewsLanguage(Context context, String language,  
                int appWidgetId) {  
           Editor editor = context.getSharedPreferences(  
                     PREFS_NAME_LANGUAGE + String.valueOf(appWidgetId),  
                     Context.MODE_PRIVATE).edit();  
           editor.putString(PREFS_VALUE_LANGUAGE + String.valueOf(appWidgetId),  
                     language);  
           editor.commit();  
      }  

While updating the appwidget, how to handle the multiple instances of the same app widget
1. Get the widget id saved in shared preference
Here is the code snippet that demonstrates how it can be done:

      public static int getWidgetId(Context context) {  
           SharedPreferences sharedPreferences = context.getSharedPreferences(  
                     PREFS_NAME_ID ,Context.MODE_PRIVATE);  
           return sharedPreferences.getInt(  
                     PREFS_VALUE_ID , 0);  
      }  

2. Assign this widget Id to the given instance of app widget:

 int incomingAppWidgetId= getWidgetId(context);  
 if (incomingAppWidgetId != INVALID_APPWIDGET_ID) {  
           updateNewsAppWidget(appWidgetManager,incomingAppWidgetId, intent);  
      }  

3. Get persistent data related to this widget:

 public static String getWidgetNewsLanguage(Context context, int appWidgetId) {  
           SharedPreferences sharedPreferences = context.getSharedPreferences(  
                     PREFS_NAME_LANGUAGE + String.valueOf(appWidgetId),  
                     Context.MODE_PRIVATE);  
           return sharedPreferences.getString(  
                     PREFS_VALUE_LANGUAGE + String.valueOf(appWidgetId), null);  
      }  

4. Assign each pending intents, the retrieved widget ID:

 private void btnClick(RemoteViews views, int appWidgetId) {  
                Intent btnClickIntent = new Intent(this, this.getClass());  
                btnClickIntent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId);  
                btnClickIntent.setAction(ACTION_WIDGET_CLICK);  
                PendingIntent btnClickPendingIntent = PendingIntent.getService(  
                          this, appWidgetId, btnClickIntent,  
                          PendingIntent.FLAG_UPDATE_CURRENT);  
                views.setOnClickPendingIntent(R.id.widgetNewsCategoryTitle,  
                          btnClickPendingIntent);  
           }  

5. Upate the given widget, by passing the retrieved widget ID

 public void updateNewsAppWidget(AppWidgetManager appWidgetManager,  
                     int appWidgetId, Intent intent) {  
                RemoteViews views = new RemoteViews(this.getPackageName(),  
                          R.layout.layout_appwidget_large);  
                ArrayList<News> 
                initializeButtonClicks(appWidgetId);  
                appWidgetManager.updateAppWidget(appWidgetId, views);  
           }  

6. If you want to store arraylist with respect to the given widget, then store it in the hash value with key as widget id and value as newslist. Make the dataholder static in save it in a different data holder class.

 //In DataHolder class  
 public class DataHolder {  
 public static HashMap<Integer, ArrayList<News>> WIDGET_HASH_MAP = new HashMap<Integer, ArrayList<News>>();  
 }  

Hence, your app widget is now ready to handle its multiple instances.
Part -4 of App Widget, marks the end of app widget series. I will meet you in my blog through some other tutorials.
Until then, Happy Coding

Advertisements

Part -3: Handling User Interaction in App Widgets and Update Widgets Respectively


This app widget tutorial is with respect to the app widget present in Nepal Khabar present in the Google play store.

In the previous tutorials, I have written about

Basics of and App Widget
Multiple Sized Widdget

How to update an App Widget?

To update appwidget, we will use IntentService. IntentService is a special kind of service which performs an action and shuts itself down, once the action is complete. It is superb form small, periodic operations. IntentService can be used within the WidgetProvider class. The give code snippets will clarify this:

 public class OurWidget extends AppWidgetProvider {  
      public void onUpdate(Context context,  
                android.appwidget.AppWidgetManager appWidgetManager,  
                int[] appWidgetIds) {  
           int appWidgetId = INVALID_APPWIDGET_ID;  
           if (appWidgetIds != null) {  
                int N = appWidgetIds.length;  
                if (N == 1) {  
                     appWidgetId = appWidgetIds[0];  
                }  
           }  
           Intent intent = new Intent(context, UpdateWidgetService.class);  
           intent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId);  
           intent.setAction("DO NOTHING ACTION");  
           context.startService(intent);  
      }  
      /**  
       * static class does not need instantiation UpdateWidgetService is a Service  
       * that identifies the App Widgets , instantiates AppWidgetManager and calls  
       * updateAppWidget() to update the widget values  
       */  
      public static class UpdateWidgetService extends IntentService {  
           public UpdateWidgetService() {  
                super("UpdateWidgetService");  
           } 
@Override
		protected void onHandleIntent(Intent intent) {
			AppWidgetManager appWidgetManager = AppWidgetManager
					.getInstance(this);

			int incomingAppWidgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID,
					INVALID_APPWIDGET_ID);
			if (incomingAppWidgetId != INVALID_APPWIDGET_ID) {
				updateAppWidget(appWidgetManager, incomingAppWidgetId,
						intent.getAction());

			}
		} 
 }  

This service must me started from the onUpdate method of our AppWidgetProvider. onUpdate is called when the widget gets added to the homescreen.
onHandleIntent(): called each time IntentService starts. The service shuts down once we exit this method.
This service is declared in manifest as follows:

 <service android:name="OurWidget.UpdateWidgetService"/>  

Now, lets update our widget using onHandleIntent():
In order to handle user interaction with an App Widget, the following tasks must be performed:

    1 . Set a unique click handler for each App Widget control
    2. Have the click handler send a command to a registered receiver
    3. Process the command received and perform any action necessary
    4. Update the App Widget to reflect the changes

The following code snippets will clarify the above points:
The app widget is updated and user interaction is handled through the given class:

 public class OurWidget extends AppWidgetProvider {  
      public static String ACTION_WIDGET_TITLE_CLICK = "Action_widget_title_click";  
      public static String ACTION_WIDGET_CONTENT_CLICK = "Action_widget_conent_click";  
      /**  
       * Called in response to the ACTION_APPWIDGET_UPDATE broadcast when this  
       * AppWidget provider is being asked to provide RemoteViews for a set of  
       * AppWidgets. Override this method to implement your own AppWidget  
       * functionality.  
       */  
      public void onUpdate(Context context,  
                android.appwidget.AppWidgetManager appWidgetManager,  
                int[] appWidgetIds) {  
           int appWidgetId = INVALID_APPWIDGET_ID;  
           if (appWidgetIds != null) {  
                int N = appWidgetIds.length;  
                if (N == 1) {  
                     appWidgetId = appWidgetIds[0];  
                }  
           }  
           Intent intent = new Intent(context, UpdateWidgetService.class);  
           intent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId);  
           intent.setAction("DO NOTHING ACTION");  
           context.startService(intent);  
      }  
      /**  
       * static class does not need instantiation UpdateWidgetService is a Service  
       * that identifies the App Widgets , instantiates AppWidgetManager and calls  
       * updateAppWidget() to update the widget values  
       */  
      public static class UpdateWidgetService extends IntentService {  
           public UpdateWidgetService() {  
                super("UpdateWidgetService");  
           }  
           @Override  
           protected void onHandleIntent(Intent intent) {  
                AppWidgetManager appWidgetManager = AppWidgetManager  
                          .getInstance(this);  
                int incomingAppWidgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID,  
                          INVALID_APPWIDGET_ID);  
                if (incomingAppWidgetId != INVALID_APPWIDGET_ID) {  
                     updateOneAppWidget(appWidgetManager, incomingAppWidgetId,  
                               intent.getAction());  
                }  
           }  
           /**  
            * For the random passcode app widget with the provided ID, updates its  
            * display with a new passcode, and registers click handling for its  
            * buttons.  
            */  
           private void updateOneAppWidget(AppWidgetManager appWidgetManager,  
                     int appWidgetId, String whichButton) {  
                RemoteViews views = new RemoteViews(this.getPackageName(),  
                          R.layout.appwidget_layout);  
                onWidgetHeaderClick(views, appWidgetId);  
                onWidgetContentClick(views, appWidgetId);  
                bindDataToRemoteView(views, listOfQuedMessage.get(count));  
                //3. Process the command received and perform any action necessary  
                if (whichButton.equals(ACTION_WIDGET_VIEW_CLICK)) {  
                     views.setTextViewText(R.id.widgetHeader, "So you clicked Widget Title");                      
                } else if (whichButton.equals(ACTION_WIDGET_TITLE_VIEW_CLICK)) {  
                     views.setTextViewText(R.id.widgetContent, "So you clicked Widget Header");  
                }  
                appWidgetManager.updateAppWidget(appWidgetId, views);//4. Update the App Widget to reflect the changes  
           }  
           public void bindDataToRemoteView(RemoteViews views, Message message) {  
                views.setTextViewText(R.id.widgetHeader, "Widget Title");  
                views.setTextViewText(R.id.widgetContent, "Widget Header : Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh ");  
           }  
           //1. Set a unique click handler for each App Widget control  
           private void onWidgetHeaderClick(RemoteViews views, int appWidgetId) {  
                Intent btnNextIntent = new Intent(this, this.getClass());  
                btnNextIntent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId);  
                btnNextIntent.setAction(ACTION_WIDGET_TITLE_CLICK);  
                PendingIntent btnNextPendingIntent = PendingIntent.getService(this,  
                          0, btnNextIntent, PendingIntent.FLAG_UPDATE_CURRENT);  
                views.setOnClickPendingIntent(R.id.widgetHeader,  
                          btnNextPendingIntent);  
           }  
           private void onWidgetContentClick(RemoteViews views, int appWidgetId) {  
                Intent btnPrevIntent = new Intent(this, this.getClass());  
                btnPrevIntent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId);  
                btnPrevIntent.setAction(ACTION_WIDGET_CONTENT_CLICK); //2. Have the click handler send a command to a registered receiver  
                PendingIntent btnNextPendingIntent = PendingIntent.getService(this,  
                          0, btnPrevIntent, PendingIntent.FLAG_UPDATE_CURRENT);  
                views.setOnClickPendingIntent(R.id.widgetContent,  
                          btnNextPendingIntent);  
           }  
      }  
 }  

The layout for our App Widget:

 <?xml version="1.0" encoding="utf-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:id="@+id/full_widget"  
   android:layout_width="fill_parent"  
   android:layout_height="140dp"  
   android:layout_gravity="center_vertical"  
   android:layout_margin="20dp"  
   android:orientation="vertical" >  
   <TextView  
     android:id="@+id/widgetHeader"  
     android:layout_width="fill_parent"  
     android:layout_height="wrap_content"  
     android:background="@android:color/black"  
     android:paddingLeft="5dp"  
     android:paddingRight="5dp"  
     android:text="TEST"  
     android:textColor="#FFFFFF"  
     android:textSize="14sp"  
     android:textStyle="bold" />  
   <View  
     android:layout_width="fill_parent"  
     android:layout_height="1dp"  
     android:background="#FFFFFF" />  
   <TextView  
     android:id="@+id/widgetContent"  
     android:layout_width="fill_parent"  
     android:layout_height="wrap_content"  
     android:background="@android:color/black"  
     android:paddingLeft="5dp"  
     android:paddingRight="5dp"  
     android:text="Test"  
     android:textColor="#6F7981"  
     android:textSize="14sp"  
     android:textStyle="bold" />  
 </LinearLayout>  

The sample project is located in github.

My next tutorial will be about, Multiple instance of same app widget
References:
Styling Android
Developers Forum

Part -2: Creating Multiple Sized App Widgets


This app widget tutorial is with respect to the app widget present in Nepal Khabar present in the Google play store.

After App Widget Basics, lets learn how to add multiple sized app widget for our application. To create multiple sized appwidgets, we must create following things:

1.As we have defined appwidgetprovider receiver in manifest file in After App Widget Basics, add Receiver definition for small, medium and large receiver tag in manifest file
2. Create three app widget provider info, i.e. the xml file for small, medium and large app widget in res > xml file and include it in the meta-data of each appwidget provider
3. Create appwidgetprovider class for small, medium and large app widget

How to define receiver for multiple app widgets in receiver file ?
The following code snippets demonstrates how it can be done:
Receiver for large widget

  <receiver  
       android:name="WidgetProviderLarge"  
       android:icon="@drawable/ic_launcher"  
       android:label="@string/large_widget_name"  
        >  
       <intent-filter>  
         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  
       </intent-filter>  
       <meta-data  
         android:name="android.appwidget.provider"  
         android:resource="@xml/appwidgetproviderinfo_large" />  
     </receiver>  

Receiver for medium widget

  <receiver  
       android:name="WidgetProviderMedium"  
       android:icon="@drawable/ic_launcher"  
       android:label="large_medium"  
        >  
       <intent-filter>  
         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  
       </intent-filter>  
       <meta-data  
         android:name="android.appwidget.provider"  
         android:resource="@xml/appwidgetproviderinfo_medium" />  
     </receiver>  

Receiver for medium widget

  <receiver  
       android:name="WidgetProviderSmall"  
       android:icon="@drawable/ic_launcher"  
       android:label="small_widget" >  
       <intent-filter>  
         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  
       </intent-filter>  
       <meta-data  
         android:name="android.appwidget.provider"  
         android:resource="@xml/appwidgetproviderinfo_small" />  
     </receiver>   

While defining each appwidget receiver in manifest file, we must keep in mind

    android:name for each widget have to be different. If we provide same name to both the receivers then, only one widget will be visible in widgets list.
    we can use the same layout or different layout. Basically this is as per our requirement.

How to create appwidget provider info in xml for each app widget ?
The following code snippets demonstrates how it can be done:
app widgetprovider info for large widget in xml.

 <?xml version="1.0" encoding="utf-8"?>  
 <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  
   android:initialLayout="@layout/layout_appwidget_large"  
   android:minHeight="115dp"  
   android:minWidth="250dp"  
   android:updatePeriodMillis="21600000" ><!-- 6hrs -->  
 </appwidget-provider>  

app widgetprovider info for large widget in xml

 

How to create appwidget provider class for each app widget ?
The following code snippets demonstrates how it can be done:
app widgetprovider class for large widget

 public class WidgetProviderLarge extends AppWidgetProvider {   
  public void onUpdate(Context context, AppWidgetManager appWidgetManager,   
         int[] appWidgetIds) {   
       this.context = context;   
       Log.d("onUpdate", "called");   
       for (int widgetId : appWidgetIds) {   
         updateAppWidget(context, appWidgetManager, widgetId);   
       }   
    }   
 }  

app widgetprovider class for small widget

 public class WidgetProviderSmall extends AppWidgetProvider {   
  public void onUpdate(Context context, AppWidgetManager appWidgetManager,   
         int[] appWidgetIds) {   
       this.context = context;   
       Log.d("onUpdate", "called");   
       for (int widgetId : appWidgetIds) {   
         updateAppWidget(context, appWidgetManager, widgetId);   
       }   
    }   
 }  

Check out the sample project in github .

Notice: if you want to find the label of appwidget from your configuration activty(optiona), then the following code snippet will help you:

      AppWidgetProviderInfo providerInfo = AppWidgetManager.getInstance(  
                     getBaseContext()).getAppWidgetInfo(mAppWidgetId);  
           String appWidgetLabel = providerInfo.label;  

My next tutorial will be about Handling User Interaction in Widgets
Since then, happy coding 😀

Part -1: Creating a Simple App Widget with Configuration Actvitiy


This app widget tutorial is with respect to the app widget present in Nepal Khabar present in the Google play store.
While writing this tutorial, it is presumed that the readers will have basic knowledge about how to create and run a basic project in android and have some knowledge of java and xml file.

What is an app widget?

Technically, an app widget is a miniature application.In simple terms, the applications we see running on the home screen is app widget. These apps are in homescreen to show periodic updates of the data. These data must be helpful to the users.These app widgets are in the homescreen due to AppWidgetProvider.

What are the basic things to know about, when creating an app widget in your application?

So that you can easily create one app widget of your choice, i want to give a little bit of concept on the basics of app widget. I have listed out the required classes and xml file to create an app widget. So, go ahead, read it and create an app widget of your choice.

First off all, know what AppWidgetProvider is…….

AppWidgetProvider publishes app widget in the homescreen. It publishes app widget by allowing us (i.e. developers ) to interface with the app widget programmatically, based on broadcast events.It provides the broadcast when the App Widget is updated, enabled, disabled and deleted. It is defined in manifest as shown in the given snippet of code :

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

<action android:name =”android.appwidget.action.APPWIDGET_UPDATE” /> is responsible for updating the app widget.

android:name within meta-date specifies the metadata name. Use android.appwidget.provider to identify the data as the AppWidgetProviderInfo descriptor.

android:resource within meta-data defines the layout for the app widget,its update frequency. It is our AppWidgetProviderInfo file.

1. AppWidgetProviderInfo 

AppWidgetProvider must have the knowledge about the layout of the app widget, the update frequency of the app widget and it must also know whether any configuration activity( optional ) for app widget is defined. These informations are provided to the AppWidgetProvider through AppWidgetProviderInfo. The AppWidgetProviderInfo is xml file. It must be located inside res>xml folder. It is defined within android:resource in the receiver tag of manifest file. The AppWidgetProviderInfo xml file generally consist of the following attributes as shown in the given snippet of code :

 <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  
   android:configure="ConfigurationActivity"  
   android:initialLayout="@layout/layout_appwidget_large"  
   android:minHeight="115dp"  
   android:minWidth="250dp"  
   android:updatePeriodMillis="21600000" ><!-- 6hrs -->  
 </appwidget-provider>  

2. The app widget layout:

An app widget is different from normal Activity in Android. It runs on the home screen and there may be other app widgets running that we cannot interfere with. We must understand the difference between normal Activity and app widget. A normal Activity individual controls can draw outside of their own bounds, and even those of their parent layouts if they allow it via their clipChildren attribute. However doing this on the home screen could interfere with other widgets, and to prevent this kind of bad behaviour from app widgets, they do not get direct access to the app widget layout and it’s child views.

Instead they must use a RemoteViews object to update these views. RemoteViews is a proxy which provides us with limited access to these views, and consequently we have a limited subset of widgets that we can use in our app widget layouts such as AnalogClock, Button, Chronometer, ImageButton, ImageView, ProgressBar, TextView, ViewFlipper, ListView, GridView, StackView, AdapterViewFlipper. We are also limited to the following layout types: FrameLayout, LinearLayout, RelativeLayout, GridLayout

3. AppWidgetConfiguration Activity:

AppWidgetConfiguration Activity is an optional Activity that launches when the user adds our App Widget and allows him or her to modify App Widget settings at create-time.To transform an activity to a configuration one, we must provide <action android:name =”android.appwidget.action.APPWIDGET_UPDATE”> within <intent-filter> tag while defining it in manifest file.We must also include it in the appwidget provider info file as shown in the given code snippet

 <!-- app widget configuration activity in manifest -->  
     <activity android:name="ConfigurationActivity" >  
       <intent-filter>  
            <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />  
       </intent-filter>  
     </activity>  

Configuration Activity, is like any Activity, with two exceptions.

  • In onCreate() we have to ensure that if the user presses BACK or cancelled the activity, then we should not add app widget. This is done by adding
 setResult(RESULT_CANCELED);  
  • The result should include the App Widget ID passed by the Intent that launched the Activity(saved in the Intent extras as EXTRA_APPWIDGET_ID).
 private void initializeAppWidget(){  
      mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;  
           Intent intent = getIntent();  
           Bundle extras = intent.getExtras();  
           if (extras != null) {  
                mAppWidgetId = extras.getInt(EXTRA_APPWIDGET_ID,  
                          INVALID_APPWIDGET_ID);  
                showProgressDialog();  
                saveTheUserValueInPref(selectedCategory, sourceAndLanguage,  
                          mAppWidgetId);  
                getDataToLoadInWidget = new GetDataToLoadInWidget(  
                          ConfigurationActivity.this, selectedSource,  
                          selectedLanguage, selectedCategory);  
           }  
           if (mAppWidgetId == INVALID_APPWIDGET_ID) {  
                finish();  
           }  
           }  

Then, when you have finished configuration, return the app widget id:

   private void configureYourResult(){  

           //configure your activity   
           //after successful do as  

          Intent resultValue = new Intent();  
           resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);  
           setResult(RESULT_OK, resultValue);  
           finish();  
      }  

4. The AppWidgetProvider class :The AppWidgetProvider receives only the event broadcasts that are relevant to the App Widget, such as when the App Widget is updated, deleted, enabled, and disabled. When we fire an android.appwidget.action.APPWIDGET_UPDATE from ConfigurationActivity. 

 public class CopyOfWidgetProviderLarge extends AppWidgetProvider {  
      Context context;  
      public void onUpdate(Context context, AppWidgetManager appWidgetManager,  
                int[] appWidgetIds) {  
           this.context = context;  
           Log.d("onUpdate", "called");  
           for (int widgetId : appWidgetIds) {  
                updateAppWidget(context, appWidgetManager, widgetId);  
           }  
      }  

      private void updateAppWidget(Context context,  
                AppWidgetManager appWidgetManager, int appWidgetId) {  
           // Inflate layout.  
           RemoteViews remoteViews = new RemoteViews(context.getPackageName(),  
                     R.layout.widget);  
           // Update UI.  
           remoteViews.setTextViewText(R.id.label, "testing");  
           appWidgetManager.updateAppWidget(appWidgetId, views);  
      }  
 }  

The sample project is located in github. In the next tutorial, I am going to write about Multiple Sized Widdget. Until then, Happy Coding 😀

References:

Styling Android
Malubu WordPress
Android Developer Site