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