Basic building blocks
- AppWidgetProviderInfo object
- AppWidgetProvider
- Widget View layout
AppWidgetProviderInfo object:
It provides meta-data about the widget for e.g widget initial layout, its min-height, min-width, update frequency and AppWidgetProvider class. It should be defined in xml file.
AppWidgetProvider:
It provides interfaces and ways to interact with the widget. The callback methods get called based on the broadcast events. These events get triggered as soon as the widget is added, removed, enabled, disabled or update interval lapses.
Widget ViewLayout:
View layout is basically written in the xml file. This is the initial layout of the widget. There are very few View components available. The RemoteView Object can support the following layout classes:
- FrameLayout
- GridLayout (Available on API 16)
- LinearLayout
- RelativeLayout
and following widget classes
- AnalogClock
- Button
- Chronometer
- FrameLayout
- ImageButton
- ImageView
- ProgressBar
- TextView
- ViewFlipper
- ViewStub (Available on API 16)
Platform Version : Android API Level 10.
IDE : Eclipse Helios Service Release 2
Emulator: Android 4.0.4
Once the dialog box appears, select the BlankActivity and click the next button.
Fill the Activity Name and Layout file name for the dialog box shown below and hit the finish button.
Content of widget_layout.xml file. You can ignore the “android:background” attribute field for LinearLayout and button, since this field has been used here to customize the background color and the size.
<linearlayout android:background="@drawable/background"
android:gravity="center" android:layout_height="match_parent"
android:layout_margin="4dp" android:layout_width="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<button android:background="@drawable/button_shape"
android:id="@+id/BtEnableDisable"
android:layout_height="wrap_content" android:layout_width="100dp"
android:text="@string/strWidgetBtEnableDisable"/>
</linearlayout>
<appwidget-provider android:initiallayout="@layout/widget_layout"
android:minheight="40dp" android:minwidth="130dp"
android:updateperiodmillis="0"
xmlns:android="http://schemas.android.com/apk/res/android">
</appwidget-provider>
In order to customize the widget background and shape you need to define the xml and include it in widget_layout.xml file. To create this file use Android XML file dialog as mentioned in the previous steps but for customization file select Resource Type as Drawable and Root element as Shape. You can put the following content in background.xml.
<shape android:id="@+id/Corners"
xmlns:android="http://schemas.android.com/apk/res/android">
<gradient android:angle="45" android:endcolor="#997f7f7f"
android:startcolor="#99111111"/>
<padding android:bottom="1dp" android:left="4dp"
android:right="4dp" android:top="1dp"/>
<corners android:radius="7dp"/>
<stroke android:color="#FFFFFFFF" android:width="2dp"/>
</shape>
<shape android:id="@+id/shapeBt"
xmlns:android="http://schemas.android.com/apk/res/android">
<gradient android:angle="45" android:endcolor="#FFFAFAFA"
android:startcolor="#FFFFFFFF"/>
<padding android:bottom="1dp" android:left="4dp"
android:right="4dp" android:top="1dp"/>
<corners android:radius="7dp"/>
<stroke android:color="#FFFFFFFF" android:width="2dp"/>
<size android:height="35dp" android:width="90dp"/>
</shape>
SimpleWidgetAppWigetProvider class extends AppWidgetProvider class. The methods defined in the SimpleWidgetAppWidgetProvider determine the behavior of the the widget. These methods get called based on the events triggered.
Methods present in AppWidgetProvider:
- onReceive(): This method gets called for every broadcast and before every other callback method present in AppWidgetProvider. In most of the cases you don’t need to implement this method.
- onEnabled(): This method gets called at the very first instance when the widget gets created. This method is similar to onCreate() method present in Activity class. If some initialization is required for your widget then this is the right place to do it.
- onDisabled(): This method gets called when the very last instance is removed from the container.
- onUpdate(): This method gets called to update the widget. It gets called even when you create an instance of widget, thus it makes some initial setup. This method gets called at interval defined by updatePeriodMillis attribute in widget-meta info.
- onDeleted(): As the name suggests, this method gets called whenever an instance is removed/deleted.
You will notice that Service gets invoked by startService() method call and passing intent object as a parameter in startService().
package com.rakesh.simplewidget;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
public class SimpleWidgetAppWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
ComponentName thisWidget = new ComponentName(context,
SimpleWidgetAppWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
// Build the intent to call the service
Intent intent = new Intent(context.getApplicationContext(),
UpdateWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
// Update the widgets via the service
context.startService(intent);
}
UpdateWidgetService class extends the service class. Here onStart() method has been defined which does the actual job(here we are trying to enable/disable the packet data) and then it tries to update the remote view with the help of updateAppWidget() method call. PendingIntent and setOnClickPendingIntent() method help to attach action listener to the button.
package com.rakesh.simplewidget;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.telephony.TelephonyManager;
import android.widget.RemoteViews;
public class UpdateWidgetService extends Service {
@Override
public void onStart(Intent intent, int startId) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this
.getApplicationContext());
int[] allWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
ComponentName thisWidget = new ComponentName(getApplicationContext(),
SimpleWidgetAppWidgetProvider.class);
for (int widgetId : allWidgetIds) {
RemoteViews remoteViews = new RemoteViews(this
.getApplicationContext().getPackageName(),
R.layout.widget_layout);
EnableDisableConnectivity edConn = new EnableDisableConnectivity(this.getApplicationContext());
edConn.enableDisableDataPacketConnection(!checkConnectivityState(this.getApplicationContext()));
// Register an onClickListener
Intent clickIntent = new Intent(this.getApplicationContext(),
SimpleWidgetAppWidgetProvider.class);
clickIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,
allWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, clickIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.BtEnableDisable, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
stopSelf();
super.onStart(intent, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private boolean checkConnectivityState(Context context){
final TelephonyManager telephonyManager = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
return telephonyManager.getDataState() == TelephonyManager.DATA_CONNECTED;
}
}
Since this tutorial is about how to update the widget when CONNECTIVITY_CHANGE intent is received, so here ConnectivityReceiver class has been defined which extends the BroadcastReceiver class. When application receives CONNECTIVITY_CHANGE intent it calls onReceive() method. In onReceive() method we try to get the remote view and then change the text and color of the button with the help of setTetViewText() and setTextColor() method respectively.
package com.rakesh.simplewidget;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;
public class ConnectivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
NetworkInfo info = (NetworkInfo)intent.getExtras().get(ConnectivityManager.EXTRA_NETWORK_INFO);
if(info.getType() == ConnectivityManager.TYPE_MOBILE){
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
if(info.isConnectedOrConnecting()){
Toast.makeText(context, "Data packet enabled", Toast.LENGTH_SHORT).show();
Log.d("RK","Mobile data is enabled");
remoteViews.setTextColor(R.id.BtEnableDisable, Color.GREEN);
remoteViews.setTextViewText(R.id.BtEnableDisable, "Enabled");
}else{
Toast.makeText(context, "Data packet disabled", Toast.LENGTH_SHORT).show();
Log.e("RK","Mobile data is disconnected");
remoteViews.setTextColor(R.id.BtEnableDisable, Color.BLACK);
remoteViews.setTextViewText(R.id.BtEnableDisable,"Disabled");
}
ComponentName thiswidget = new ComponentName(context, SimpleWidgetAppWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(thiswidget, remoteViews);
}
}
}
Below is the Android manifest file which defines the application components and their permissions.
<manifest android:versioncode="1" android:versionname="1.0"
package="com.rakesh.simplewidget"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minsdkversion="10"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application android:icon="@drawable/widget_icon"
android:label="@string/app_name">
<activity android:label="@string/app_name"
android:name=".SimpleWidgetExampleActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<receiver
android:icon="@drawable/widget_icon"
android:label="@string/app_name"
android:name=".SimpleWidgetAppWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE">
</action></intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget_metadata"/>
</receiver>
<service android:name=".UpdateWidgetService"/>
<receiver android:name=".ConnectivityReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
</meta-data>
</application>
</manifest>
You may have noticed that the AppwidgetProvider class has registered APPWIDGET_UPDATE intent. The class shown below is a helper class which basically enables/disables the packet data connection state. You can skip this part because it does not have much relevance to widget. But I have provided this code for those who may be interested in enabling/disabling packet data connection.
package com.rakesh.simplewidget;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.content.Context;
import android.net.ConnectivityManager;
import android.telephony.TelephonyManager;
import android.util.Log;
public class EnableDisableConnectivity {
private Context mContext;
public EnableDisableConnectivity(Context context){
this.mContext = context;
}
public boolean enableDataPacketConnection(){
return enableDisableDataPacketConnection(true);
}
public boolean disableDataPacketConnection(){
return enableDisableDataPacketConnection(false);
}
public boolean enableDisableDataPacketConnection(boolean enable){
boolean result = false;
Method dataConnSwitchmethod;
Class telephonyManagerClass;
Object ITelephonyStub;
Class ITelephonyClass;
final ConnectivityManager conman =
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
try{
final Class conmanClass = Class.forName(conman.getClass().getName());
final Field iConnectivityManagerField = conmanClass.getDeclaredField("mService");
iConnectivityManagerField.setAccessible(true);
final Object iConnectivityManager = iConnectivityManagerField.get(conman);
final Class iConnectivityManagerClass = Class.forName(iConnectivityManager.getClass().getName());
final Method setMobileDataEnabledMethod =
iConnectivityManagerClass.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE);
setMobileDataEnabledMethod.setAccessible(true);
setMobileDataEnabledMethod.invoke(iConnectivityManager, enable);
result = true;
}catch(Exception e){
Log.e("Error", "here is an exception "+e.getMessage());
result =false;
}
return result;
}
private boolean checkConnectivityState(){
final TelephonyManager telephonyManager = (TelephonyManager) mContext
.getSystemService(Context.TELEPHONY_SERVICE);
return telephonyManager.getDataState() == TelephonyManager.DATA_CONNECTED;
}
}
|
|
|
|---|
If you want to download the source code you can find it here. In next Widget tutorial we will learn how to use AlarmManger to update the widget if the periodicity of update is less than 30 mins.
Related tutorial:
Tutorial on Android Widget with AlarmManager
Tutorial on Android AlaramManager
Your valuable comments are always welcomed. It will help to improve my post and understanding.
“By three methods we may learn wisdom: First, by reflection, which is noblest; Second, by imitation, which is easiest; and third by experience, which is the bitterest.”
By : Confucius







Hi
Excellent article, Thanks