• 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.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         if (Log.isLoggable(TAG, Log.VERBOSE)) {
55             Log.v(TAG, "SmsInteractionsLoader");
56         }
57         mPhoneNums = phoneNums;
58         mMaxToRetrieve = maxToRetrieve;
59     }
60 
61     @Override
loadInBackground()62     public List<ContactInteraction> loadInBackground() {
63         if (Log.isLoggable(TAG, Log.VERBOSE)) {
64             Log.v(TAG, "loadInBackground");
65         }
66         // Confirm the device has Telephony and numbers were provided before proceeding
67         if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
68                 || mPhoneNums == null || mPhoneNums.length == 0) {
69             return Collections.emptyList();
70         }
71 
72         // Retrieve the thread IDs
73         List<String> threadIdStrings = new ArrayList<>();
74         for (String phone : mPhoneNums) {
75             // TODO: the phone numbers added to the ContactInteraction result should retain their
76             // original formatting since TalkBack is not reading the normalized numbers correctly
77             try {
78                 threadIdStrings.add(String.valueOf(
79                         TelephonyThreadsCompat.getOrCreateThreadId(getContext(), phone)));
80             } catch (Exception e) {
81                 // Do nothing. Telephony.Threads.getOrCreateThreadId() throws exceptions when
82                 // it can't find/create a threadId (b/17657656).
83             }
84         }
85 
86         // Query the SMS database for the threads
87         Cursor cursor = getSmsCursorFromThreads(threadIdStrings);
88         if (cursor != null) {
89             try {
90                 List<ContactInteraction> interactions = new ArrayList<>();
91                 while (cursor.moveToNext()) {
92                     ContentValues values = new ContentValues();
93                     DatabaseUtils.cursorRowToContentValues(cursor, values);
94                     interactions.add(new SmsInteraction(values));
95                 }
96 
97                 return interactions;
98             } finally {
99                 cursor.close();
100             }
101         }
102 
103         return Collections.emptyList();
104     }
105 
106     /**
107      * Return the most recent messages between a list of threads
108      */
getSmsCursorFromThreads(List<String> threadIds)109     private Cursor getSmsCursorFromThreads(List<String> threadIds) {
110         if (threadIds.size() == 0) {
111             return null;
112         }
113         String selection = Telephony.Sms.THREAD_ID + " IN "
114                 + ContactInteractionUtil.questionMarks(threadIds.size());
115 
116         return getContext().getContentResolver().query(
117                 Telephony.Sms.CONTENT_URI,
118                 /* projection = */ null,
119                 selection,
120                 threadIds.toArray(new String[threadIds.size()]),
121                 Telephony.Sms.DEFAULT_SORT_ORDER
122                         + " LIMIT " + mMaxToRetrieve);
123     }
124 
125     @Override
onStartLoading()126     protected void onStartLoading() {
127         super.onStartLoading();
128 
129         if (mData != null) {
130             deliverResult(mData);
131         }
132 
133         if (takeContentChanged() || mData == null) {
134             forceLoad();
135         }
136     }
137 
138     @Override
onStopLoading()139     protected void onStopLoading() {
140         // Attempt to cancel the current load task if possible.
141         cancelLoad();
142     }
143 
144     @Override
deliverResult(List<ContactInteraction> data)145     public void deliverResult(List<ContactInteraction> data) {
146         mData = data;
147         if (isStarted()) {
148             super.deliverResult(data);
149         }
150     }
151 
152     @Override
onReset()153     protected void onReset() {
154         super.onReset();
155 
156         // Ensure the loader is stopped
157         onStopLoading();
158         if (mData != null) {
159             mData.clear();
160         }
161     }
162 }
163