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