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 */ 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 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 try { 70 threadIdStrings.add(String.valueOf( 71 Telephony.Threads.getOrCreateThreadId(getContext(), phone))); 72 } catch (Exception e) { 73 // Do nothing. Telephony.Threads.getOrCreateThreadId() throws exceptions when 74 // it can't find/create a threadId (b/17657656). 75 } 76 } 77 78 // Query the SMS database for the threads 79 Cursor cursor = getSmsCursorFromThreads(threadIdStrings); 80 if (cursor != null) { 81 try { 82 List<ContactInteraction> interactions = new ArrayList<>(); 83 while (cursor.moveToNext()) { 84 ContentValues values = new ContentValues(); 85 DatabaseUtils.cursorRowToContentValues(cursor, values); 86 interactions.add(new SmsInteraction(values)); 87 } 88 89 return interactions; 90 } finally { 91 cursor.close(); 92 } 93 } 94 95 return Collections.emptyList(); 96 } 97 98 /** 99 * Return the most recent messages between a list of threads 100 */ 101 private Cursor getSmsCursorFromThreads(List<String> threadIds) { 102 if (threadIds.size() == 0) { 103 return null; 104 } 105 String selection = Telephony.Sms.THREAD_ID + " IN " 106 + ContactInteractionUtil.questionMarks(threadIds.size()); 107 108 return getContext().getContentResolver().query( 109 Telephony.Sms.CONTENT_URI, 110 /* projection = */ null, 111 selection, 112 threadIds.toArray(new String[threadIds.size()]), 113 Telephony.Sms.DEFAULT_SORT_ORDER 114 + " LIMIT " + mMaxToRetrieve); 115 } 116 117 @Override 118 protected void onStartLoading() { 119 super.onStartLoading(); 120 121 if (mData != null) { 122 deliverResult(mData); 123 } 124 125 if (takeContentChanged() || mData == null) { 126 forceLoad(); 127 } 128 } 129 130 @Override 131 protected void onStopLoading() { 132 // Attempt to cancel the current load task if possible. 133 cancelLoad(); 134 } 135 136 @Override 137 public void deliverResult(List<ContactInteraction> data) { 138 mData = data; 139 if (isStarted()) { 140 super.deliverResult(data); 141 } 142 } 143 144 @Override 145 protected void onReset() { 146 super.onReset(); 147 148 // Ensure the loader is stopped 149 onStopLoading(); 150 if (mData != null) { 151 mData.clear(); 152 } 153 } 154 } 155