1/* 2 * Copyright (C) 2010 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 */ 16package com.android.contacts.common.list; 17 18import android.content.AsyncTaskLoader; 19import android.content.Context; 20import android.content.pm.PackageManager; 21import android.database.ContentObserver; 22import android.database.Cursor; 23import android.database.MatrixCursor; 24import android.net.Uri; 25import android.os.Handler; 26import android.provider.ContactsContract.Directory; 27import android.text.TextUtils; 28import com.android.contacts.common.R; 29import com.android.dialer.common.LogUtil; 30import com.android.dialer.common.cp2.DirectoryCompat; 31import com.android.dialer.util.PermissionsUtil; 32 33/** A specialized loader for the list of directories, see {@link Directory}. */ 34public class DirectoryListLoader extends AsyncTaskLoader<Cursor> { 35 36 public static final int SEARCH_MODE_NONE = 0; 37 public static final int SEARCH_MODE_DEFAULT = 1; 38 public static final int SEARCH_MODE_CONTACT_SHORTCUT = 2; 39 public static final int SEARCH_MODE_DATA_SHORTCUT = 3; 40 // This is a virtual column created for a MatrixCursor. 41 public static final String DIRECTORY_TYPE = "directoryType"; 42 private static final String[] RESULT_PROJECTION = { 43 Directory._ID, DIRECTORY_TYPE, Directory.DISPLAY_NAME, Directory.PHOTO_SUPPORT, 44 }; 45 private final ContentObserver mObserver = 46 new ContentObserver(new Handler()) { 47 @Override 48 public void onChange(boolean selfChange) { 49 forceLoad(); 50 } 51 }; 52 private int mDirectorySearchMode; 53 private boolean mLocalInvisibleDirectoryEnabled; 54 private MatrixCursor mDefaultDirectoryList; 55 56 public DirectoryListLoader(Context context) { 57 super(context); 58 } 59 60 public void setDirectorySearchMode(int mode) { 61 mDirectorySearchMode = mode; 62 } 63 64 /** 65 * A flag that indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should be 66 * included in the results. 67 */ 68 public void setLocalInvisibleDirectoryEnabled(boolean flag) { 69 this.mLocalInvisibleDirectoryEnabled = flag; 70 } 71 72 @Override 73 protected void onStartLoading() { 74 if (PermissionsUtil.hasContactsReadPermissions(getContext())) { 75 getContext() 76 .getContentResolver() 77 .registerContentObserver(DirectoryQuery.URI, false, mObserver); 78 } else { 79 LogUtil.w("DirectoryListLoader.onStartLoading", "contacts permission not available."); 80 } 81 forceLoad(); 82 } 83 84 @Override 85 protected void onStopLoading() { 86 getContext().getContentResolver().unregisterContentObserver(mObserver); 87 } 88 89 @Override 90 public Cursor loadInBackground() { 91 if (mDirectorySearchMode == SEARCH_MODE_NONE) { 92 return getDefaultDirectories(); 93 } 94 95 MatrixCursor result = new MatrixCursor(RESULT_PROJECTION); 96 Context context = getContext(); 97 PackageManager pm = context.getPackageManager(); 98 String selection; 99 switch (mDirectorySearchMode) { 100 case SEARCH_MODE_DEFAULT: 101 selection = null; 102 break; 103 104 case SEARCH_MODE_CONTACT_SHORTCUT: 105 selection = Directory.SHORTCUT_SUPPORT + "=" + Directory.SHORTCUT_SUPPORT_FULL; 106 break; 107 108 case SEARCH_MODE_DATA_SHORTCUT: 109 selection = 110 Directory.SHORTCUT_SUPPORT 111 + " IN (" 112 + Directory.SHORTCUT_SUPPORT_FULL 113 + ", " 114 + Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY 115 + ")"; 116 break; 117 118 default: 119 throw new RuntimeException("Unsupported directory search mode: " + mDirectorySearchMode); 120 } 121 Cursor cursor = null; 122 try { 123 cursor = 124 context 125 .getContentResolver() 126 .query( 127 DirectoryQuery.URI, 128 DirectoryQuery.PROJECTION, 129 selection, 130 null, 131 DirectoryQuery.ORDER_BY); 132 133 if (cursor == null) { 134 return result; 135 } 136 137 while (cursor.moveToNext()) { 138 long directoryId = cursor.getLong(DirectoryQuery.ID); 139 if (!mLocalInvisibleDirectoryEnabled && DirectoryCompat.isInvisibleDirectory(directoryId)) { 140 continue; 141 } 142 String directoryType = null; 143 144 String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME); 145 int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID); 146 if (!TextUtils.isEmpty(packageName) && typeResourceId != 0) { 147 try { 148 directoryType = pm.getResourcesForApplication(packageName).getString(typeResourceId); 149 } catch (Exception e) { 150 LogUtil.e( 151 "ContactEntryListAdapter.loadInBackground", 152 "cannot obtain directory type from package: " + packageName); 153 } 154 } 155 String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME); 156 int photoSupport = cursor.getInt(DirectoryQuery.PHOTO_SUPPORT); 157 result.addRow(new Object[] {directoryId, directoryType, displayName, photoSupport}); 158 } 159 } catch (RuntimeException e) { 160 LogUtil.w( 161 "ContactEntryListAdapter.loadInBackground", "runtime exception when querying directory"); 162 } finally { 163 if (cursor != null) { 164 cursor.close(); 165 } 166 } 167 168 return result; 169 } 170 171 private Cursor getDefaultDirectories() { 172 if (mDefaultDirectoryList == null) { 173 mDefaultDirectoryList = new MatrixCursor(RESULT_PROJECTION); 174 mDefaultDirectoryList.addRow( 175 new Object[] {Directory.DEFAULT, getContext().getString(R.string.contactsList), null}); 176 mDefaultDirectoryList.addRow( 177 new Object[] { 178 Directory.LOCAL_INVISIBLE, 179 getContext().getString(R.string.local_invisible_directory), 180 null 181 }); 182 } 183 return mDefaultDirectoryList; 184 } 185 186 @Override 187 protected void onReset() { 188 stopLoading(); 189 } 190 191 private static final class DirectoryQuery { 192 193 public static final Uri URI = DirectoryCompat.getContentUri(); 194 public static final String ORDER_BY = Directory._ID; 195 196 public static final String[] PROJECTION = { 197 Directory._ID, 198 Directory.PACKAGE_NAME, 199 Directory.TYPE_RESOURCE_ID, 200 Directory.DISPLAY_NAME, 201 Directory.PHOTO_SUPPORT, 202 }; 203 204 public static final int ID = 0; 205 public static final int PACKAGE_NAME = 1; 206 public static final int TYPE_RESOURCE_ID = 2; 207 public static final int DISPLAY_NAME = 3; 208 public static final int PHOTO_SUPPORT = 4; 209 } 210} 211