172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov/* 272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project 372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * 472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); 572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * you may not use this file except in compliance with the License. 672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * You may obtain a copy of the License at 772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * 872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * 1072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 1172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, 1272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * See the License for the specific language governing permissions and 1472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * limitations under the License 1572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 1672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 1772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovpackage com.android.providers.contacts; 1872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 19e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DirectoryColumns; 2072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables; 2172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport com.google.android.collect.Lists; 22ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onukiimport com.google.android.collect.Sets; 23ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onukiimport com.google.common.annotations.VisibleForTesting; 2472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 2572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.ContentValues; 2672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.Context; 2772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.PackageInfo; 2872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.PackageManager; 2972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.PackageManager.NameNotFoundException; 3072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.ProviderInfo; 31e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.content.res.Resources; 32e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.content.res.Resources.NotFoundException; 3372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.database.Cursor; 3472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.database.sqlite.SQLiteDatabase; 3572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.net.Uri; 3672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.os.Bundle; 3772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.os.SystemClock; 38e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.provider.ContactsContract; 3972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.provider.ContactsContract.Directory; 40e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.text.TextUtils; 4172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.util.Log; 4272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 4372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport java.util.ArrayList; 4472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport java.util.List; 45ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onukiimport java.util.Set; 4672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 4772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov/** 4872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Manages the contents of the {@link Directory} table. 4972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 5043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro// TODO: Determine whether directories need to be aware of data sets under the account. 51bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovpublic class ContactDirectoryManager { 5272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 5372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private static final String TAG = "ContactDirectoryManager"; 54ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki private static final boolean DEBUG = false; // DON'T SUBMIT WITH TRUE 5572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 563d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov public static final String PROPERTY_DIRECTORY_SCAN_COMPLETE = "directoryScanComplete"; 573d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov public static final String CONTACT_DIRECTORY_META_DATA = "android.content.ContactDirectory"; 5872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 5972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public class DirectoryInfo { 6072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov long id; 6172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String packageName; 6272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String authority; 6372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String accountName; 6472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String accountType; 6572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String displayName; 6672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int typeResourceId; 6772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int exportSupport = Directory.EXPORT_SUPPORT_NONE; 6872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int shortcutSupport = Directory.SHORTCUT_SUPPORT_NONE; 69994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov int photoSupport = Directory.PHOTO_SUPPORT_NONE; 70ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki @Override 71ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki public String toString() { 72ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki return "DirectoryInfo:" 73ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + "id=" + id 74ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " packageName=" + accountType 75ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " authority=" + authority 76ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " accountName=***" 77ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " accountType=" + accountType; 78ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 7972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 8072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 8172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private final static class DirectoryQuery { 8272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final String[] PROJECTION = { 8372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.ACCOUNT_NAME, 8472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.ACCOUNT_TYPE, 8572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.DISPLAY_NAME, 8672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.TYPE_RESOURCE_ID, 8772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.EXPORT_SUPPORT, 8872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.SHORTCUT_SUPPORT, 89994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov Directory.PHOTO_SUPPORT, 9072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov }; 9172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 9272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int ACCOUNT_NAME = 0; 9372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int ACCOUNT_TYPE = 1; 9472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int DISPLAY_NAME = 2; 9572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int TYPE_RESOURCE_ID = 3; 9672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int EXPORT_SUPPORT = 4; 9772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int SHORTCUT_SUPPORT = 5; 98994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov public static final int PHOTO_SUPPORT = 6; 9972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 10072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 10172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private final ContactsProvider2 mContactsProvider; 102ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki private final Context mContext; 103ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki private final PackageManager mPackageManager; 10472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 10572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public ContactDirectoryManager(ContactsProvider2 contactsProvider) { 106ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki mContactsProvider = contactsProvider; 107ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki mContext = contactsProvider.getContext(); 108ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki mPackageManager = mContext.getPackageManager(); 10972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 11072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 111e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov public ContactsDatabaseHelper getDbHelper() { 112e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return (ContactsDatabaseHelper) mContactsProvider.getDatabaseHelper(); 113e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 114e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 11572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 11672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Scans all packages owned by the specified calling UID looking for contact 11772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * directory providers. 11872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 11972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public void scanPackagesByUid(int callingUid) { 120ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final String[] callerPackages = mPackageManager.getPackagesForUid(callingUid); 12172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (callerPackages != null) { 12272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov for (int i = 0; i < callerPackages.length; i++) { 12372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov onPackageChanged(callerPackages[i]); 12472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 12572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 12672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 12772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 12872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 129e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * Scans through existing directories to see if the cached resource IDs still 130e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * match their original resource names. If not - plays it safe by refreshing all directories. 131e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * 132e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * @return true if all resource IDs were found valid 133e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov */ 134e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov private boolean areTypeResourceIdsValid() { 135e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov SQLiteDatabase db = getDbHelper().getReadableDatabase(); 136e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 137e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov Cursor cursor = db.query(Tables.DIRECTORIES, 138e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov new String[] { Directory.TYPE_RESOURCE_ID, Directory.PACKAGE_NAME, 139e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov DirectoryColumns.TYPE_RESOURCE_NAME }, null, null, null, null, null); 140e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov try { 141e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov while (cursor.moveToNext()) { 142e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov int resourceId = cursor.getInt(0); 143e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov if (resourceId != 0) { 144e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov String packageName = cursor.getString(1); 145e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov String storedResourceName = cursor.getString(2); 146ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki String resourceName = getResourceNameById(packageName, resourceId); 147e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov if (!TextUtils.equals(storedResourceName, resourceName)) { 148e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return false; 149e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 150e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 151e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 152e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } finally { 153e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov cursor.close(); 154e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 155e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 156e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return true; 157e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 158e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 159e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov /** 160e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * Given a resource ID, returns the corresponding resource name or null if the package name / 161e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * resource ID combination is invalid. 162e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov */ 163ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki private String getResourceNameById(String packageName, int resourceId) { 164e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov try { 165ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Resources resources = mPackageManager.getResourcesForApplication(packageName); 166e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return resources.getResourceName(resourceId); 167e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } catch (NameNotFoundException e) { 168e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return null; 169e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } catch (NotFoundException e) { 170e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return null; 171e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 172e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 173e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 174e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov /** 175afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov * Scans all packages for directory content providers. 17672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 177bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov public void scanAllPackages(boolean rescan) { 178bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov if (rescan || !areTypeResourceIdsValid()) { 179bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov getDbHelper().setProperty(PROPERTY_DIRECTORY_SCAN_COMPLETE, "0"); 180bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov } 181bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov 182bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov scanAllPackagesIfNeeded(); 183bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov } 184bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov 18572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private void scanAllPackagesIfNeeded() { 186e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov String scanComplete = getDbHelper().getProperty(PROPERTY_DIRECTORY_SCAN_COMPLETE, "0"); 18772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!"0".equals(scanComplete)) { 18872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov return; 18972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 19072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 19172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov long start = SystemClock.currentThreadTimeMillis(); 19272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int count = scanAllPackages(); 193e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov getDbHelper().setProperty(PROPERTY_DIRECTORY_SCAN_COMPLETE, "1"); 19472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov long end = SystemClock.currentThreadTimeMillis(); 19572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.i(TAG, "Discovered " + count + " contact directories in " + (end - start) + "ms"); 19672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 19772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // Announce the change to listeners of the contacts authority 19872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov mContactsProvider.notifyChange(false); 19972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 20072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 201ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki @VisibleForTesting 202ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki static boolean isDirectoryProvider(ProviderInfo provider) { 203ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Bundle metaData = provider.metaData; 204ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (metaData == null) return false; 205ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 206ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Object trueFalse = metaData.get(CONTACT_DIRECTORY_META_DATA); 207ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki return trueFalse != null && Boolean.TRUE.equals(trueFalse); 208ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 209ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 210ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki /** 211ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki * @return List of packages that contain a directory provider. 212ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki */ 213ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki @VisibleForTesting 214ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki static Set<String> getDirectoryProviderPackages(PackageManager pm) { 215ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final Set<String> ret = Sets.newHashSet(); 216ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 217ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // Note to 3rd party developers: 218ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // queryContentProviders() is a public API but this method doesn't officially support 219ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // the GET_META_DATA flag. Don't use it in your app. 220ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final List<ProviderInfo> providers = pm.queryContentProviders(null, 0, 221ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki PackageManager.GET_META_DATA); 222ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (providers == null) { 223ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki return ret; 224ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 225ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki for (ProviderInfo provider : providers) { 226ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (isDirectoryProvider(provider)) { 227ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki ret.add(provider.packageName); 228ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 229ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 230ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (DEBUG) { 231ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Log.d(TAG, "Found " + ret.size() + " directory provider packages"); 232ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 233ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 234ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki return ret; 235ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 236ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 237ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki @VisibleForTesting 23872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int scanAllPackages() { 239e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov SQLiteDatabase db = getDbHelper().getWritableDatabase(); 240e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov insertDefaultDirectory(db); 241e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov insertLocalInvisibleDirectory(db); 242e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 24372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int count = 0; 244ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 245ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // Prepare query strings for removing stale rows which don't correspond to existing 246ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // directories. 247ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki StringBuilder deleteWhereBuilder = new StringBuilder(); 248ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki ArrayList<String> deleteWhereArgs = new ArrayList<String>(); 249ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereBuilder.append("NOT (" + Directory._ID + "=? OR " + Directory._ID + "=?"); 250ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(String.valueOf(Directory.DEFAULT)); 251ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(String.valueOf(Directory.LOCAL_INVISIBLE)); 252ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final String wherePart = "(" + Directory.PACKAGE_NAME + "=? AND " 253ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + Directory.DIRECTORY_AUTHORITY + "=? AND " 254ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + Directory.ACCOUNT_NAME + "=? AND " 255ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + Directory.ACCOUNT_TYPE + "=?)"; 256ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 257ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki for (String packageName : getDirectoryProviderPackages(mPackageManager)) { 258ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (DEBUG) Log.d(TAG, "package=" + packageName); 259ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 260ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final PackageInfo packageInfo; 261ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki try { 262ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki packageInfo = mPackageManager.getPackageInfo(packageName, 263ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA); 264ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (packageInfo == null) continue; // Just in case... 265ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } catch (NameNotFoundException nnfe) { 266ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki continue; // Application just removed? 26772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 26837f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa 269ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki List<DirectoryInfo> directories = updateDirectoriesForPackage(packageInfo, true); 270ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (directories != null && !directories.isEmpty()) { 271ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki count += directories.size(); 272ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 273ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // We shouldn't delete rows for existing directories. 274ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki for (DirectoryInfo info : directories) { 275ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (DEBUG) Log.d(TAG, " directory=" + info); 276ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereBuilder.append(" OR "); 277ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereBuilder.append(wherePart); 278ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(info.packageName); 279ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(info.authority); 280ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(info.accountName); 281ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(info.accountType); 282ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 283ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 28472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 285ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 286ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereBuilder.append(")"); // Close "NOT (" 287ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 288ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki int deletedRows = db.delete(Tables.DIRECTORIES, deleteWhereBuilder.toString(), 289ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.toArray(new String[0])); 290ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Log.i(TAG, "deleted " + deletedRows 291ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " stale rows which don't have any relevant directory"); 29272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov return count; 29372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 29472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 295e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov private void insertDefaultDirectory(SQLiteDatabase db) { 296e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov ContentValues values = new ContentValues(); 297e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory._ID, Directory.DEFAULT); 298e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.PACKAGE_NAME, mContext.getApplicationInfo().packageName); 299e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.DIRECTORY_AUTHORITY, ContactsContract.AUTHORITY); 300e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.TYPE_RESOURCE_ID, R.string.default_directory); 301e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(DirectoryColumns.TYPE_RESOURCE_NAME, 302e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov mContext.getResources().getResourceName(R.string.default_directory)); 303e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_NONE); 304e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.SHORTCUT_SUPPORT, Directory.SHORTCUT_SUPPORT_FULL); 305e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.PHOTO_SUPPORT, Directory.PHOTO_SUPPORT_FULL); 306e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov db.replace(Tables.DIRECTORIES, null, values); 307e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 308e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 309e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov private void insertLocalInvisibleDirectory(SQLiteDatabase db) { 310e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov ContentValues values = new ContentValues(); 311e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory._ID, Directory.LOCAL_INVISIBLE); 312e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.PACKAGE_NAME, mContext.getApplicationInfo().packageName); 313e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.DIRECTORY_AUTHORITY, ContactsContract.AUTHORITY); 314e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.TYPE_RESOURCE_ID, R.string.local_invisible_directory); 315e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(DirectoryColumns.TYPE_RESOURCE_NAME, 316e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov mContext.getResources().getResourceName(R.string.local_invisible_directory)); 317e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_NONE); 318e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.SHORTCUT_SUPPORT, Directory.SHORTCUT_SUPPORT_FULL); 319e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.PHOTO_SUPPORT, Directory.PHOTO_SUPPORT_FULL); 320e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov db.replace(Tables.DIRECTORIES, null, values); 321e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 322e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 32372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 32472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Scans the specified package for content directories. The package may have 32572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * already been removed, so packageName does not necessarily correspond to 32672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * an installed package. 32772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 32872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public void onPackageChanged(String packageName) { 32972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov PackageInfo packageInfo = null; 33072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 33172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov try { 332ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki packageInfo = mPackageManager.getPackageInfo(packageName, 33372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA); 33472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } catch (NameNotFoundException e) { 33572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // The package got removed 33672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov packageInfo = new PackageInfo(); 33772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov packageInfo.packageName = packageName; 33872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 33972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 34072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov updateDirectoriesForPackage(packageInfo, false); 341e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 34272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 343ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 34472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 34572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Scans the specified package for content directories and updates the {@link Directory} 34672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * table accordingly. 34772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 34837f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa private List<DirectoryInfo> updateDirectoriesForPackage( 34937f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa PackageInfo packageInfo, boolean initialScan) { 35072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov ArrayList<DirectoryInfo> directories = Lists.newArrayList(); 35172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 35272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov ProviderInfo[] providers = packageInfo.providers; 35372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (providers != null) { 35472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov for (ProviderInfo provider : providers) { 355ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (isDirectoryProvider(provider)) { 356ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki queryDirectoriesForAuthority(directories, provider); 35772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 35872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 35972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 36072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 36172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (directories.size() == 0 && initialScan) { 36237f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa return null; 36372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 36472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 365e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov SQLiteDatabase db = getDbHelper().getWritableDatabase(); 36672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov db.beginTransaction(); 36772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov try { 36872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov updateDirectories(db, directories); 36972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // Clear out directories that are no longer present 37072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov StringBuilder sb = new StringBuilder(Directory.PACKAGE_NAME + "=?"); 37172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!directories.isEmpty()) { 37272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov sb.append(" AND " + Directory._ID + " NOT IN("); 37372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov for (DirectoryInfo info: directories) { 37472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov sb.append(info.id).append(","); 37572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 37672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov sb.setLength(sb.length() - 1); // Remove the extra comma 37772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov sb.append(")"); 37872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 37972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov db.delete(Tables.DIRECTORIES, sb.toString(), new String[] { packageInfo.packageName }); 38072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov db.setTransactionSuccessful(); 38172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } finally { 38272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov db.endTransaction(); 38372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 38472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 38572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov mContactsProvider.resetDirectoryCache(); 38637f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa return directories; 38772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 38872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 38972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 39072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Sends a {@link Directory#CONTENT_URI} request to a specific contact directory 39172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * provider and appends all discovered directories to the directoryInfo list. 39272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 39372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov protected void queryDirectoriesForAuthority( 39472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov ArrayList<DirectoryInfo> directoryInfo, ProviderInfo provider) { 39572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Uri uri = new Uri.Builder().scheme("content") 39672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov .authority(provider.authority).appendPath("directories").build(); 39772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Cursor cursor = null; 39872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov try { 39972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov cursor = mContext.getContentResolver().query( 40072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov uri, DirectoryQuery.PROJECTION, null, null, null); 40172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (cursor == null) { 40272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.i(TAG, providerDescription(provider) + " returned a NULL cursor."); 40372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } else { 40472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov while (cursor.moveToNext()) { 40572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov DirectoryInfo info = new DirectoryInfo(); 40672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.packageName = provider.packageName; 40772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.authority = provider.authority; 40872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME); 40972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE); 41072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME); 41172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!cursor.isNull(DirectoryQuery.TYPE_RESOURCE_ID)) { 41272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID); 41372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 41472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!cursor.isNull(DirectoryQuery.EXPORT_SUPPORT)) { 41572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT); 41672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov switch (exportSupport) { 41772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.EXPORT_SUPPORT_NONE: 41872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY: 41972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.EXPORT_SUPPORT_ANY_ACCOUNT: 42072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.exportSupport = exportSupport; 42172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov break; 42272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov default: 42372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.e(TAG, providerDescription(provider) 42472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov + " - invalid export support flag: " + exportSupport); 42572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 42672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 42772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!cursor.isNull(DirectoryQuery.SHORTCUT_SUPPORT)) { 42872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int shortcutSupport = cursor.getInt(DirectoryQuery.SHORTCUT_SUPPORT); 42972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov switch (shortcutSupport) { 43072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.SHORTCUT_SUPPORT_NONE: 43172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY: 43272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.SHORTCUT_SUPPORT_FULL: 43372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.shortcutSupport = shortcutSupport; 43472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov break; 43572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov default: 43672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.e(TAG, providerDescription(provider) 43772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov + " - invalid shortcut support flag: " + shortcutSupport); 43872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 43972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 440994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov if (!cursor.isNull(DirectoryQuery.PHOTO_SUPPORT)) { 441994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov int photoSupport = cursor.getInt(DirectoryQuery.PHOTO_SUPPORT); 442994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov switch (photoSupport) { 443994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov case Directory.PHOTO_SUPPORT_NONE: 444994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov case Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY: 445994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov case Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY: 446994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov case Directory.PHOTO_SUPPORT_FULL: 447994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov info.photoSupport = photoSupport; 448994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov break; 449994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov default: 450994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov Log.e(TAG, providerDescription(provider) 451994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov + " - invalid photo support flag: " + photoSupport); 452994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov } 453994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov } 45472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov directoryInfo.add(info); 45572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 45672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 45772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } catch (Throwable t) { 45872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.e(TAG, providerDescription(provider) + " exception", t); 45972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } finally { 46072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (cursor != null) { 46172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov cursor.close(); 46272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 46372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 46472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 46572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 46672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 46772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Updates the directories tables in the database to match the info received 46872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * from directory providers. 46972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 47072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private void updateDirectories(SQLiteDatabase db, ArrayList<DirectoryInfo> directoryInfo) { 47172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // Insert or replace existing directories. 47272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // This happens so infrequently that we can use a less-then-optimal one-a-time approach 47372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov for (DirectoryInfo info : directoryInfo) { 47472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov ContentValues values = new ContentValues(); 47572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.PACKAGE_NAME, info.packageName); 47672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.DIRECTORY_AUTHORITY, info.authority); 47772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.ACCOUNT_NAME, info.accountName); 47872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.ACCOUNT_TYPE, info.accountType); 47972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.TYPE_RESOURCE_ID, info.typeResourceId); 48072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.DISPLAY_NAME, info.displayName); 48172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.EXPORT_SUPPORT, info.exportSupport); 48272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.SHORTCUT_SUPPORT, info.shortcutSupport); 483994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov values.put(Directory.PHOTO_SUPPORT, info.photoSupport); 48472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 485e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov if (info.typeResourceId != 0) { 486ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki String resourceName = getResourceNameById(info.packageName, info.typeResourceId); 487e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(DirectoryColumns.TYPE_RESOURCE_NAME, resourceName); 488e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 489e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 49072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Cursor cursor = db.query(Tables.DIRECTORIES, new String[] { Directory._ID }, 49172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.PACKAGE_NAME + "=? AND " + Directory.DIRECTORY_AUTHORITY + "=? AND " 49272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov + Directory.ACCOUNT_NAME + "=? AND " + Directory.ACCOUNT_TYPE + "=?", 49372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov new String[] { 49472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.packageName, info.authority, info.accountName, info.accountType }, 49572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov null, null, null); 49672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov try { 49772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov long id; 49872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (cursor.moveToFirst()) { 49972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov id = cursor.getLong(0); 50072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov db.update(Tables.DIRECTORIES, values, Directory._ID + "=?", 50172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov new String[] { String.valueOf(id) }); 50272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } else { 50372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov id = db.insert(Tables.DIRECTORIES, null, values); 50472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 50572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.id = id; 50672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } finally { 50772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov cursor.close(); 50872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 50972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 51072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 51172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 51272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov protected String providerDescription(ProviderInfo provider) { 51372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov return "Directory provider " + provider.packageName + "(" + provider.authority + ")"; 51472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 51572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov} 516