blob: 218604cebedb2dffc01f8609adf76313158f71cb [file] [log] [blame]
Chiao Chengbeca8562012-11-28 18:06:44 -08001/*
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
Gary Mai69c182a2016-12-05 13:07:03 -080017package com.android.contacts.util;
Chiao Chengbeca8562012-11-28 18:06:44 -080018
Wenyi Wang79675452016-08-17 10:43:28 -070019import android.accounts.Account;
Chiao Chengbeca8562012-11-28 18:06:44 -080020import android.app.Activity;
Walter Jangf2cad222016-07-14 19:49:06 +000021import android.app.Fragment;
Wenyi Wang79675452016-08-17 10:43:28 -070022import android.content.ActivityNotFoundException;
Wenyi Wangb3186762016-04-17 17:29:31 -070023import android.content.AsyncTaskLoader;
Marcus Hagerottb61e6542016-12-12 12:44:11 -080024import android.content.BroadcastReceiver;
Chiao Chengbeca8562012-11-28 18:06:44 -080025import android.content.Context;
26import android.content.Intent;
Marcus Hagerottb61e6542016-12-12 12:44:11 -080027import android.content.IntentFilter;
Wenyi Wangb3186762016-04-17 17:29:31 -070028import android.graphics.drawable.Drawable;
Wenyi Wang79675452016-08-17 10:43:28 -070029import android.provider.ContactsContract.Contacts;
30import android.provider.ContactsContract.Intents;
Marcus Hagerottb61e6542016-12-12 12:44:11 -080031import android.support.v4.content.LocalBroadcastManager;
Wenyi Wangcfe94652016-10-03 21:05:04 -070032import android.text.TextUtils;
Chiao Chengbeca8562012-11-28 18:06:44 -080033import android.util.Log;
Wenyi Wang79675452016-08-17 10:43:28 -070034import android.widget.Toast;
Chiao Chengbeca8562012-11-28 18:06:44 -080035
Wenyi Wang79675452016-08-17 10:43:28 -070036import com.android.contacts.R;
Gary Mai363af602016-09-28 10:01:23 -070037import com.android.contacts.activities.ContactEditorActivity;
Gary Mai69c182a2016-12-05 13:07:03 -080038import com.android.contacts.list.AccountFilterActivity;
39import com.android.contacts.list.ContactListFilter;
40import com.android.contacts.list.ContactListFilterController;
41import com.android.contacts.model.AccountTypeManager;
Marcus Hagerottb61e6542016-12-12 12:44:11 -080042import com.android.contacts.model.Contact;
Gary Mai69c182a2016-12-05 13:07:03 -080043import com.android.contacts.model.account.AccountDisplayInfo;
44import com.android.contacts.model.account.AccountDisplayInfoFactory;
Marcus Hagerott75895e72016-12-12 17:21:57 -080045import com.android.contacts.model.account.AccountInfo;
Gary Mai69c182a2016-12-05 13:07:03 -080046import com.android.contacts.model.account.AccountType;
47import com.android.contacts.model.account.AccountWithDataSet;
48import com.android.contacts.preference.ContactsPreferences;
Marcus Hagerottb61e6542016-12-12 12:44:11 -080049import com.android.contacts.util.concurrent.ContactsExecutors;
50import com.android.contacts.util.concurrent.ListenableFutureLoader;
Marcus Hagerottbc510842016-09-09 19:29:45 -070051import com.android.contactsbind.ObjectFactory;
Gary Maiac333592016-09-28 17:27:40 -070052
Marcus Hagerottb61e6542016-12-12 12:44:11 -080053import com.google.common.base.Function;
54import com.google.common.base.Predicate;
Wenyi Wangb3186762016-04-17 17:29:31 -070055import com.google.common.collect.Lists;
Marcus Hagerottb61e6542016-12-12 12:44:11 -080056import com.google.common.util.concurrent.Futures;
57import com.google.common.util.concurrent.ListenableFuture;
Wenyi Wangb3186762016-04-17 17:29:31 -070058
59import java.util.ArrayList;
60import java.util.List;
Chiao Chengbeca8562012-11-28 18:06:44 -080061
Marcus Hagerottb61e6542016-12-12 12:44:11 -080062import javax.annotation.Nullable;
63
Chiao Chengbeca8562012-11-28 18:06:44 -080064/**
65 * Utility class for account filter manipulation.
66 */
67public class AccountFilterUtil {
68 private static final String TAG = AccountFilterUtil.class.getSimpleName();
69
Walter Jangf2cad222016-07-14 19:49:06 +000070 /**
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 */
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 }
Wenyi Wang1aa9d9f2016-04-22 16:31:51 -070087
Chiao Chengbeca8562012-11-28 18:06:44 -080088 /**
Walter Jangf2cad222016-07-14 19:49:06 +000089 * Useful method to handle onActivityResult() for
90 * {@link #startAccountFilterActivityForResult(Fragment, int, ContactListFilter)}.
91 *
Chiao Chengbeca8562012-11-28 18:06:44 -080092 * This will update filter via a given ContactListFilterController.
93 */
94 public static void handleAccountFilterResult(
95 ContactListFilterController filterController, int resultCode, Intent data) {
96 if (resultCode == Activity.RESULT_OK) {
97 final ContactListFilter filter = (ContactListFilter)
Walter Jangf2cad222016-07-14 19:49:06 +000098 data.getParcelableExtra(AccountFilterActivity.EXTRA_CONTACT_LIST_FILTER);
Chiao Chengbeca8562012-11-28 18:06:44 -080099 if (filter == null) {
100 return;
101 }
102 if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
103 filterController.selectCustomFilter();
104 } else {
Wenyi Wang1cfe9c62016-06-01 20:49:13 -0700105 filterController.setContactListFilter(filter, /* persistent */
106 filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
Chiao Chengbeca8562012-11-28 18:06:44 -0800107 }
108 }
109 }
Wenyi Wangb3186762016-04-17 17:29:31 -0700110
111 /**
112 * Loads a list of contact list filters
113 */
Marcus Hagerottb61e6542016-12-12 12:44:11 -0800114 public static class FilterLoader extends ListenableFutureLoader<List<ContactListFilter>> {
115 private AccountTypeManager mAccountTypeManager;
Marcus Hagerottbc510842016-09-09 19:29:45 -0700116 private DeviceLocalAccountTypeFactory mDeviceLocalFactory;
Wenyi Wangb3186762016-04-17 17:29:31 -0700117
118 public FilterLoader(Context context) {
Marcus Hagerott75895e72016-12-12 17:21:57 -0800119 super(context, new IntentFilter(AccountTypeManager.BROADCAST_ACCOUNTS_CHANGED));
Marcus Hagerottb61e6542016-12-12 12:44:11 -0800120 mAccountTypeManager = AccountTypeManager.getInstance(context);
Marcus Hagerottbc510842016-09-09 19:29:45 -0700121 mDeviceLocalFactory = ObjectFactory.getDeviceLocalAccountTypeFactory(context);
Wenyi Wangb3186762016-04-17 17:29:31 -0700122 }
123
Tingting Wang0ac73ba2016-07-05 22:33:01 -0700124
Marcus Hagerottb61e6542016-12-12 12:44:11 -0800125 @Override
126 protected ListenableFuture<List<ContactListFilter>> loadData() {
Marcus Hagerott75895e72016-12-12 17:21:57 -0800127 return Futures.transform(mAccountTypeManager.filterAccountsAsync(
Marcus Hagerottb61e6542016-12-12 12:44:11 -0800128 AccountTypeManager.writableFilter()),
Marcus Hagerott75895e72016-12-12 17:21:57 -0800129 new Function<List<AccountInfo>, List<ContactListFilter>>() {
Marcus Hagerottb61e6542016-12-12 12:44:11 -0800130 @Override
Marcus Hagerott75895e72016-12-12 17:21:57 -0800131 public List<ContactListFilter> apply(List<AccountInfo> input) {
Marcus Hagerottb61e6542016-12-12 12:44:11 -0800132 return getFiltersForAccounts(input);
133 }
134 }, ContactsExecutors.getDefaultThreadPoolExecutor());
135 }
136
Marcus Hagerott75895e72016-12-12 17:21:57 -0800137 private List<ContactListFilter> getFiltersForAccounts(List<AccountInfo> accounts) {
138 final ArrayList<ContactListFilter> accountFilters = new ArrayList<>();
139 AccountInfo.sortAccounts(getDefaultAccount(getContext()), accounts);
Marcus Hagerottb61e6542016-12-12 12:44:11 -0800140
Marcus Hagerott75895e72016-12-12 17:21:57 -0800141 for (AccountInfo accountInfo : accounts) {
142 final AccountType accountType = accountInfo.getType();
143 final AccountWithDataSet account = accountInfo.getAccount();
Marcus Hagerottb61e6542016-12-12 12:44:11 -0800144 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
Marcus Hagerott75895e72016-12-12 17:21:57 -0800162 return accountFilters;
Marcus Hagerottb61e6542016-12-12 12:44:11 -0800163 }
Wenyi Wangb3186762016-04-17 17:29:31 -0700164 }
Wenyi Wangaa0e6ff2016-07-06 17:22:42 -0700165
166 private static AccountWithDataSet getDefaultAccount(Context context) {
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700167 return new ContactsPreferences(context).getDefaultAccount();
Wenyi Wangaa0e6ff2016-07-06 17:22:42 -0700168 }
Wenyi Wang587b8f22016-08-17 17:40:45 -0700169
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 }
Wenyi Wang79675452016-08-17 10:43:28 -0700182
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.
Marcus Hagerott935b56a2016-09-07 11:59:35 -0700194 if (!isAllContactsFilter(filter) && filter.accountName != null
195 && filter.accountType != null) {
Wenyi Wang79675452016-08-17 10:43:28 -0700196 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);
Marcus Hagerott935b56a2016-09-07 11:59:35 -0700199 } else if (isDeviceContactsFilter(filter)) {
200 // It's OK to add this even though it's an implicit intent. If a different app
201 // receives the intent it should just ignore the flag.
Gary Mai363af602016-09-28 10:01:23 -0700202 intent.putExtra(ContactEditorActivity.EXTRA_SAVE_TO_DEVICE_FLAG, true);
Wenyi Wang79675452016-08-17 10:43:28 -0700203 }
204
205 try {
206 ImplicitIntentsUtil.startActivityInApp(context, intent);
207 } catch (ActivityNotFoundException ex) {
208 Toast.makeText(context, R.string.missing_app, Toast.LENGTH_SHORT).show();
209 }
210 }
211
212 public static boolean isAllContactsFilter(ContactListFilter filter) {
213 return filter != null && filter.isContactsFilterType();
214 }
215
216 public static boolean isDeviceContactsFilter(ContactListFilter filter) {
217 return filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS;
218 }
Wenyi Wangcfe94652016-10-03 21:05:04 -0700219
220 /**
221 * Returns action bar title for filter and returns default title "Contacts" if filter is empty.
222 */
223 public static String getActionBarTitleForFilter(Context context, ContactListFilter filter) {
224 if (filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS) {
225 return context.getString(R.string.account_phone);
Marcus Hagerottf7451132016-10-20 14:32:16 -0700226 } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT &&
227 !TextUtils.isEmpty(filter.accountName)) {
Wenyi Wangcfe94652016-10-03 21:05:04 -0700228 return getActionBarTitleForAccount(context, filter);
229 }
230 return context.getString(R.string.contactsList);
231 }
232
233 private static String getActionBarTitleForAccount(Context context, ContactListFilter filter) {
234 final AccountDisplayInfoFactory factory =
235 AccountDisplayInfoFactory.forAllAccounts(context);
236 final AccountDisplayInfo account = factory.getAccountDisplayInfoFor(filter);
237 if (account.hasGoogleAccountType()) {
238 return context.getString(R.string.title_from_google);
239 }
240 return account.withFormattedName(context, R.string.title_from_other_accounts)
241 .getNameLabel().toString();
242 }
Chiao Chengbeca8562012-11-28 18:06:44 -0800243}