1 /*
2  * Copyright (C) 2015 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.messaging.datamodel;
18 
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.net.Uri;
22 import android.provider.ContactsContract;
23 import android.provider.ContactsContract.Contacts;
24 
25 import com.android.messaging.util.FallbackStrategies;
26 import com.android.messaging.util.FallbackStrategies.Strategy;
27 import com.android.messaging.util.LogUtil;
28 import com.android.messaging.util.OsUtil;
29 
30 /**
31  * Helper for querying frequent (and/or starred) contacts.
32  */
33 public class FrequentContactsCursorQueryData extends CursorQueryData {
34     private static final String TAG = LogUtil.BUGLE_TAG;
35 
36     private static class FrequentContactsCursorLoader extends BoundCursorLoader {
37         private final Uri mOriginalUri;
38 
FrequentContactsCursorLoader(String bindingId, Context context, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)39         FrequentContactsCursorLoader(String bindingId, Context context, Uri uri,
40                 String[] projection, String selection, String[] selectionArgs, String sortOrder) {
41             super(bindingId, context, uri, projection, selection, selectionArgs, sortOrder);
42             mOriginalUri = uri;
43         }
44 
45         @Override
loadInBackground()46         public Cursor loadInBackground() {
47             return FallbackStrategies
48                     .startWith(new PrimaryStrequentContactsQueryStrategy())
49                     .thenTry(new FrequentOnlyContactsQueryStrategy())
50                     .thenTry(new PhoneOnlyStrequentContactsQueryStrategy())
51                     .execute(null);
52         }
53 
54         private abstract class StrequentContactsQueryStrategy implements Strategy<Void, Cursor> {
55             @Override
execute(Void params)56             public Cursor execute(Void params) throws Exception {
57                 final Uri uri = getUri();
58                 if (uri != null) {
59                     setUri(uri);
60                 }
61                 return FrequentContactsCursorLoader.super.loadInBackground();
62             }
getUri()63             protected abstract Uri getUri();
64         }
65 
66         private class PrimaryStrequentContactsQueryStrategy extends StrequentContactsQueryStrategy {
67             @Override
getUri()68             protected Uri getUri() {
69                 // Use the original URI requested.
70                 return mOriginalUri;
71             }
72         }
73 
74         private class FrequentOnlyContactsQueryStrategy extends StrequentContactsQueryStrategy {
75             @Override
getUri()76             protected Uri getUri() {
77                 // Some phones have a buggy implementation of the Contacts provider which crashes
78                 // when we query for strequent (starred+frequent) contacts (b/17991485).
79                 // If this happens, switch to just querying for frequent contacts.
80                 return Contacts.CONTENT_FREQUENT_URI;
81             }
82         }
83 
84         private class PhoneOnlyStrequentContactsQueryStrategy extends
85                 StrequentContactsQueryStrategy {
86             @Override
getUri()87             protected Uri getUri() {
88                 // Some 3rd party ROMs have content provider
89                 // implementation where invalid SQL queries are returned for regular strequent
90                 // queries. Using strequent_phone_only query as a fallback to display only phone
91                 // contacts. This is the last-ditch effort; if this fails, we will display an
92                 // empty frequent list (b/18354836).
93                 final String strequentQueryParam = OsUtil.isAtLeastL() ?
94                         ContactsContract.STREQUENT_PHONE_ONLY : "strequent_phone_only";
95                 // TODO: Handle enterprise contacts post M once contacts provider supports it
96                 return Contacts.CONTENT_STREQUENT_URI.buildUpon()
97                         .appendQueryParameter(strequentQueryParam, "true").build();
98             }
99         }
100     }
101 
FrequentContactsCursorQueryData(Context context, String[] projection, String selection, String[] selectionArgs, String sortOrder)102     public FrequentContactsCursorQueryData(Context context, String[] projection,
103             String selection, String[] selectionArgs, String sortOrder) {
104         // TODO: Handle enterprise contacts post M once contacts provider supports it
105         super(context, Contacts.CONTENT_STREQUENT_URI, projection, selection, selectionArgs,
106                 sortOrder);
107     }
108 
109     @Override
createBoundCursorLoader(String bindingId)110     public BoundCursorLoader createBoundCursorLoader(String bindingId) {
111         return new FrequentContactsCursorLoader(bindingId, mContext, mUri, mProjection, mSelection,
112                 mSelectionArgs, mSortOrder);
113     }
114 }
115