Ch12 Listings


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.ai.android.livefolders"
   android:versionCode="1"
   android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
   <activity android:name=".SimpleActivity"
      android:label="@string/app_name">
      <intent-filter>
         <action android:name="android.intent.action.MAIN" />
         <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
   </activity>

   <!-- LIVE FOLDERS -->
   <activity
      android:name=".AllContactsLiveFolderCreatorActivity"
      android:label="New live folder "
      android:icon="@drawable/icon">
      <intent-filter>
         <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
         <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
   </activity>
   <provider android:authorities="com.ai.livefolders.contacts"
      android:multiprocess="true"
      android:name=".MyContactsProvider" />
</application>
<uses-sdk android:minSdkVersion="3" />
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
</manifest>

public class AllContactsLiveFolderCreatorActivity extends Activity
{
   @Override
   protected void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      final Intent intent = getIntent();
      final String action = intent.getAction();
      if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
         setResult(RESULT_OK,
            createLiveFolder(MyContactsProvider.CONTACTS_URI,
                        "Contacts LF",
                        R.drawable.icon)
                        );
      }
      else {
         setResult(RESULT_CANCELED);
      }
      finish();
   }
   private Intent createLiveFolder(Uri uri, String name, int icon)
   {
      final Intent intent = new Intent();
      intent.setData(uri);
      intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);
      intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON,
      Intent.ShortcutIconResource.fromContext(this, icon));
      intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
                  LiveFolders.DISPLAY_MODE_LIST);
      return intent;
   }
}

public class MyContactsProvider extends ContentProvider {
   public static final String AUTHORITY = "com.ai.livefolders.contacts";

   //Uri that goes as input to the live-folder creation
   public static final Uri CONTACTS_URI = Uri.parse("content://" +
                                    AUTHORITY + "/contacts" );
   //To distinguish this URI
   private static final int TYPE_MY_URI = 0;
   private static final UriMatcher URI_MATCHER;
   static{
      URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
      URI_MATCHER.addURI(AUTHORITY, "contacts", TYPE_MY_URI);
   }

   @Override
   public boolean onCreate() {
      return true;
   }

   @Override
   public int bulkInsert(Uri arg0, ContentValues[] values) {
      return 0; //nothing to insert
   }

   //Set of columns needed by a live folder
   //This is the live-folder contract
   private static final String[] CURSOR_COLUMNS = new String[]
   {
      BaseColumns._ID,
      LiveFolders.NAME,
      LiveFolders.DESCRIPTION,
      LiveFolders.INTENT,
      LiveFolders.ICON_PACKAGE,
      LiveFolders.ICON_RESOURCE
   };

   //In case there are no rows
   //use this stand-in as an error message
   //Notice it has the same set of columns of a live folder
   private static final String[] CURSOR_ERROR_COLUMNS = new String[]
   {
      BaseColumns._ID,
      LiveFolders.NAME,
      LiveFolders.DESCRIPTION
   };

   //The error message row
   private static final Object[] ERROR_MESSAGE_ROW =
   new Object[]
   {
      -1, //id
      "No contacts found", //name
      "Check your contacts database" //description
   };

   //The error cursor to use
   private static MatrixCursor sErrorCursor = new MatrixCursor(CURSOR_ERROR_COLUMNS);
   static
   {
      sErrorCursor.addRow(ERROR_MESSAGE_ROW);
   }
   
   //Columns to be retrieved from the contacts database
   private static final String[] CONTACTS_COLUMN_NAMES = new String[]
   {
      People._ID,
      People.DISPLAY_NAME,
      People.TIMES_CONTACTED,
      People.STARRED
   };
   public Cursor query(Uri uri, String[] projection, String selection,
               String[] selectionArgs, String sortOrder)
   {
      //Figure out the uri and return error if not matching
      int type = URI_MATCHER.match(uri);
      if(type == UriMatcher.NO_MATCH)
      {
         return sErrorCursor;
      }
      Log.i("ss", "query called");
      try
      {
         MatrixCursor mc = loadNewData(this);
         mc.setNotificationUri(getContext().getContentResolver(),
         Uri.parse("content://contacts/people/"));
         MyCursor wmc = new MyCursor(mc,this);
         return wmc;
      }
      catch (Throwable e)
      {
         return sErrorCursor;
      }
   }
   public static MatrixCursor loadNewData(ContentProvider cp)
   {
      MatrixCursor mc = new MatrixCursor(CURSOR_COLUMNS);
      Cursor allContacts = null;
      try
      {
         allContacts = cp.getContext().getContentResolver().query(
                           People.CONTENT_URI,
                           CONTACTS_COLUMN_NAMES,
                           null, //row filter
                           null,
                           People.DISPLAY_NAME); //order by
         while(allContacts.moveToNext())
         {
            String timesContacted = "Times contacted: "+allContacts.getInt(2);
            Object[] rowObject = new Object[]
            {
               allContacts.getLong(0), //id
               allContacts.getString(1), //name
               timesContacted,
               //description
               Uri.parse("content://contacts/people/"
                     +allContacts.getLong(0)), //intent uri
                     cp.getContext().getPackageName(), //package
                     R.drawable.icon //icon
            };
            mc.addRow(rowObject);
         }
         return mc;
      }
      finally
      {
         allContacts.close();
      }
   }

   @Override
   public String getType(Uri uri)
   {
      //indicates the MIME type for a given URI
      //targeted for this wrapper provider
      //This usually looks like
      // "vnd.android.cursor.dir/vnd.google.note"
      return People.CONTENT_TYPE;
   }

   public Uri insert(Uri uri, ContentValues initialValues) {
      throw new UnsupportedOperationException(
               "no insert as this is just a wrapper");
   }

   @Override
   public int delete(Uri uri, String selection, String[] selectionArgs) {
      throw new UnsupportedOperationException(
               "no delete as this is just a wrapper");
   }

   public int update(Uri uri, ContentValues values,
                  String selection, String[] selectionArgs)
   {
      throw new UnsupportedOperationException(
               "no update as this is just a wrapper");
   }
}

private static final String[] CURSOR_COLUMNS = new String[]
{
   BaseColumns._ID,
   LiveFolders.NAME,
   LiveFolders.DESCRIPTION,
   LiveFolders.INTENT,
   LiveFolders.ICON_PACKAGE,
   LiveFolders.ICON_RESOURCE
};

MatrixCursor mc = loadNewData(this);
mc.setNotificationUri(getContext().getContentResolver(),
   Uri.parse("content://contacts/people/"));

MatrixCursor mc = loadNewData(this);
mc.setNotificationUri(getContext().getContentResolver(),
               Uri.parse("content://contacts/people/"));
MyCursor wmc = new MyCursor(mc,this);

public class MyCursor extends BetterCursorWrapper
{
   private ContentProvider mcp = null;
   public MyCursor(MatrixCursor mc, ContentProvider inCp)
   {
      super(mc);
      mcp = inCp;
   }
   public boolean requery()
   {
      MatrixCursor mc = MyContactsProvider.loadNewData(mcp);
      this.setInternalCursor(mc);
      return super.requery();
   }
}

public class BetterCursorWrapper implements CrossProcessCursor
{
   //Holds the internal cursor to delegate methods to
   protected CrossProcessCursor internalCursor;

   //Constructor takes a crossprocesscursor as an input
   public BetterCursorWrapper(CrossProcessCursor inCursor)
   {
      this.setInternalCursor(inCursor);
   }

   //You can reset in one of the derived class's methods
   public void setInternalCursor(CrossProcessCursor inCursor)
   {
      internalCursor = inCursor;
   }

   //All delegated methods follow
   public void fillWindow(int arg0, CursorWindow arg1) {
      internalCursor.fillWindow(arg0, arg1);
   }
   // ..... other delegated methods
}

public class SimpleActivity extends Activity
{
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
   }
}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
>
   <TextView
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Live Folder Example"
   />
</LinearLayout>