1 /* 2 * Copyright (C) 2012 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.contacts.util; 18 19 import android.accounts.Account; 20 import android.app.Activity; 21 import android.app.Fragment; 22 import android.content.ActivityNotFoundException; 23 import android.content.AsyncTaskLoader; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.graphics.drawable.Drawable; 29 import android.provider.ContactsContract.Contacts; 30 import android.provider.ContactsContract.Intents; 31 import androidx.localbroadcastmanager.content.LocalBroadcastManager; 32 import android.text.TextUtils; 33 import android.util.Log; 34 import android.widget.Toast; 35 36 import com.android.contacts.R; 37 import com.android.contacts.activities.ContactEditorActivity; 38 import com.android.contacts.list.AccountFilterActivity; 39 import com.android.contacts.list.ContactListFilter; 40 import com.android.contacts.list.ContactListFilterController; 41 import com.android.contacts.model.AccountTypeManager; 42 import com.android.contacts.model.Contact; 43 import com.android.contacts.model.account.AccountDisplayInfo; 44 import com.android.contacts.model.account.AccountDisplayInfoFactory; 45 import com.android.contacts.model.account.AccountInfo; 46 import com.android.contacts.model.account.AccountType; 47 import com.android.contacts.model.account.AccountWithDataSet; 48 import com.android.contacts.preference.ContactsPreferences; 49 import com.android.contacts.util.concurrent.ContactsExecutors; 50 import com.android.contacts.util.concurrent.ListenableFutureLoader; 51 import com.android.contactsbind.ObjectFactory; 52 53 import com.google.common.base.Function; 54 import com.google.common.base.Predicate; 55 import com.google.common.collect.Lists; 56 import com.google.common.util.concurrent.Futures; 57 import com.google.common.util.concurrent.ListenableFuture; 58 59 import java.util.ArrayList; 60 import java.util.List; 61 62 import javax.annotation.Nullable; 63 64 /** 65 * Utility class for account filter manipulation. 66 */ 67 public class AccountFilterUtil { 68 private static final String TAG = AccountFilterUtil.class.getSimpleName(); 69 70 /** 71 * Launches account filter setting Activity using 72 * {@link Fragment#startActivityForResult(Intent, int)}. 73 * 74 * @param requestCode requestCode for {@link Activity#startActivityForResult(Intent, int)} 75 * @param currentFilter currently-selected filter, so that it can be displayed as activated. 76 */ startAccountFilterActivityForResult( Fragment fragment, int requestCode, ContactListFilter currentFilter)77 public static void startAccountFilterActivityForResult( 78 Fragment fragment, int requestCode, ContactListFilter currentFilter) { 79 final Activity activity = fragment.getActivity(); 80 if (activity != null) { 81 final Intent intent = new Intent(activity, AccountFilterActivity.class); 82 fragment.startActivityForResult(intent, requestCode); 83 } else { 84 Log.w(TAG, "getActivity() returned null. Ignored"); 85 } 86 } 87 88 /** 89 * Useful method to handle onActivityResult() for 90 * {@link #startAccountFilterActivityForResult(Fragment, int, ContactListFilter)}. 91 * 92 * This will update filter via a given ContactListFilterController. 93 */ handleAccountFilterResult( ContactListFilterController filterController, int resultCode, Intent data)94 public static void handleAccountFilterResult( 95 ContactListFilterController filterController, int resultCode, Intent data) { 96 if (resultCode == Activity.RESULT_OK) { 97 final ContactListFilter filter = (ContactListFilter) 98 data.getParcelableExtra(AccountFilterActivity.EXTRA_CONTACT_LIST_FILTER); 99 if (filter == null) { 100 return; 101 } 102 if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) { 103 filterController.selectCustomFilter(); 104 } else { 105 filterController.setContactListFilter(filter, /* persistent */ 106 filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 107 } 108 } 109 } 110 111 /** 112 * Loads a list of contact list filters 113 */ 114 public static class FilterLoader extends ListenableFutureLoader<List<ContactListFilter>> { 115 private AccountTypeManager mAccountTypeManager; 116 private DeviceLocalAccountTypeFactory mDeviceLocalFactory; 117 FilterLoader(Context context)118 public FilterLoader(Context context) { 119 super(context, new IntentFilter(AccountTypeManager.BROADCAST_ACCOUNTS_CHANGED)); 120 mAccountTypeManager = AccountTypeManager.getInstance(context); 121 mDeviceLocalFactory = ObjectFactory.getDeviceLocalAccountTypeFactory(context); 122 } 123 124 125 @Override loadData()126 protected ListenableFuture<List<ContactListFilter>> loadData() { 127 return Futures.transform(mAccountTypeManager.filterAccountsAsync( 128 AccountTypeManager.writableFilter()), 129 new Function<List<AccountInfo>, List<ContactListFilter>>() { 130 @Override 131 public List<ContactListFilter> apply(List<AccountInfo> input) { 132 return getFiltersForAccounts(input); 133 } 134 }, ContactsExecutors.getDefaultThreadPoolExecutor()); 135 } 136 getFiltersForAccounts(List<AccountInfo> accounts)137 private List<ContactListFilter> getFiltersForAccounts(List<AccountInfo> accounts) { 138 final ArrayList<ContactListFilter> accountFilters = new ArrayList<>(); 139 AccountInfo.sortAccounts(getDefaultAccount(getContext()), accounts); 140 141 for (AccountInfo accountInfo : accounts) { 142 final AccountType accountType = accountInfo.getType(); 143 final AccountWithDataSet account = accountInfo.getAccount(); 144 if ((accountType.isExtension() || 145 DeviceLocalAccountTypeFactory.Util.isLocalAccountType( 146 mDeviceLocalFactory, account.type)) && 147 !account.hasData(getContext())) { 148 // Hide extensions and device accounts with no raw_contacts. 149 continue; 150 } 151 final Drawable icon = accountType != null ? 152 accountType.getDisplayIcon(getContext()) : null; 153 if (DeviceLocalAccountTypeFactory.Util.isLocalAccountType( 154 mDeviceLocalFactory, account.type)) { 155 accountFilters.add(ContactListFilter.createDeviceContactsFilter(icon, account)); 156 } else { 157 accountFilters.add(ContactListFilter.createAccountFilter( 158 account.type, account.name, account.dataSet, icon)); 159 } 160 } 161 162 return accountFilters; 163 } 164 } 165 166 private static AccountWithDataSet getDefaultAccount(Context context) { 167 return new ContactsPreferences(context).getDefaultAccount(); 168 } 169 170 /** 171 * Returns a {@link ContactListFilter} of type 172 * {@link ContactListFilter#FILTER_TYPE_ALL_ACCOUNTS}, or if a custom "Contacts to display" 173 * filter has been set, then one of type {@link ContactListFilter#FILTER_TYPE_CUSTOM}. 174 */ 175 public static ContactListFilter createContactsFilter(Context context) { 176 final int filterType = 177 ContactListFilterController.getInstance(context).isCustomFilterPersisted() 178 ? ContactListFilter.FILTER_TYPE_CUSTOM 179 : ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS; 180 return ContactListFilter.createFilterWithType(filterType); 181 } 182 183 /** 184 * Start editor intent; and if filter is an account filter, we pass account info to editor so 185 * as to create a contact in that account. 186 */ 187 public static void startEditorIntent(Context context, Intent src, ContactListFilter filter) { 188 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 189 intent.putExtras(src); 190 191 // If we are in account view, we pass the account explicitly in order to 192 // create contact in the account. This will prevent the default account dialog 193 // from being displayed. 194 if (!isAllContactsFilter(filter) && filter.accountName != null 195 && filter.accountType != null) { 196 final Account account = new Account(filter.accountName, filter.accountType); 197 intent.putExtra(Intents.Insert.EXTRA_ACCOUNT, account); 198 intent.putExtra(Intents.Insert.EXTRA_DATA_SET, filter.dataSet); 199 } else if (isDeviceContactsFilter(filter)) { 200 intent.putExtra(ContactEditorActivity.EXTRA_ACCOUNT_WITH_DATA_SET, 201 filter.toAccountWithDataSet()); 202 } 203 204 try { 205 ImplicitIntentsUtil.startActivityInApp(context, intent); 206 } catch (ActivityNotFoundException ex) { 207 Toast.makeText(context, R.string.missing_app, Toast.LENGTH_SHORT).show(); 208 } 209 } 210 211 public static boolean isAllContactsFilter(ContactListFilter filter) { 212 return filter != null && filter.isContactsFilterType(); 213 } 214 215 public static boolean isDeviceContactsFilter(ContactListFilter filter) { 216 return filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS; 217 } 218 219 /** 220 * Returns action bar title for filter and returns default title "Contacts" if filter is empty. 221 */ 222 public static String getActionBarTitleForFilter(Context context, ContactListFilter filter) { 223 if (filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS) { 224 return context.getString(R.string.account_phone); 225 } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT && 226 !TextUtils.isEmpty(filter.accountName)) { 227 return getActionBarTitleForAccount(context, filter); 228 } 229 return context.getString(R.string.contactsList); 230 } 231 232 private static String getActionBarTitleForAccount(Context context, ContactListFilter filter) { 233 final AccountInfo info = AccountTypeManager.getInstance(context) 234 .getAccountInfoForAccount(filter.toAccountWithDataSet()); 235 if (info == null) { 236 return context.getString(R.string.contactsList); 237 } 238 239 if (info.hasGoogleAccountType()) { 240 return context.getString(R.string.title_from_google); 241 } 242 return context.getString(R.string.title_from_other_accounts, 243 info.getNameLabel().toString()); 244 } 245 } 246