• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package com.android.dialer.searchfragment.cp2;
18 
19 import android.content.Context;
20 import android.content.CursorLoader;
21 import android.database.Cursor;
22 import android.database.MatrixCursor;
23 import android.database.MergeCursor;
24 import android.net.Uri;
25 import android.provider.ContactsContract.CommonDataKinds.Phone;
26 import android.provider.ContactsContract.Directory;
27 import android.support.annotation.NonNull;
28 import android.support.annotation.Nullable;
29 import android.text.TextUtils;
30 import com.android.dialer.common.LogUtil;
31 import com.android.dialer.contacts.ContactsComponent;
32 import com.android.dialer.contacts.displaypreference.ContactDisplayPreferences.DisplayOrder;
33 import com.android.dialer.contacts.displaypreference.ContactDisplayPreferences.SortOrder;
34 import com.android.dialer.searchfragment.common.Projections;
35 import com.android.dialer.searchfragment.common.SearchCursor;
36 import com.android.dialer.smartdial.SmartDialCursorLoader;
37 import com.android.dialer.util.PermissionsUtil;
38 
39 /** Cursor Loader for CP2 contacts. */
40 public final class SearchContactsCursorLoader extends CursorLoader {
41 
42   private final String query;
43   private final boolean isRegularSearch;
44 
45   /** @param query Contacts cursor will be filtered based on this query. */
SearchContactsCursorLoader( Context context, @Nullable String query, boolean isRegularSearch)46   public SearchContactsCursorLoader(
47       Context context, @Nullable String query, boolean isRegularSearch) {
48     super(
49         context,
50         buildUri(query),
51         getProjection(context),
52         getWhere(context),
53         null,
54         getSortKey(context) + " ASC");
55     this.query = TextUtils.isEmpty(query) ? "" : query;
56     this.isRegularSearch = isRegularSearch;
57   }
58 
getProjection(Context context)59   private static String[] getProjection(Context context) {
60     boolean displayOrderPrimary =
61         (ContactsComponent.get(context).contactDisplayPreferences().getDisplayOrder()
62             == DisplayOrder.PRIMARY);
63     return displayOrderPrimary
64         ? Projections.CP2_PROJECTION
65         : Projections.CP2_PROJECTION_ALTERNATIVE;
66   }
67 
getWhere(Context context)68   private static String getWhere(Context context) {
69     String where = getProjection(context)[Projections.DISPLAY_NAME] + " IS NOT NULL";
70     where += " AND " + Phone.NUMBER + " IS NOT NULL";
71     return where;
72   }
73 
getSortKey(Context context)74   private static String getSortKey(Context context) {
75     boolean sortOrderPrimary =
76         (ContactsComponent.get(context).contactDisplayPreferences().getSortOrder()
77             == SortOrder.BY_PRIMARY);
78     return sortOrderPrimary ? Phone.SORT_KEY_PRIMARY : Phone.SORT_KEY_ALTERNATIVE;
79   }
80 
buildUri(String query)81   private static Uri buildUri(String query) {
82     return Phone.CONTENT_FILTER_URI.buildUpon().appendPath(query).build();
83   }
84 
85   @Override
loadInBackground()86   public Cursor loadInBackground() {
87     if (!PermissionsUtil.hasContactsReadPermissions(getContext())) {
88       LogUtil.i("SearchContactsCursorLoader.loadInBackground", "Contacts permission denied.");
89       return null;
90     }
91     return isRegularSearch ? regularSearchLoadInBackground() : dialpadSearchLoadInBackground();
92   }
93 
regularSearchLoadInBackground()94   private Cursor regularSearchLoadInBackground() {
95     return RegularSearchCursor.newInstance(getContext(), super.loadInBackground());
96   }
97 
dialpadSearchLoadInBackground()98   private Cursor dialpadSearchLoadInBackground() {
99     SmartDialCursorLoader loader = new SmartDialCursorLoader(getContext());
100     loader.configureQuery(query);
101     Cursor cursor = loader.loadInBackground();
102     return SmartDialCursor.newInstance(getContext(), cursor);
103   }
104 
105   static class SmartDialCursor extends MergeCursor implements SearchCursor {
106 
newInstance(Context context, Cursor smartDialCursor)107     static SmartDialCursor newInstance(Context context, Cursor smartDialCursor) {
108       if (smartDialCursor == null || smartDialCursor.getCount() == 0) {
109         LogUtil.i("SmartDialCursor.newInstance", "Cursor was null or empty");
110         return new SmartDialCursor(new Cursor[] {new MatrixCursor(Projections.CP2_PROJECTION)});
111       }
112 
113       MatrixCursor headerCursor = new MatrixCursor(HEADER_PROJECTION);
114       headerCursor.addRow(new String[] {context.getString(R.string.all_contacts)});
115       return new SmartDialCursor(
116           new Cursor[] {headerCursor, convertSmartDialCursorToSearchCursor(smartDialCursor)});
117     }
118 
SmartDialCursor(Cursor[] cursors)119     private SmartDialCursor(Cursor[] cursors) {
120       super(cursors);
121     }
122 
123     @Override
isHeader()124     public boolean isHeader() {
125       return isFirst();
126     }
127 
128     @Override
updateQuery(@ullable String query)129     public boolean updateQuery(@Nullable String query) {
130       return false;
131     }
132 
133     @Override
getDirectoryId()134     public long getDirectoryId() {
135       return Directory.DEFAULT;
136     }
137 
convertSmartDialCursorToSearchCursor(Cursor smartDialCursor)138     private static MatrixCursor convertSmartDialCursorToSearchCursor(Cursor smartDialCursor) {
139       MatrixCursor cursor = new MatrixCursor(Projections.CP2_PROJECTION);
140       if (!smartDialCursor.moveToFirst()) {
141         return cursor;
142       }
143 
144       do {
145         Object[] newRow = new Object[Projections.CP2_PROJECTION.length];
146         for (int i = 0; i < Projections.CP2_PROJECTION.length; i++) {
147           String column = Projections.CP2_PROJECTION[i];
148           int index = smartDialCursor.getColumnIndex(column);
149           if (index != -1) {
150             switch (smartDialCursor.getType(index)) {
151               case FIELD_TYPE_INTEGER:
152                 newRow[i] = smartDialCursor.getInt(index);
153                 break;
154               case FIELD_TYPE_STRING:
155                 newRow[i] = smartDialCursor.getString(index);
156                 break;
157               case FIELD_TYPE_FLOAT:
158                 newRow[i] = smartDialCursor.getFloat(index);
159                 break;
160               case FIELD_TYPE_BLOB:
161                 newRow[i] = smartDialCursor.getBlob(index);
162                 break;
163               case FIELD_TYPE_NULL:
164               default:
165                 // No-op
166                 break;
167             }
168           }
169         }
170         cursor.addRow(newRow);
171       } while (smartDialCursor.moveToNext());
172       return cursor;
173     }
174   }
175 
176   static class RegularSearchCursor extends MergeCursor implements SearchCursor {
177 
newInstance(Context context, Cursor regularSearchCursor)178     static RegularSearchCursor newInstance(Context context, Cursor regularSearchCursor) {
179       if (regularSearchCursor == null || regularSearchCursor.getCount() == 0) {
180         LogUtil.i("RegularSearchCursor.newInstance", "Cursor was null or empty");
181         return new RegularSearchCursor(new Cursor[] {new MatrixCursor(Projections.CP2_PROJECTION)});
182       }
183 
184       MatrixCursor headerCursor = new MatrixCursor(HEADER_PROJECTION);
185       headerCursor.addRow(new String[] {context.getString(R.string.all_contacts)});
186       return new RegularSearchCursor(new Cursor[] {headerCursor, regularSearchCursor});
187     }
188 
RegularSearchCursor(Cursor[] cursors)189     public RegularSearchCursor(Cursor[] cursors) {
190       super(cursors);
191     }
192 
193     @Override
isHeader()194     public boolean isHeader() {
195       return isFirst();
196     }
197 
198     @Override
updateQuery(@onNull String query)199     public boolean updateQuery(@NonNull String query) {
200       return false; // no-op
201     }
202 
203     @Override
getDirectoryId()204     public long getDirectoryId() {
205       return 0; // no-op
206     }
207   }
208 }
209