blob: 5c7d5e39de069cc1192df888292dbc172abb4b58 [file] [log] [blame]
Paul Soulosb3054e52014-06-05 16:46:02 -07001/*
2 * Copyright (C) 2014 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 */
16package com.android.contacts.interactions;
17
18import android.content.AsyncTaskLoader;
19import android.content.ContentValues;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.database.Cursor;
23import android.database.DatabaseUtils;
24import android.provider.Telephony;
25import android.util.Log;
26
Wenyi Wang04719722015-11-30 13:56:47 -080027import com.android.contacts.common.compat.TelephonyThreadsCompat;
28
Paul Soulosb3054e52014-06-05 16:46:02 -070029import java.util.ArrayList;
30import java.util.Collections;
31import java.util.List;
32
33/**
34 * Loads the most recent sms between the passed in phone numbers.
35 *
36 * This is a two part process. The first step is retrieving the threadIds for each of the phone
37 * numbers using fuzzy matching. The next step is to run another query against these threadIds
38 * to retrieve the actual sms.
39 */
40public class SmsInteractionsLoader extends AsyncTaskLoader<List<ContactInteraction>> {
41
42 private static final String TAG = SmsInteractionsLoader.class.getSimpleName();
43
44 private String[] mPhoneNums;
45 private int mMaxToRetrieve;
46 private List<ContactInteraction> mData;
47
48 /**
49 * Loads a list of SmsInteraction from the supplied phone numbers.
50 */
51 public SmsInteractionsLoader(Context context, String[] phoneNums,
52 int maxToRetrieve) {
53 super(context);
54 Log.v(TAG, "SmsInteractionsLoader");
55 mPhoneNums = phoneNums;
56 mMaxToRetrieve = maxToRetrieve;
57 }
58
59 @Override
60 public List<ContactInteraction> loadInBackground() {
61 Log.v(TAG, "loadInBackground");
62 // Confirm the device has Telephony and numbers were provided before proceeding
63 if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
64 || mPhoneNums == null || mPhoneNums.length == 0) {
65 return Collections.emptyList();
66 }
67
68 // Retrieve the thread IDs
69 List<String> threadIdStrings = new ArrayList<>();
70 for (String phone : mPhoneNums) {
Walter Jang8a3fe522014-10-16 11:12:13 -070071 // TODO: the phone numbers added to the ContactInteraction result should retain their
72 // original formatting since TalkBack is not reading the normalized numbers correctly
Brian Attwellae6c7172014-09-25 11:20:49 -070073 try {
74 threadIdStrings.add(String.valueOf(
Wenyi Wang04719722015-11-30 13:56:47 -080075 TelephonyThreadsCompat.getOrCreateThreadId(getContext(), phone)));
Brian Attwellae6c7172014-09-25 11:20:49 -070076 } catch (Exception e) {
77 // Do nothing. Telephony.Threads.getOrCreateThreadId() throws exceptions when
78 // it can't find/create a threadId (b/17657656).
79 }
Paul Soulosb3054e52014-06-05 16:46:02 -070080 }
81
82 // Query the SMS database for the threads
83 Cursor cursor = getSmsCursorFromThreads(threadIdStrings);
Paul Soulos899aa212014-06-11 12:04:43 -070084 if (cursor != null) {
85 try {
86 List<ContactInteraction> interactions = new ArrayList<>();
87 while (cursor.moveToNext()) {
88 ContentValues values = new ContentValues();
89 DatabaseUtils.cursorRowToContentValues(cursor, values);
90 interactions.add(new SmsInteraction(values));
91 }
Paul Soulosb3054e52014-06-05 16:46:02 -070092
Paul Soulos899aa212014-06-11 12:04:43 -070093 return interactions;
94 } finally {
95 cursor.close();
96 }
Paul Soulosb3054e52014-06-05 16:46:02 -070097 }
98
Paul Soulos899aa212014-06-11 12:04:43 -070099 return Collections.emptyList();
Paul Soulosb3054e52014-06-05 16:46:02 -0700100 }
101
102 /**
103 * Return the most recent messages between a list of threads
104 */
105 private Cursor getSmsCursorFromThreads(List<String> threadIds) {
Brian Attwellae6c7172014-09-25 11:20:49 -0700106 if (threadIds.size() == 0) {
107 return null;
108 }
Paul Soulosb3054e52014-06-05 16:46:02 -0700109 String selection = Telephony.Sms.THREAD_ID + " IN "
110 + ContactInteractionUtil.questionMarks(threadIds.size());
111
112 return getContext().getContentResolver().query(
113 Telephony.Sms.CONTENT_URI,
114 /* projection = */ null,
115 selection,
116 threadIds.toArray(new String[threadIds.size()]),
117 Telephony.Sms.DEFAULT_SORT_ORDER
118 + " LIMIT " + mMaxToRetrieve);
119 }
120
121 @Override
122 protected void onStartLoading() {
123 super.onStartLoading();
124
125 if (mData != null) {
126 deliverResult(mData);
127 }
128
129 if (takeContentChanged() || mData == null) {
130 forceLoad();
131 }
132 }
133
134 @Override
135 protected void onStopLoading() {
136 // Attempt to cancel the current load task if possible.
137 cancelLoad();
138 }
139
140 @Override
141 public void deliverResult(List<ContactInteraction> data) {
142 mData = data;
143 if (isStarted()) {
144 super.deliverResult(data);
145 }
146 }
147
148 @Override
149 protected void onReset() {
150 super.onReset();
151
152 // Ensure the loader is stopped
153 onStopLoading();
154 if (mData != null) {
155 mData.clear();
156 }
157 }
158}