1b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos/* 2b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * Copyright (C) 2014 The Android Open Source Project 3b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * 4b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * Licensed under the Apache License, Version 2.0 (the "License"); 5b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * you may not use this file except in compliance with the License. 6b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * You may obtain a copy of the License at 7b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * 8b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * http://www.apache.org/licenses/LICENSE-2.0 9b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * 10b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * Unless required by applicable law or agreed to in writing, software 11b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * distributed under the License is distributed on an "AS IS" BASIS, 12b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * See the License for the specific language governing permissions and 14b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * limitations under the License. 15b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos */ 16b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulospackage com.android.contacts.interactions; 17b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 18b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport android.content.AsyncTaskLoader; 19b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport android.content.ContentValues; 20b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport android.content.Context; 21b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport android.content.pm.PackageManager; 22b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport android.database.Cursor; 23b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport android.database.DatabaseUtils; 24b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport android.provider.Telephony; 25b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport android.util.Log; 26b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 27047197228d7e1112efdd5524f3e3b58926b848f6Wenyi Wangimport com.android.contacts.common.compat.TelephonyThreadsCompat; 28047197228d7e1112efdd5524f3e3b58926b848f6Wenyi Wang 29b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport java.util.ArrayList; 30b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport java.util.Collections; 31b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulosimport java.util.List; 32b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 33b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos/** 34b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * Loads the most recent sms between the passed in phone numbers. 35b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * 36b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * This is a two part process. The first step is retrieving the threadIds for each of the phone 37b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * numbers using fuzzy matching. The next step is to run another query against these threadIds 38b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * to retrieve the actual sms. 39b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos */ 40b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulospublic class SmsInteractionsLoader extends AsyncTaskLoader<List<ContactInteraction>> { 41b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 42b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos private static final String TAG = SmsInteractionsLoader.class.getSimpleName(); 43b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 44b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos private String[] mPhoneNums; 45b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos private int mMaxToRetrieve; 46b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos private List<ContactInteraction> mData; 47b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 48b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos /** 49b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * Loads a list of SmsInteraction from the supplied phone numbers. 50b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos */ 51b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos public SmsInteractionsLoader(Context context, String[] phoneNums, 52b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos int maxToRetrieve) { 53b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos super(context); 54b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos Log.v(TAG, "SmsInteractionsLoader"); 55b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos mPhoneNums = phoneNums; 56b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos mMaxToRetrieve = maxToRetrieve; 57b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 58b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 59b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos @Override 60b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos public List<ContactInteraction> loadInBackground() { 61b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos Log.v(TAG, "loadInBackground"); 62b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos // Confirm the device has Telephony and numbers were provided before proceeding 63b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY) 64b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos || mPhoneNums == null || mPhoneNums.length == 0) { 65b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos return Collections.emptyList(); 66b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 67b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 68b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos // Retrieve the thread IDs 69b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos List<String> threadIdStrings = new ArrayList<>(); 70b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos for (String phone : mPhoneNums) { 718a3fe52802135be2402a5b216325615fb796a509Walter Jang // TODO: the phone numbers added to the ContactInteraction result should retain their 728a3fe52802135be2402a5b216325615fb796a509Walter Jang // original formatting since TalkBack is not reading the normalized numbers correctly 73ae6c7170daf92a91b9a189a985cd6d06817d92bdBrian Attwell try { 74ae6c7170daf92a91b9a189a985cd6d06817d92bdBrian Attwell threadIdStrings.add(String.valueOf( 75047197228d7e1112efdd5524f3e3b58926b848f6Wenyi Wang TelephonyThreadsCompat.getOrCreateThreadId(getContext(), phone))); 76ae6c7170daf92a91b9a189a985cd6d06817d92bdBrian Attwell } catch (Exception e) { 77ae6c7170daf92a91b9a189a985cd6d06817d92bdBrian Attwell // Do nothing. Telephony.Threads.getOrCreateThreadId() throws exceptions when 78ae6c7170daf92a91b9a189a985cd6d06817d92bdBrian Attwell // it can't find/create a threadId (b/17657656). 79ae6c7170daf92a91b9a189a985cd6d06817d92bdBrian Attwell } 80b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 81b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 82b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos // Query the SMS database for the threads 83b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos Cursor cursor = getSmsCursorFromThreads(threadIdStrings); 84899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos if (cursor != null) { 85899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos try { 86899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos List<ContactInteraction> interactions = new ArrayList<>(); 87899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos while (cursor.moveToNext()) { 88899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos ContentValues values = new ContentValues(); 89899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos DatabaseUtils.cursorRowToContentValues(cursor, values); 90899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos interactions.add(new SmsInteraction(values)); 91899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 92b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 93899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return interactions; 94899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } finally { 95899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos cursor.close(); 96899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos } 97b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 98b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 99899aa21e911ee7170beab228d44d7fed68c414e4Paul Soulos return Collections.emptyList(); 100b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 101b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 102b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos /** 103b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos * Return the most recent messages between a list of threads 104b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos */ 105b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos private Cursor getSmsCursorFromThreads(List<String> threadIds) { 106ae6c7170daf92a91b9a189a985cd6d06817d92bdBrian Attwell if (threadIds.size() == 0) { 107ae6c7170daf92a91b9a189a985cd6d06817d92bdBrian Attwell return null; 108ae6c7170daf92a91b9a189a985cd6d06817d92bdBrian Attwell } 109b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos String selection = Telephony.Sms.THREAD_ID + " IN " 110b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos + ContactInteractionUtil.questionMarks(threadIds.size()); 111b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 112b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos return getContext().getContentResolver().query( 113b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos Telephony.Sms.CONTENT_URI, 114b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos /* projection = */ null, 115b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos selection, 116b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos threadIds.toArray(new String[threadIds.size()]), 117b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos Telephony.Sms.DEFAULT_SORT_ORDER 118b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos + " LIMIT " + mMaxToRetrieve); 119b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 120b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 121b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos @Override 122b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos protected void onStartLoading() { 123b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos super.onStartLoading(); 124b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 125b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos if (mData != null) { 126b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos deliverResult(mData); 127b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 128b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 129b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos if (takeContentChanged() || mData == null) { 130b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos forceLoad(); 131b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 132b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 133b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 134b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos @Override 135b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos protected void onStopLoading() { 136b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos // Attempt to cancel the current load task if possible. 137b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos cancelLoad(); 138b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 139b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 140b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos @Override 141b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos public void deliverResult(List<ContactInteraction> data) { 142b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos mData = data; 143b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos if (isStarted()) { 144b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos super.deliverResult(data); 145b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 146b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 147b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 148b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos @Override 149b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos protected void onReset() { 150b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos super.onReset(); 151b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos 152b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos // Ensure the loader is stopped 153b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos onStopLoading(); 154b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos if (mData != null) { 155b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos mData.clear(); 156b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 157b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos } 158b3054e551173887029c55cb10b83f1afb7f8a6fePaul Soulos} 159