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

| July 23, 2013 | 18 Comments

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 (18)

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. 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 .

Leave a Reply to Langdon Himebaugh Cancel reply

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