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