Tuesday 14 June 2016

Android Widget Tutorial - Updating with AlarmManager

Widget is a small application (child) which is placed at home screen or lock screen. This is mostly used for quick access of application. Some of widget examples we mostly use are Music widget, cricket score, news, weather app etc. 
Widget is a part of application, so it preserves all the permissions of main application.
Widget UI is designed by using RemoteView, this view can be updated from the service. 
Here I am putting the sample widget application like cricket score app. This widget will display the live scores and will update the score in every 60 seconds. Refresh option is also given for manual refresh.
Steps to create Android Home Widget
1. Design a widget layout, widget_layout.xml .
2. Create a BroadcastReceiver MyWidget.java, which will handle the widget UI. 
3. Define an Alarm using AlarmManager for service update.
4. Create a service MyService.java for data sync update 
5. Register the receiver and service in AndroidManifest.xml .
1. Design a widget layout.
widget_layout.xml 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="50dp"
              android:orientation="vertical"
              android:layout_margin="4dp"
              android:background="#A9F5F2">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:weightSum="1">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_marginTop="15dp"
            android:layout_weight="0.33">

            <TextView android:id="@+id/widgetlabelUp"
                      android:layout_height="wrap_content"
                      android:layout_width="wrap_content"
                      android:textSize="10dp"
                      android:textColor="#848484"
                      android:layout_marginLeft="17dp"
                      android:text="Ind: 100/2"/>
            <TextView android:id="@+id/widgetlabelTemp"
                      android:layout_height="wrap_content"
                      android:layout_width="wrap_content"
                      android:layout_marginLeft="17dp"
                      android:textSize="10dp"
                      android:textColor="#848484"
                      android:text="Over: 15.1"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_marginTop="0dp"
            android:layout_weight="0.33">
            <ProgressBar
                android:id="@+id/progressBar"
                style="?android:attr/progressBarStyleSmall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"/>
            <TextView android:id="@+id/widgetlabelLowDc"
                      android:layout_height="wrap_content"
                      android:layout_width="wrap_content"
                      android:textSize="10dp"
                      android:textColor="#848484"
                      android:layout_marginLeft="10dp"
                      android:text="Sahwag: 55"/>
            <TextView android:id="@+id/widgetlabelBattery"
                      android:layout_height="wrap_content"
                      android:layout_width="wrap_content"
                      android:layout_marginLeft="10dp"
                      android:textSize="10dp"
                      android:textColor="#848484"
                      android:text="Sachin: 40"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_marginTop="0dp"
            android:layout_weight="0.33">
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
                <ImageView android:id="@+id/widgetlabelRef"
                           android:layout_width="wrap_content"
                           android:layout_height="wrap_content"
                           android:src="@drawable/refresh_icon"
                           android:layout_alignParentRight="true"
                           android:layout_alignParentTop="true"
                           android:layout_marginTop="2dp"
                           android:layout_marginRight="5dp"/>
            </RelativeLayout>
            <TextView android:id="@+id/widgetlabelCA"
                      android:layout_height="wrap_content"
                      android:layout_width="wrap_content"
                      android:textSize="10dp"
                      android:textColor="#848484"
                      android:layout_marginLeft="10dp"
                      android:layout_marginTop="2dp"
                      android:text="SA: 275/8"/>
            <TextView android:id="@+id/widgetlabelMA"
                      android:layout_height="wrap_content"
                      android:layout_width="wrap_content"
                      android:layout_marginLeft="10dp"
                      android:textSize="10dp"
                      android:textColor="#848484"
                      android:text="Over: 50"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

2. Create BroadcastReceiver, MyWidget and add RemotView.
Create class MyWidget.java by extending AppWidgetProvider and import the suggested packages.

public class MyWidget extends AppWidgetProvider {
}

Inside onUpdate of MyWidget.java, we need set out widget UI. We can set the clickListener for particular selected view.
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);

views.setOnClickPendingIntent(R.id.widgetlabelUp, pendingIntent);

3. Update widget data using AlarmManager
Set the widget updating time using AlarmManager. Here i have set for 60 seconds.
final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
final Calendar TIME = Calendar.getInstance();
TIME.set(Calendar.MINUTE0);
TIME.set(Calendar.SECOND0);
TIME.set(Calendar.MILLISECOND0);
final Intent in = new Intent(context, MyService.class);
if (service == null) {
    service = PendingIntent.getService(context, 0, in, PendingIntent.FLAG_CANCEL_CURRENT);
}
alarmManager.setRepeating(AlarmManager.RTC01000 60service);

Complete code for above two steps: 

MyWidget.java

import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;

public class MyWidget extends AppWidgetProvider {
    public static String ACTION_WIDGET_CONFIGURE "ConfigureWidget";
    public static String ACTION_WIDGET_RECEIVER "ActionReceiverWidget";
    private PendingIntent service null;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;
        // Perform this loop procedure for each App Widget that belongs to this provider
        for (int i = 0; i < N; i++) {
            int appWidgetId = appWidgetIds[i];
            Intent intent = new Intent(context, MainActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
            // Get the layout for the App Widget and attach an on-click listener to the button
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
            views.setOnClickPendingIntent(R.id.widgetlabelUp, pendingIntent);

           //set alarm
            final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            final Calendar TIME = Calendar.getInstance();
            TIME.set(Calendar.MINUTE0);
            TIME.set(Calendar.SECOND0);
            TIME.set(Calendar.MILLISECOND0);
            final Intent in = new Intent(context, MyService.class);
            if (service == null) {
                service = PendingIntent.getService(context, 0, in, PendingIntent.FLAG_CANCEL_CURRENT);
            }
            alarmManager.setRepeating(AlarmManager.RTC01000 120service);
            //onclick refresh
            views.setOnClickPendingIntent(R.id.widgetlabelRefservice);
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        final AlarmManager m = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        if (service != null) {
            m.cancel(service);
        }
    }
    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
    }
}

4. Sync data using service for widget update
You can call the background service to sync up json data using asynctask. Here is the code for service with asynctask. In onPost update the widget like this: 
manager.updateAppWidget(thisWidgetview);

MyService.java

package com.example.widgetmtoc;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.IBinder;
import android.widget.RemoteViews;

public class MyService extends Service {
    static RemoteViews view;

    @Override
    public void onCreate() {
        view new RemoteViews(getPackageName(), R.layout.widget_layout);
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new SyncData().execute();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    private class SyncData extends AsyncTask<String, Void, String> {
        HttpClient hc;
        HttpGet get;
        HttpResponse res;
        HttpEntity ent;
        int totalwicket;
        ComponentName thisWidget;
        AppWidgetManager manager;

        public SyncData() {
            thisWidget new ComponentName(MyService.this, MyWidget.class);
            manager = AppWidgetManager.getInstance(MyService.this);
        }

        @Override
        protected void onPreExecute() {
            view.setViewVisibility(R.id.progressBar0);
            view.setProgressBar(R.id.progressBar00false);
            manager.updateAppWidget(thisWidgetview);
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(String... params) {
            String url = "Set your service json get URL here";
            try {
                HttpParams httpParameters = new BasicHttpParams();
                HttpConnectionParams.setConnectionTimeout(httpParameters, 5000);
                HttpConnectionParams.setSoTimeout(httpParameters, 5000);
                hc new DefaultHttpClient(httpParameters);
                get new HttpGet(url);
                res hc.execute(get);
                ent res.getEntity();
                InputStream is = ent.getContent();
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                StringBuffer sb = new StringBuffer();
                String s = null;
                do {
                    s = br.readLine();
                    sb.append(s);
                } while (s != null);
                String result = sb.toString();
                try {
                    /*
                    * Parse your data here just like i did for sample, replace with your code
                    * */
                    JSONObject jo;
                    jo = new JSONObject(result);
                    JSONArray ja = jo.getJSONArray("Score");
                    JSONObject j = ja.getJSONObject(0);
                    total = j.getInt("total");
                    wicket = j.getInt("wicket");

                } catch (JSONException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return result;
            } catch (ClientProtocolException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String result) {
            /*
            * Update widget UI like below sample, replace with your code*/
            String lable1 = "India: " total "/" total;
            view.setTextViewText(R.id.widgetlabelUp, lable1);
            manager.updateAppWidget(thisWidgetview);

            stopSelf();
        }
    }
}


5. Configure AndroidManifest.xml
Set all the permissions and register the receiver and service.
AndroidManifest.xml 

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

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

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- Accessing Alarm -->
   <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
   <uses-permission android:name="android.permission.WAKE_LOCK"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <activity
            android:name="com.example.widgetmtoc.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name="MyWidget" android:label="mTOC">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                <action android:name="com.example.widgetmtoc.ACTION_WIDGET_RECEIVER"/>
            </intent-filter>    
            <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info"/>
        </receiver>

         <service android:enabled="true" android:name=".MyService" />
   
    </application>
</manifest>

So we all are set here for widget. Nothing to do in MainActivity.java for widget. I have left my MainActivity.java without any function. You can put your own application functions and codes.

public class MainActivity extends Activity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      /*
      * Add your application code and implementation here*/
   }
}

Run the application and close it. Now, go to the widget browser and select your widget, drag it to Home screen. You can see data syncing and updation. 

Above Lollipop, you will get the widget browser once you long tap on home screen. 


Please feel free to comment for any clarification or suggestion.
***

1 comment: