Building a Mobile Employee Directory – Step 7: Create an App Widget with it’s own ContentProvider & Service.

| July 23, 2013 | 20 Comments

For the source code relating to this post, checkout this Github repository.

Continuing with the mobile employee directory example, I’ve added three App Widgets to embed information on the phone’s main screen.

  1. Create EmployeeWidgetProvider (Changes every 30 minutes, with a random selection. Click to view Employee in DetailActivity.)
  2. Create EmployeeWidgetProvider2 & EmployeeWidgetService2 (Changes when clicked, with a random selection)
  3. Create EmployeeStackWidgetProvider & EmployeeStackWidgetService (Changes when scrolled. Click to view Employee in DetailActivity.)
  4. Create WidgetItem
  5. Create appwidget_background.xml  (in res/drawable)
  6. Create stack_widget_item_background.xml  (in res/drawable)
  7. Create preview1.png  (in res/drawable)
  8. Create preview2.png   (in res/drawable)
  9. Create stack_widget_item.xml  (in res/layout)
  10. Create stack_widget_layout.xml  (in res/layout)
  11. Create widget_layout.xml  (in res/layout)
  12. Create stack_widget.xml  (in res/xml)
  13. Create widget.xml  (in res/xml)
  14. Modify dimens.xml  (in res/values)
  15. Modify strings.xml  (in res/values)
  16. Modify AndroidManifest.xml

EmployeeWidgetProvider.java

package com.himebaugh.employeedirectory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Random;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.widget.RemoteViews;

public class EmployeeWidgetProvider extends AppWidgetProvider{

	private int mID;
	private String mName;
	private String mTitle;
	private String mDepartment;
	private String mPicture;

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

		// Get all ids
		ComponentName thisWidget = new ComponentName(context, EmployeeWidgetProvider.class);

		int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

		for (int widgetId : allWidgetIds) {

			// Select a random employee
			mID = (new Random().nextInt(11) + 1);

			Uri uri = ContentUris.withAppendedId(EmployeeProvider.CONTENT_URI, mID);
			String[] projection = { EmployeeDatabase.COLUMN_ID, EmployeeDatabase.COLUMN_FIRSTNAME, EmployeeDatabase.COLUMN_LASTNAME, EmployeeDatabase.COLUMN_TITLE, EmployeeDatabase.COLUMN_DEPARTMENT,
					EmployeeDatabase.COLUMN_CITY, EmployeeDatabase.COLUMN_OFFICE_PHONE, EmployeeDatabase.COLUMN_MOBILE_PHONE, EmployeeDatabase.COLUMN_EMAIL, EmployeeDatabase.COLUMN_PICTURE };
			String selection = null;
			String[] selectionArgs = null;
			String sortOrder = EmployeeDatabase.COLUMN_LASTNAME + " COLLATE LOCALIZED ASC";

			Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

			if (cursor != null && cursor.getCount() > 0) {
				cursor.moveToFirst();
				mName = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_LASTNAME)) + ", " + cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_FIRSTNAME));
				mTitle = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_TITLE));
				mDepartment = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_DEPARTMENT));
				mPicture = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_PICTURE));
			} else {
				mName = "empty cursor";
			}

			cursor.close();

			RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);

			// Set the text
			remoteViews.setTextViewText(R.id.appwidget_layout_name, mName);
			remoteViews.setTextViewText(R.id.appwidget_layout_title, mTitle);
			remoteViews.setTextViewText(R.id.appwidget_layout_department, mDepartment);

			InputStream is;
			try {
				is = context.getAssets().open("pics/" + mPicture);
				Bitmap bit = BitmapFactory.decodeStream(is);

				remoteViews.setImageViewBitmap(R.id.appwidget_layout_picture, bit);
			} catch (IOException e) {
				e.printStackTrace();
			}

			Uri details = Uri.withAppendedPath(EmployeeProvider.CONTENT_URI, "" + mID);
			Intent intent = new Intent(Intent.ACTION_VIEW, details);
			intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 				// ??

			PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

			remoteViews.setOnClickPendingIntent(R.id.appwidget_layout_text, pendingIntent);

			appWidgetManager.updateAppWidget(widgetId, remoteViews);
		}
	}

}

EmployeeWidgetProvider2.java

package com.himebaugh.employeedirectory;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class EmployeeWidgetProvider2 extends AppWidgetProvider {

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

		// Get all ids
		ComponentName thisWidget = new ComponentName(context, EmployeeWidgetProvider2.class);
		int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

		// Build the intent to call the service
		Intent intent = new Intent(context.getApplicationContext(), EmployeeWidgetService2.class);
		intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);

		// Update the widgets via the service
		context.startService(intent);
	}

}

EmployeeWidgetService2.java

package com.himebaugh.employeedirectory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Random;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.IBinder;
import android.widget.RemoteViews;

public class EmployeeWidgetService2 extends Service {

	private int mID;
	private String mName;
	private String mTitle;
	private String mDepartment;
	private String mPicture;

	@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(), EmployeeWidgetProvider2.class);
		int[] allWidgetIds2 = appWidgetManager.getAppWidgetIds(thisWidget);

		for (int widgetId : allWidgetIds) {

			// Select a random employee
			mID = (new Random().nextInt(11) + 1);

			Uri uri = ContentUris.withAppendedId(EmployeeProvider.CONTENT_URI, mID);
			String[] projection = { EmployeeDatabase.COLUMN_ID, EmployeeDatabase.COLUMN_FIRSTNAME, EmployeeDatabase.COLUMN_LASTNAME, EmployeeDatabase.COLUMN_TITLE, EmployeeDatabase.COLUMN_DEPARTMENT,
					EmployeeDatabase.COLUMN_CITY, EmployeeDatabase.COLUMN_OFFICE_PHONE, EmployeeDatabase.COLUMN_MOBILE_PHONE, EmployeeDatabase.COLUMN_EMAIL, EmployeeDatabase.COLUMN_PICTURE };
			String selection = null;
			String[] selectionArgs = null;
			String sortOrder = EmployeeDatabase.COLUMN_LASTNAME + " COLLATE LOCALIZED ASC";

			Cursor cursor = this.getApplicationContext().getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

			if (cursor != null && cursor.getCount() > 0) {
				cursor.moveToFirst();
				mName = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_LASTNAME)) + ", " + cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_FIRSTNAME));
				mTitle = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_TITLE));
				mDepartment = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_DEPARTMENT));
				mPicture = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_PICTURE));
			} else {
				mName = "empty cursor";
			}

			cursor.close();

			RemoteViews remoteViews = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.widget_layout);

			// Set the text
			remoteViews.setTextViewText(R.id.appwidget_layout_name, mName);
			remoteViews.setTextViewText(R.id.appwidget_layout_title, mTitle);
			remoteViews.setTextViewText(R.id.appwidget_layout_department, mDepartment);

			InputStream is;
			try {
				is = getAssets().open("pics/" + mPicture );
				Bitmap bit = BitmapFactory.decodeStream(is);

				remoteViews.setImageViewBitmap(R.id.appwidget_layout_picture, bit);
			} catch (IOException e) {
				e.printStackTrace();
			}			

			// Register an onClickListener
			Intent clickIntent = new Intent(this.getApplicationContext(), EmployeeWidgetProvider2.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.appwidget_layout_text, pendingIntent);
			appWidgetManager.updateAppWidget(widgetId, remoteViews);
			//

		}
		stopSelf();

		super.onStart(intent, startId);
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
}

EmployeeStackWidgetProvider.java

package com.himebaugh.employeedirectory;

import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.widget.RemoteViews;
import android.widget.Toast;

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class EmployeeStackWidgetProvider extends AppWidgetProvider {

	public static final String TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION";
	public static final String CLICK_ACTION = "com.himebaugh.employeedirectory.stackwidget.CLICK_ACTION";
	public static final String EXTRA_ITEM = "com.himebaugh.employeedirectory.stackwidget.EXTRA_ITEM";
	public static final String EMPLOYEE_ID = "com.himebaugh.employeedirectory.stackwidget.EMPLOYEE_ID";

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

	@Override
	public void onDisabled(Context context) {
		super.onDisabled(context);
	}

	@Override
	public void onEnabled(Context context) {
		super.onEnabled(context);
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		AppWidgetManager mgr = AppWidgetManager.getInstance(context);

		if (intent.getAction().equals(TOAST_ACTION)) {
			int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
			int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);
			Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();
		}

		if (intent.getAction().equals(CLICK_ACTION)) {
			int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
			int empID = intent.getIntExtra(EMPLOYEE_ID, 0);

			Uri details = Uri.withAppendedPath(EmployeeProvider.CONTENT_URI, "" + empID);
			Intent detailsIntent = new Intent(Intent.ACTION_VIEW, details);
			detailsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			context.startActivity(detailsIntent);
		}

		super.onReceive(context, intent);
	}

	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
		// update each of the widgets with the remote adapter
		for (int i = 0; i < appWidgetIds.length; ++i) {

			Intent intent = new Intent(context, EmployeeStackWidgetService.class);
			intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
			intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));

			RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.stack_widget_layout);
			rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);
			rv.setEmptyView(R.id.stack_view, R.id.empty_view);

			Intent clickIntent = new Intent(context, EmployeeStackWidgetProvider.class);
			clickIntent.setAction(EmployeeStackWidgetProvider.CLICK_ACTION);
			clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);

			intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));

			PendingIntent clickPendingIntent = PendingIntent.getBroadcast(context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);

			rv.setPendingIntentTemplate(R.id.stack_view, clickPendingIntent);

			appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
		}
		super.onUpdate(context, appWidgetManager, appWidgetIds);
	}
}

EmployeeStackWidgetService.java

package com.himebaugh.employeedirectory;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import android.annotation.TargetApi;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class EmployeeStackWidgetService extends RemoteViewsService {
	@Override
	public RemoteViewsFactory onGetViewFactory(Intent intent) {
		return new StackRemoteViewsFactory2(this.getApplicationContext(), intent);
	}
}

class StackRemoteViewsFactory2 implements RemoteViewsService.RemoteViewsFactory {
	// private static final int mCount = 10;
	private int mCount = 0;
	private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
	private Context mContext;
	private int mAppWidgetId;

	private int mID;
	private String mName;
	private String mTitle;
	private String mDepartment;
	private String mPicture;

	public StackRemoteViewsFactory2(Context context, Intent intent) {
		mContext = context;
		mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
	}

	public void onCreate() {

		Uri uri = EmployeeProvider.CONTENT_URI;
		String[] projection = { EmployeeDatabase.COLUMN_ID, EmployeeDatabase.COLUMN_FIRSTNAME, EmployeeDatabase.COLUMN_LASTNAME, EmployeeDatabase.COLUMN_TITLE, EmployeeDatabase.COLUMN_DEPARTMENT,
				EmployeeDatabase.COLUMN_CITY, EmployeeDatabase.COLUMN_OFFICE_PHONE, EmployeeDatabase.COLUMN_MOBILE_PHONE, EmployeeDatabase.COLUMN_EMAIL, EmployeeDatabase.COLUMN_PICTURE };
		String selection = null;
		String[] selectionArgs = null;
		String sortOrder = EmployeeDatabase.COLUMN_LASTNAME + " COLLATE LOCALIZED ASC";

		Cursor cursor = mContext.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

		for (int i = 0; i < cursor.getCount(); i++) {
			cursor.moveToNext();
			mID = cursor.getInt(cursor.getColumnIndex(EmployeeDatabase.COLUMN_ID));
			mName = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_LASTNAME)) + ", " + cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_FIRSTNAME));
			mTitle = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_TITLE));
			mDepartment = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_DEPARTMENT));
			mPicture = cursor.getString(cursor.getColumnIndex(EmployeeDatabase.COLUMN_PICTURE));

			mWidgetItems.add(new WidgetItem(mID, mName, mTitle, mDepartment, mPicture));
			mCount = mCount + 1;

		}

		cursor.close();

		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void onDestroy() {
		mWidgetItems.clear();
	}

	public int getCount() {
		return mCount;
	}

	public RemoteViews getViewAt(int position) {

		RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.stack_widget_item);
		rv.setTextViewText(R.id.stack_widget_layout_name, mWidgetItems.get(position)._name);
		rv.setTextViewText(R.id.stack_widget_layout_title, mWidgetItems.get(position)._title);
		rv.setTextViewText(R.id.stack_widget_layout_department, mWidgetItems.get(position)._department);

		Bundle extras = new Bundle();

		extras.putInt(EmployeeStackWidgetProvider.EMPLOYEE_ID, mWidgetItems.get(position)._empID);
		Intent fillInIntent = new Intent();
		fillInIntent.putExtras(extras);
		rv.setOnClickFillInIntent(R.id.stack_widget_layout_text, fillInIntent);

		InputStream is;
		try {
			System.out.println("Loading view " + position);

			is = mContext.getAssets().open("pics/" + mWidgetItems.get(position)._picture);
			Bitmap bit = BitmapFactory.decodeStream(is);

			rv.setImageViewBitmap(R.id.stack_widget_layout_picture, bit);

		} catch (IOException e) {
			e.printStackTrace();
		}

		return rv;
	}

	public RemoteViews getLoadingView() {
		return null;
	}

	public int getViewTypeCount() {
		return 1;
	}

	public long getItemId(int position) {
		return position;
	}

	public boolean hasStableIds() {
		return true;
	}

	public void onDataSetChanged() {

	}
}

WidgetItem.java

package com.himebaugh.employeedirectory;

public class WidgetItem {
	Integer _empID;
	String _name;
	String _title;
	String _department;
	String _picture;

    public WidgetItem(Integer empID, String name, String title, String department, String picture) {
		this._empID = empID;
		this._name = name;
		this._title = title;
		this._department = department;
		this._picture = picture;
    }
}

appwidget_background.xml

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <gradient
        android:angle="270"
        android:endColor="#77000000"
        android:startColor="#77000000" />

</shape>

stack_widget_item_background.xml

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <solid android:color="#FF000000" />

    <stroke
        android:width="1dp"
        android:color="#FFFFFFFF" />

    <padding
        android:bottom="1dp"
        android:left="0dp"
        android:right="0dp"
        android:top="1dp" />

</shape>

stack_widget_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/stack_widget_layout_text"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="0dip"
    android:background="@drawable/stack_widget_item_background"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/stack_widget_layout_picture"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/employee_photo"
        android:minHeight="50dp"
        android:minWidth="50dp"
        android:src="@drawable/ic_launcher" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:minHeight="65dp"
        android:minWidth="245dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/stack_widget_layout_name"
            style="@android:style/TextAppearance.Medium"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Loading..." />

        <TextView
            android:id="@+id/stack_widget_layout_title"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Loading2..." />

        <TextView
            android:id="@+id/stack_widget_layout_department"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:text="Loading3..." />

        <TextView
            android:id="@+id/stack_widget_layout_empid"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:visibility="gone" />
    </LinearLayout>

</LinearLayout>

stack_widget_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/widget_margin" >

    <StackView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/stack_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:loopViews="true" />

    <TextView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/empty_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/stack_widget_item_background"
        android:gravity="center"
        android:text="@string/empty_view_text"
        android:textColor="#ffffff"
        android:textSize="20sp"
        android:textStyle="bold" />

</FrameLayout>

widget_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/appwidget_layout_text"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="8dip"
    android:background="@drawable/appwidget_background" >

    <ImageView
        android:id="@+id/appwidget_layout_picture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:contentDescription="@string/employee_photo"
        android:minHeight="50dp"
        android:minWidth="50dp"
        android:src="@drawable/ic_launcher" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/appwidget_layout_name"
            style="@android:style/TextAppearance.Medium"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Loading..." />

        <TextView
            android:id="@+id/appwidget_layout_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Loading2..." />

        <TextView
            android:id="@+id/appwidget_layout_department"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:text="Loading3..." />
    </LinearLayout>

</LinearLayout>

stack_widget.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/stack_widget_layout"
    android:minHeight="64dp"
    android:minWidth="278dp"
    android:previewImage="@drawable/preview2"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="3600000"
    android:widgetCategory="keyguard|home_screen" >

</appwidget-provider>

widget.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget_layout"
    android:minHeight="64dp"
    android:minWidth="278dp"
    android:previewImage="@drawable/preview1"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="1800000"
    android:widgetCategory="keyguard|home_screen" >

</appwidget-provider>

dimens.xml

<resources>

    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>

    <dimen name="widget_margin">8dp</dimen>

</resources>

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Employee Directory</string>
    <string name="employee_details">Employee Details</string>
    <string name="employee_photo">Employee Photo</string>
    <string name="detail_header1">Call Office</string>
    <string name="detail_header2">Call Mobile</string>
    <string name="detail_header3">SMS</string>
    <string name="detail_header4">Email</string>
    <string name="place_holder">Text will be replaced</string>
    <string name="action_settings">Settings</string>
    <string name="menu_search">Search Quotes</string>
    <string name="search_hint">"Search employees" </string>
    <string name="search_instructions">Use the search box in the Action Bar to look up a word</string>
    <string name="search_settings_description">Where show???</string>
    <!-- Search failure message. -->
    <string name="no_results">No results found for \"%s\"</string>
    <string name="empty_view_text">Empty</string>

</resources>

AndroidManifest.xml

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

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

    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

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

        <!-- Main -->
        <activity
            android:name="com.himebaugh.employeedirectory.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>

        <!-- Detail -->
        <activity
            android:name="com.himebaugh.employeedirectory.DetailActivity"
            android:label="@string/employee_details" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />

                <data android:mimeType="vnd.android.cursor.item/vnd.himebaugh.search.employees" />
            </intent-filter>
        </activity>

        <!-- Searchable -->
        <activity
            android:name=".SearchableActivity"
            android:launchMode="singleTop" >
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
            </intent-filter>

            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
        </activity>

        <!-- Points to searchable activity so the whole app can invoke search. -->
        <meta-data
            android:name="android.app.default_searchable"
            android:value="com.himebaugh.employeedirectory.SearchableActivity" />

        <provider
            android:name="com.himebaugh.employeedirectory.EmployeeProvider"
            android:authorities="com.himebaugh.employeedirectory.EmployeeProvider"
            android:multiprocess="true" >
        </provider>

                <!-- App Widget 1 -->
        <receiver
            android:name=".EmployeeWidgetProvider"
            android:label="Employee Widget 1" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget" />
        </receiver>

        <!-- App Widget 2 w/ Service -->
        <receiver
            android:name=".EmployeeWidgetProvider2"
            android:label="Employee Widget 2" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget" />
        </receiver>

        <service android:name=".EmployeeWidgetService2" />

        <!-- App Widget 3 (StackWidget) -->
        <receiver
            android:name=".EmployeeStackWidgetProvider"
            android:label="Employee Widget 3" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/stack_widget" />
        </receiver>

        <service
            android:name=".EmployeeStackWidgetService"
            android:exported="false"
            android:permission="android.permission.BIND_REMOTEVIEWS" />
    </application>

</manifest>

 

Screenshots of the application

App-EmployeeDirectory-01

 

App-EmployeeDirectory-01

 

App-EmployeeDirectory-01

 

App-EmployeeDirectory-01

 

Directory Structure

Tags: , , , ,

Category: Android Application Development

Comments (20)

Trackback URL | Comments RSS Feed

  1. minh says:

    where are you getting the graphics for preview1.png, preview2.png, @drawable/sms, @drawable/phone, and @drawable/mail. Thanks

  2. minh says:

    For the employee photo, what path are you storing it into? Drawable/res?

  3. minh says:

    Hey, how come the imageview is controlled in “detail_activity.xml” but in the “employeestackwidgetservice.java”, the code points to rv.setImageViewBitmap(R.id.stack_widget_layout_picture, bit)? stack_widget_layout_picture is in “stack_widget_item.xml” I can’t find the portion of the code that points to “R.id.activity_detail_picture”

    • minh says:

      Ok found it. It’s in DetailActivity.java, mPicture = (ImageView) findViewById(R.id.activity_detail_picture);

      • Langdon Himebaugh says:

        Yes. As project got larger, I only posted new or changed files. DetailActivity.java & activity_detail.xml are in Step 4.

  4. Langdon Himebaugh says:

    The code has been place on GitHub. Download at https://github.com/langhimebaugh/EmployeeDirectory-Step-07

  5. ritz says:

    can u plz tell me how can i add any new employee to this app thru coding???

  6. Lucinda says:

    Hello my friend! I want to say that this post is awesome, great written and include
    approximately all important infos. I’d like to see extra posts like
    this .

  7. Sarang says:

    Hi,
    Thanks for the wonderful tutorial.Could u please share the final code of the project because there are so many steps /versions of the files??Also how to add organisational chart to it or may be “reporting to”button.Appreciate your quick help

Leave a Reply

Your email address will not be published. Required fields are marked *