summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/java/com/android/common/contacts/DataUsageStatUpdater.java263
1 files changed, 263 insertions, 0 deletions
diff --git a/common/java/com/android/common/contacts/DataUsageStatUpdater.java b/common/java/com/android/common/contacts/DataUsageStatUpdater.java
new file mode 100644
index 0000000..bc1f8ea
--- /dev/null
+++ b/common/java/com/android/common/contacts/DataUsageStatUpdater.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.common.contacts;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Data;
+import android.text.TextUtils;
+import android.text.util.Rfc822Token;
+import android.text.util.Rfc822Tokenizer;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Convenient class for updating usage statistics in ContactsProvider.
+ *
+ * Applications like Email, Sms, etc. can promote recipients for better sorting with this class
+ *
+ * @see ContactsContract.Contacts
+ */
+public class DataUsageStatUpdater {
+ private static final String TAG = DataUsageStatUpdater.class.getSimpleName();
+
+ /**
+ * Copied from API in ICS (not available before it). You can use values here if you are sure
+ * it is supported by the device.
+ */
+ public static final class DataUsageFeedback {
+ private static final Uri FEEDBACK_URI =
+ Uri.withAppendedPath(Data.CONTENT_URI, "usagefeedback");
+
+ private static final String USAGE_TYPE = "type";
+ public static final String USAGE_TYPE_CALL = "call";
+ public static final String USAGE_TYPE_LONG_TEXT = "long_text";
+ public static final String USAGE_TYPE_SHORT_TEXT = "short_text";
+ }
+
+ private final ContentResolver mResolver;
+
+ public DataUsageStatUpdater(Context context) {
+ mResolver = context.getContentResolver();
+ }
+
+ /**
+ * Updates usage statistics using comma-separated RFC822 address like
+ * "Joe <joe@example.com>, Due <due@example.com>".
+ *
+ * This will cause Disk access so should be called in a background thread.
+ *
+ * @return true when update request is correctly sent. False when the request fails,
+ * input has no valid entities.
+ */
+ public boolean updateWithRfc822Address(Collection<CharSequence> texts){
+ if (texts == null) {
+ return false;
+ } else {
+ final Set<String> addresses = new HashSet<String>();
+ for (CharSequence text : texts) {
+ Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text.toString().trim());
+ for (Rfc822Token token : tokens) {
+ addresses.add(token.getAddress());
+ }
+ }
+ return updateWithAddress(addresses);
+ }
+ }
+
+ /**
+ * Update usage statistics information using a list of email addresses.
+ *
+ * This will cause Disk access so should be called in a background thread.
+ *
+ * @see #update(Collection, Collection, String)
+ *
+ * @return true when update request is correctly sent. False when the request fails,
+ * input has no valid entities.
+ */
+ public boolean updateWithAddress(Collection<String> addresses) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "updateWithAddress: " + Arrays.toString(addresses.toArray()));
+ }
+ if (addresses != null && !addresses.isEmpty()) {
+ final ArrayList<String> whereArgs = new ArrayList<String>();
+ final StringBuilder whereBuilder = new StringBuilder();
+ final String[] questionMarks = new String[addresses.size()];
+
+ whereArgs.addAll(addresses);
+ Arrays.fill(questionMarks, "?");
+ // Email.ADDRESS == Email.DATA1. Email.ADDRESS can be available from API Level 11.
+ whereBuilder.append(Email.DATA1 + " IN (")
+ .append(TextUtils.join(",", questionMarks))
+ .append(")");
+ final Cursor cursor = mResolver.query(Email.CONTENT_URI,
+ new String[] {Email.CONTACT_ID, Email._ID}, whereBuilder.toString(),
+ whereArgs.toArray(new String[0]), null);
+
+ if (cursor == null) {
+ Log.w(TAG, "Cursor for Email.CONTENT_URI became null.");
+ } else {
+ final Set<Long> contactIds = new HashSet<Long>(cursor.getCount());
+ final Set<Long> dataIds = new HashSet<Long>(cursor.getCount());
+ try {
+ cursor.move(-1);
+ while(cursor.moveToNext()) {
+ contactIds.add(cursor.getLong(0));
+ dataIds.add(cursor.getLong(1));
+ }
+ } finally {
+ cursor.close();
+ }
+ return update(contactIds, dataIds, DataUsageFeedback.USAGE_TYPE_LONG_TEXT);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Update usage statistics information using a list of phone numbers.
+ *
+ * This will cause Disk access so should be called in a background thread.
+ *
+ * @see #update(Collection, Collection, String)
+ *
+ * @return true when update request is correctly sent. False when the request fails,
+ * input has no valid entities.
+ */
+ public boolean updateWithPhoneNumber(Collection<String> numbers) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "updateWithPhoneNumber: " + Arrays.toString(numbers.toArray()));
+ }
+ if (numbers != null && !numbers.isEmpty()) {
+ final ArrayList<String> whereArgs = new ArrayList<String>();
+ final StringBuilder whereBuilder = new StringBuilder();
+ final String[] questionMarks = new String[numbers.size()];
+
+ whereArgs.addAll(numbers);
+ Arrays.fill(questionMarks, "?");
+ // Phone.NUMBER == Phone.DATA1. NUMBER can be available from API Level 11.
+ whereBuilder.append(Phone.DATA1 + " IN (")
+ .append(TextUtils.join(",", questionMarks))
+ .append(")");
+ final Cursor cursor = mResolver.query(Phone.CONTENT_URI,
+ new String[] {Phone.CONTACT_ID, Phone._ID}, whereBuilder.toString(),
+ whereArgs.toArray(new String[0]), null);
+
+ if (cursor == null) {
+ Log.w(TAG, "Cursor for Phone.CONTENT_URI became null.");
+ } else {
+ final Set<Long> contactIds = new HashSet<Long>(cursor.getCount());
+ final Set<Long> dataIds = new HashSet<Long>(cursor.getCount());
+ try {
+ cursor.move(-1);
+ while(cursor.moveToNext()) {
+ contactIds.add(cursor.getLong(0));
+ dataIds.add(cursor.getLong(1));
+ }
+ } finally {
+ cursor.close();
+ }
+ return update(contactIds, dataIds, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return true when one or more of update requests are correctly sent.
+ * False when all the requests fail.
+ */
+ private boolean update(Collection<Long> contactIds, Collection<Long> dataIds, String type) {
+ final long currentTimeMillis = System.currentTimeMillis();
+
+ boolean successful = false;
+
+ // From ICS we can use per-contact-method structure. We'll check if the device supports it
+ // and call the API.
+ //
+ // STOPSHIP: Use 13 or later when we're sure ICS has correct SDK version
+ if (Build.VERSION.SDK_INT >= 12) {
+ if (dataIds.isEmpty()) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Given list for data IDs is null. Ignoring.");
+ }
+ } else {
+ final Uri uri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
+ .appendPath(TextUtils.join(",", dataIds))
+ .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, type)
+ .build();
+ if (mResolver.update(uri, new ContentValues(), null, null) > 0) {
+ successful = true;
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "update toward data rows " + dataIds + " failed");
+ }
+ }
+ }
+ } else {
+ // Use older API.
+ if (contactIds.isEmpty()) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Given list for contact IDs is null. Ignoring.");
+ }
+ } else {
+ final StringBuilder whereBuilder = new StringBuilder();
+ final ArrayList<String> whereArgs = new ArrayList<String>();
+ final String[] questionMarks = new String[contactIds.size()];
+ for (long contactId : contactIds) {
+ whereArgs.add(String.valueOf(contactId));
+ }
+ Arrays.fill(questionMarks, "?");
+ whereBuilder.append(ContactsContract.Contacts._ID + " IN (").
+ append(TextUtils.join(",", questionMarks)).
+ append(")");
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "contactId where: " + whereBuilder.toString());
+ Log.d(TAG, "contactId selection: " + whereArgs);
+ }
+
+ final ContentValues values = new ContentValues();
+ values.put(ContactsContract.Contacts.LAST_TIME_CONTACTED, currentTimeMillis);
+ if (mResolver.update(ContactsContract.Contacts.CONTENT_URI, values,
+ whereBuilder.toString(), whereArgs.toArray(new String[0])) > 0) {
+ successful = true;
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "update toward raw contacts " + contactIds + " failed");
+ }
+ }
+ }
+ }
+
+ return successful;
+ }
+}