• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 package com.android.contacts.interactions;
17 
18 import android.content.AsyncTaskLoader;
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.database.Cursor;
23 import android.database.DatabaseUtils;
24 import android.provider.Telephony;
25 import android.util.Log;
26 
27 import com.android.contacts.common.compat.TelephonyThreadsCompat;
28 
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import 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  */
40 public 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      */
SmsInteractionsLoader(Context context, String[] phoneNums, int maxToRetrieve)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
loadInBackground()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) {
71             // TODO: the phone numbers added to the ContactInteraction result should retain their
72             // original formatting since TalkBack is not reading the normalized numbers correctly
73             try {
74                 threadIdStrings.add(String.valueOf(
75                         TelephonyThreadsCompat.getOrCreateThreadId(getContext(), phone)));
76             } catch (Exception e) {
77                 // Do nothing. Telephony.Threads.getOrCreateThreadId() throws exceptions when
78                 // it can't find/create a threadId (b/17657656).
79             }
80         }
81 
82         // Query the SMS database for the threads
83         Cursor cursor = getSmsCursorFromThreads(threadIdStrings);
84         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                 }
92 
93                 return interactions;
94             } finally {
95                 cursor.close();
96             }
97         }
98 
99         return Collections.emptyList();
100     }
101 
102     /**
103      * Return the most recent messages between a list of threads
104      */
getSmsCursorFromThreads(List<String> threadIds)105     private Cursor getSmsCursorFromThreads(List<String> threadIds) {
106         if (threadIds.size() == 0) {
107             return null;
108         }
109         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
onStartLoading()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
onStopLoading()135     protected void onStopLoading() {
136         // Attempt to cancel the current load task if possible.
137         cancelLoad();
138     }
139 
140     @Override
deliverResult(List<ContactInteraction> data)141     public void deliverResult(List<ContactInteraction> data) {
142         mData = data;
143         if (isStarted()) {
144             super.deliverResult(data);
145         }
146     }
147 
148     @Override
onReset()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 }
159