blob: 4f2dcf0987592cabce91688285cda9a81ecb6508 [file] [log] [blame]
Wenyi Wang1fc3ef42016-01-14 18:21:40 -08001/*
2 * Copyright (C) 2016 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
17package com.android.contacts.callblocking;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.database.Cursor;
22import android.os.AsyncTask;
23import android.provider.ContactsContract.CommonDataKinds.Phone;
24import android.provider.ContactsContract.Contacts;
25import android.telephony.PhoneNumberUtils;
26import android.text.TextUtils;
27import android.util.Log;
28import android.widget.Toast;
29
30import com.android.contacts.R;
31import com.android.contacts.common.util.TelephonyManagerUtils;
32
33import com.google.i18n.phonenumbers.NumberParseException;
34import com.google.i18n.phonenumbers.Phonenumber;
35import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
36
37import java.util.Locale;
38
39/**
40 * Utility to help with tasks related to filtered numbers.
41 */
42final public class FilteredNumbersUtil {
43
44 private static final String TAG = "FilteredNumbersUtil";
45
46 public interface CheckForSendToVoicemailContactListener {
47 void onComplete(boolean hasSendToVoicemailContact);
48 }
49
50 public interface ImportSendToVoicemailContactsListener {
51 void onImportComplete();
52 }
53
54 private static class ContactsQuery {
55 static final String[] PROJECTION = {
56 Contacts._ID
57 };
58
59 // TODO: as user can set "send to voicemail" for a contact that doesn't have a phone number,
60 // if those are the only contacts that are marked as "send to voicemail", then when you view
61 // numbers it'll be blank. We should also
62 static final String SELECT_SEND_TO_VOICEMAIL_TRUE = Contacts.SEND_TO_VOICEMAIL + "=1";
63
64 static final int ID_COLUMN_INDEX = 0;
65 }
66
67 public static class PhoneQuery {
68 static final String[] PROJECTION = {
69 Contacts._ID,
70 Phone.NORMALIZED_NUMBER,
71 Phone.NUMBER
72 };
73
74 static final int ID_COLUMN_INDEX = 0;
75 static final int NORMALIZED_NUMBER_COLUMN_INDEX = 1;
76 static final int NUMBER_COLUMN_INDEX = 2;
77
78 static final String SELECT_SEND_TO_VOICEMAIL_TRUE = Contacts.SEND_TO_VOICEMAIL + "=1";
79 }
80
81 /**
82 * Checks if there exists a contact with {@code Contacts.SEND_TO_VOICEMAIL} set to true.
83 */
84 public static void checkForSendToVoicemailContact(
85 final Context context, final CheckForSendToVoicemailContactListener listener) {
86 final AsyncTask task = new AsyncTask<Object, Void, Boolean>() {
87 @Override
88 public Boolean doInBackground(Object[] params) {
89 if (context == null) {
90 return false;
91 }
92
93 final Cursor cursor = context.getContentResolver().query(
94 Contacts.CONTENT_URI,
95 ContactsQuery.PROJECTION,
96 ContactsQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
97 null,
98 null);
99
100 boolean hasSendToVoicemailContacts = false;
101 if (cursor != null) {
102 try {
103 hasSendToVoicemailContacts = cursor.getCount() > 0;
104 } finally {
105 cursor.close();
106 }
107 }
108
109 return hasSendToVoicemailContacts;
110 }
111
112 @Override
113 public void onPostExecute(Boolean hasSendToVoicemailContact) {
114 if (listener != null) {
115 listener.onComplete(hasSendToVoicemailContact);
116 }
117 }
118 };
119 task.execute();
120 }
121
122 /**
123 * Blocks all the phone numbers of any contacts marked as SEND_TO_VOICEMAIL, then clears the
124 * SEND_TO_VOICEMAIL flag on those contacts.
125 */
126 public static void importSendToVoicemailContacts(
127 final Context context, final ImportSendToVoicemailContactsListener listener) {
128 final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler =
129 new FilteredNumberAsyncQueryHandler(context.getContentResolver());
130
131 final AsyncTask<Object, Void, Boolean> task = new AsyncTask<Object, Void, Boolean>() {
132 @Override
133 public Boolean doInBackground(Object[] params) {
134 if (context == null) {
135 return false;
136 }
137
138 // Get the phone number of contacts marked as SEND_TO_VOICEMAIL.
139 final Cursor phoneCursor = context.getContentResolver().query(
140 Phone.CONTENT_URI,
141 PhoneQuery.PROJECTION,
142 PhoneQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
143 null,
144 null);
145
146 if (phoneCursor == null) {
147 return false;
148 }
149
150 try {
151 while (phoneCursor.moveToNext()) {
152 final String normalizedNumber = phoneCursor.getString(
153 PhoneQuery.NORMALIZED_NUMBER_COLUMN_INDEX);
154 final String number = phoneCursor.getString(
155 PhoneQuery.NUMBER_COLUMN_INDEX);
156 if (normalizedNumber != null) {
157 // Block the phone number of the contact.
158 mFilteredNumberAsyncQueryHandler.blockNumber(
159 null, normalizedNumber, number, null);
160 }
161 }
162 } finally {
163 phoneCursor.close();
164 }
165
166 // Clear SEND_TO_VOICEMAIL on all contacts. The setting has been imported to Dialer.
167 ContentValues newValues = new ContentValues();
168 newValues.put(Contacts.SEND_TO_VOICEMAIL, 0);
169 context.getContentResolver().update(
170 Contacts.CONTENT_URI,
171 newValues,
172 ContactsQuery.SELECT_SEND_TO_VOICEMAIL_TRUE,
173 null);
174
175 return true;
176 }
177
178 @Override
179 public void onPostExecute(Boolean success) {
180 if (success) {
181 if (listener != null) {
182 listener.onImportComplete();
183 }
184 } else if (context != null) {
185 String toastStr = context.getString(R.string.send_to_voicemail_import_failed);
186 Toast.makeText(context, toastStr, Toast.LENGTH_SHORT).show();
187 }
188 }
189 };
190 task.execute();
191 }
192
193 public static boolean canBlockNumber(Context context, String number, String countryIso) {
194 final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
195 return !TextUtils.isEmpty(normalizedNumber)
196 && !PhoneNumberUtils.isEmergencyNumber(normalizedNumber);
197 }
198
199 /**
200 * @return a geographical description string for the specified number.
201 * @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder
202 *
203 * Copied from com.android.dialer.util.PhoneNumberUtil.getGeoDescription(mContext, info.number);
204 */
205 public static String getGeoDescription(Context context, String number) {
206 if (TextUtils.isEmpty(number)) {
207 return null;
208 }
209
210 com.google.i18n.phonenumbers.PhoneNumberUtil util =
211 com.google.i18n.phonenumbers.PhoneNumberUtil.getInstance();
212 PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
213
214 Locale locale = context.getResources().getConfiguration().locale;
215 String countryIso = TelephonyManagerUtils.getCurrentCountryIso(context, locale);
216 Phonenumber.PhoneNumber pn = null;
217 try {
218 pn = util.parse(number, countryIso);
219 } catch (NumberParseException e) {
220 if (Log.isLoggable(TAG, Log.VERBOSE)) {
221 Log.v(TAG, "getGeoDescription: NumberParseException for incoming number '" +
222 number + "'");
223 }
224 }
225
226 if (pn != null) {
227 String description = geocoder.getDescriptionForNumber(pn, locale);
228 return description;
229 }
230
231 return null;
232 }
233}