Home | History | Annotate | Download | only in list
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.contacts.common.list;
     17 
     18 import android.content.Context;
     19 import android.content.CursorLoader;
     20 import android.database.Cursor;
     21 import android.database.MatrixCursor;
     22 import android.database.MergeCursor;
     23 import android.net.Uri;
     24 import android.os.Bundle;
     25 import android.provider.ContactsContract.Profile;
     26 
     27 import com.google.common.collect.Lists;
     28 
     29 import java.util.List;
     30 
     31 /**
     32  * A loader for use in the default contact list, which will also query for the user's profile
     33  * if configured to do so.
     34  */
     35 public class ProfileAndContactsLoader extends CursorLoader {
     36 
     37     private boolean mLoadProfile;
     38 
     39     private String[] mProjection;
     40 
     41     private Uri mExtraUri;
     42     private String[] mExtraProjection;
     43     private String mExtraSelection;
     44     private String[] mExtraSelectionArgs;
     45     private boolean mMergeExtraContactsAfterPrimary;
     46 
     47     public ProfileAndContactsLoader(Context context) {
     48         super(context);
     49     }
     50 
     51     /** Whether to load the profile and merge results in before any other results. */
     52     public void setLoadProfile(boolean flag) {
     53         mLoadProfile = flag;
     54     }
     55 
     56     public void setProjection(String[] projection) {
     57         super.setProjection(projection);
     58         mProjection = projection;
     59     }
     60 
     61     /** Configure an extra query and merge results in before the primary results. */
     62     public void setLoadExtraContactsFirst(Uri uri, String[] projection) {
     63         mExtraUri = uri;
     64         mExtraProjection = projection;
     65         mMergeExtraContactsAfterPrimary = false;
     66     }
     67 
     68     /** Configure an extra query and merge results in after the primary results. */
     69     public void setLoadExtraContactsLast(Uri uri, String[] projection, String selection,
     70             String[] selectionArgs) {
     71         mExtraUri = uri;
     72         mExtraProjection = projection;
     73         mExtraSelection = selection;
     74         mExtraSelectionArgs = selectionArgs;
     75         mMergeExtraContactsAfterPrimary = true;
     76     }
     77 
     78     private boolean canLoadExtraContacts() {
     79         return mExtraUri != null && mExtraProjection != null;
     80     }
     81 
     82     @Override
     83     public Cursor loadInBackground() {
     84         // First load the profile, if enabled.
     85         List<Cursor> cursors = Lists.newArrayList();
     86         if (mLoadProfile) {
     87             cursors.add(loadProfile());
     88         }
     89         if (canLoadExtraContacts() && !mMergeExtraContactsAfterPrimary) {
     90             cursors.add(loadExtraContacts());
     91         }
     92         // ContactsCursor.loadInBackground() can return null; MergeCursor
     93         // correctly handles null cursors.
     94         Cursor cursor = null;
     95         try {
     96             cursor = super.loadInBackground();
     97         } catch (NullPointerException | SecurityException e) {
     98             // Ignore NPEs and SecurityExceptions thrown by providers
     99         }
    100         final Cursor contactsCursor = cursor;
    101         cursors.add(contactsCursor);
    102         if (canLoadExtraContacts() && mMergeExtraContactsAfterPrimary) {
    103             cursors.add(loadExtraContacts());
    104         }
    105         return new MergeCursor(cursors.toArray(new Cursor[cursors.size()])) {
    106             @Override
    107             public Bundle getExtras() {
    108                 // Need to get the extras from the contacts cursor.
    109                 return contactsCursor == null ? new Bundle() : contactsCursor.getExtras();
    110             }
    111         };
    112     }
    113 
    114     /**
    115      * Loads the profile into a MatrixCursor. On failure returns null, which
    116      * matches the behavior of CursorLoader.loadInBackground().
    117      *
    118      * @return MatrixCursor containing profile or null on query failure.
    119      */
    120     private MatrixCursor loadProfile() {
    121         Cursor cursor = getContext().getContentResolver().query(Profile.CONTENT_URI, mProjection,
    122                 null, null, null);
    123         if (cursor == null) {
    124             return null;
    125         }
    126         try {
    127             MatrixCursor matrix = new MatrixCursor(mProjection);
    128             Object[] row = new Object[mProjection.length];
    129             while (cursor.moveToNext()) {
    130                 for (int i = 0; i < row.length; i++) {
    131                     row[i] = cursor.getString(i);
    132                 }
    133                 matrix.addRow(row);
    134             }
    135             return matrix;
    136         } finally {
    137             cursor.close();
    138         }
    139     }
    140 
    141     private Cursor loadExtraContacts() {
    142         return getContext().getContentResolver().query(
    143                 mExtraUri, mExtraProjection, mExtraSelection, mExtraSelectionArgs, null);
    144     }
    145 }
    146