1 /* 2 * Copyright (C) 2015 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 17 package com.android.messaging.datamodel.data; 18 19 import android.app.LoaderManager; 20 import android.content.Context; 21 import android.content.Loader; 22 import android.database.Cursor; 23 import android.os.Bundle; 24 25 import com.android.messaging.datamodel.BoundCursorLoader; 26 import com.android.messaging.datamodel.FrequentContactsCursorBuilder; 27 import com.android.messaging.datamodel.MessagingContentProvider; 28 import com.android.messaging.datamodel.binding.BindableData; 29 import com.android.messaging.datamodel.binding.BindingBase; 30 import com.android.messaging.sms.MmsConfig; 31 import com.android.messaging.util.Assert; 32 import com.android.messaging.util.ContactUtil; 33 import com.android.messaging.util.LogUtil; 34 35 /** 36 * Class to access phone contacts. 37 * The caller is responsible for ensuring that the app has READ_CONTACTS permission (see 38 * {@link ContactUtil#hasReadContactsPermission()}) before instantiating this class. 39 */ 40 public class ContactPickerData extends BindableData implements 41 LoaderManager.LoaderCallbacks<Cursor> { 42 public interface ContactPickerDataListener { onAllContactsCursorUpdated(Cursor data)43 void onAllContactsCursorUpdated(Cursor data); onFrequentContactsCursorUpdated(Cursor data)44 void onFrequentContactsCursorUpdated(Cursor data); onContactCustomColorLoaded(ContactPickerData data)45 void onContactCustomColorLoaded(ContactPickerData data); 46 } 47 48 private static final String BINDING_ID = "bindingId"; 49 private final Context mContext; 50 private LoaderManager mLoaderManager; 51 private ContactPickerDataListener mListener; 52 private final FrequentContactsCursorBuilder mFrequentContactsCursorBuilder; 53 ContactPickerData(final Context context, final ContactPickerDataListener listener)54 public ContactPickerData(final Context context, final ContactPickerDataListener listener) { 55 mListener = listener; 56 mContext = context; 57 mFrequentContactsCursorBuilder = new FrequentContactsCursorBuilder(); 58 } 59 60 private static final int ALL_CONTACTS_LOADER = 1; 61 private static final int FREQUENT_CONTACTS_LOADER = 2; 62 private static final int PARTICIPANT_LOADER = 3; 63 64 @Override onCreateLoader(final int id, final Bundle args)65 public Loader<Cursor> onCreateLoader(final int id, final Bundle args) { 66 final String bindingId = args.getString(BINDING_ID); 67 // Check if data still bound to the requesting ui element 68 if (isBound(bindingId)) { 69 switch (id) { 70 case ALL_CONTACTS_LOADER: 71 return ContactUtil.getPhones(mContext) 72 .createBoundCursorLoader(bindingId); 73 case FREQUENT_CONTACTS_LOADER: 74 return ContactUtil.getFrequentContacts(mContext) 75 .createBoundCursorLoader(bindingId); 76 case PARTICIPANT_LOADER: 77 return new BoundCursorLoader(bindingId, mContext, 78 MessagingContentProvider.PARTICIPANTS_URI, 79 ParticipantData.ParticipantsQuery.PROJECTION, null, null, null); 80 default: 81 Assert.fail("Unknown loader id for contact picker!"); 82 break; 83 } 84 } else { 85 LogUtil.w(LogUtil.BUGLE_TAG, "Loader created after unbinding the contacts list"); 86 } 87 return null; 88 } 89 90 /** 91 * {@inheritDoc} 92 */ 93 @Override onLoadFinished(final Loader<Cursor> loader, final Cursor data)94 public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) { 95 final BoundCursorLoader cursorLoader = (BoundCursorLoader) loader; 96 if (isBound(cursorLoader.getBindingId())) { 97 switch (loader.getId()) { 98 case ALL_CONTACTS_LOADER: 99 mListener.onAllContactsCursorUpdated(data); 100 mFrequentContactsCursorBuilder.setAllContacts(data); 101 break; 102 case FREQUENT_CONTACTS_LOADER: 103 mFrequentContactsCursorBuilder.setFrequents(data); 104 break; 105 case PARTICIPANT_LOADER: 106 mListener.onContactCustomColorLoaded(this); 107 break; 108 default: 109 Assert.fail("Unknown loader id for contact picker!"); 110 break; 111 } 112 113 if (loader.getId() != PARTICIPANT_LOADER) { 114 // The frequent contacts cursor to be used in the UI depends on results from both 115 // all contacts and frequent contacts loader, and we don't know which will finish 116 // first. Therefore, try to build the cursor and notify the listener if it's 117 // successfully built. 118 final Cursor frequentContactsCursor = mFrequentContactsCursorBuilder.build(); 119 if (frequentContactsCursor != null) { 120 mListener.onFrequentContactsCursorUpdated(frequentContactsCursor); 121 } 122 } 123 } else { 124 LogUtil.w(LogUtil.BUGLE_TAG, "Loader finished after unbinding the contacts list"); 125 } 126 } 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override onLoaderReset(final Loader<Cursor> loader)132 public void onLoaderReset(final Loader<Cursor> loader) { 133 final BoundCursorLoader cursorLoader = (BoundCursorLoader) loader; 134 if (isBound(cursorLoader.getBindingId())) { 135 switch (loader.getId()) { 136 case ALL_CONTACTS_LOADER: 137 mListener.onAllContactsCursorUpdated(null); 138 mFrequentContactsCursorBuilder.setAllContacts(null); 139 break; 140 case FREQUENT_CONTACTS_LOADER: 141 mListener.onFrequentContactsCursorUpdated(null); 142 mFrequentContactsCursorBuilder.setFrequents(null); 143 break; 144 case PARTICIPANT_LOADER: 145 mListener.onContactCustomColorLoaded(this); 146 break; 147 default: 148 Assert.fail("Unknown loader id for contact picker!"); 149 break; 150 } 151 } else { 152 LogUtil.w(LogUtil.BUGLE_TAG, "Loader reset after unbinding the contacts list"); 153 } 154 } 155 init(final LoaderManager loaderManager, final BindingBase<ContactPickerData> binding)156 public void init(final LoaderManager loaderManager, 157 final BindingBase<ContactPickerData> binding) { 158 final Bundle args = new Bundle(); 159 args.putString(BINDING_ID, binding.getBindingId()); 160 mLoaderManager = loaderManager; 161 mLoaderManager.initLoader(ALL_CONTACTS_LOADER, args, this); 162 mLoaderManager.initLoader(FREQUENT_CONTACTS_LOADER, args, this); 163 mLoaderManager.initLoader(PARTICIPANT_LOADER, args, this); 164 } 165 166 @Override unregisterListeners()167 protected void unregisterListeners() { 168 mListener = null; 169 170 171 // This could be null if we bind but the caller doesn't init the BindableData 172 if (mLoaderManager != null) { 173 mLoaderManager.destroyLoader(ALL_CONTACTS_LOADER); 174 mLoaderManager.destroyLoader(FREQUENT_CONTACTS_LOADER); 175 mLoaderManager.destroyLoader(PARTICIPANT_LOADER); 176 mLoaderManager = null; 177 } 178 mFrequentContactsCursorBuilder.resetBuilder(); 179 } 180 isTooManyParticipants(final int participantCount)181 public static boolean isTooManyParticipants(final int participantCount) { 182 // When creating a conversation, the conversation will be created using the system's 183 // default SIM, so use the default MmsConfig's recipient limit. 184 return (participantCount > MmsConfig.get(ParticipantData.DEFAULT_SELF_SUB_ID) 185 .getRecipientLimit()); 186 } 187 getCanAddMoreParticipants(final int participantCount)188 public static boolean getCanAddMoreParticipants(final int participantCount) { 189 // When creating a conversation, the conversation will be created using the system's 190 // default SIM, so use the default MmsConfig's recipient limit. 191 return (participantCount < MmsConfig.get(ParticipantData.DEFAULT_SELF_SUB_ID) 192 .getRecipientLimit()); 193 } 194 } 195