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