blob: cee1b1a9a201633ffa2c6f8a44b81425f0434508 [file] [log] [blame]
Marcus Hagerott819214d2016-09-29 14:58:27 -07001/*
2 * Copyright (C) 2016 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 */
16package com.android.contacts;
17
Marcus Hagerotta8b448a2016-11-18 12:51:39 -080018import android.app.Activity;
19import android.app.Fragment;
Marcus Hagerott819214d2016-09-29 14:58:27 -070020import android.app.LoaderManager;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080021import android.content.BroadcastReceiver;
Marcus Hagerott819214d2016-09-29 14:58:27 -070022import android.content.Context;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080023import android.content.IntentFilter;
Marcus Hagerott819214d2016-09-29 14:58:27 -070024import android.content.Loader;
Marcus Hagerott819214d2016-09-29 14:58:27 -070025import android.os.Bundle;
Aravind Sreekumar71212852018-04-06 15:47:45 -070026import androidx.annotation.NonNull;
27import androidx.annotation.Nullable;
Aurimas Liutikas1f8811c2019-01-30 21:39:48 -080028import com.google.android.material.snackbar.Snackbar;
Aravind Sreekumar71212852018-04-06 15:47:45 -070029import androidx.localbroadcastmanager.content.LocalBroadcastManager;
30import androidx.collection.ArrayMap;
31import androidx.core.view.ViewCompat;
32import androidx.core.widget.ContentLoadingProgressBar;
33import androidx.appcompat.widget.Toolbar;
Marcus Hagerotte1275792016-11-17 10:41:12 -080034import android.util.SparseBooleanArray;
Marcus Hagerott819214d2016-09-29 14:58:27 -070035import android.view.LayoutInflater;
36import android.view.View;
37import android.view.ViewGroup;
Marcus Hagerott7217e692016-11-10 10:18:28 -080038import android.widget.AbsListView;
Marcus Hagerott819214d2016-09-29 14:58:27 -070039import android.widget.AdapterView;
Marcus Hagerotte1275792016-11-17 10:41:12 -080040import android.widget.ArrayAdapter;
Marcus Hagerott819214d2016-09-29 14:58:27 -070041import android.widget.ListView;
Marcus Hagerotte1275792016-11-17 10:41:12 -080042import android.widget.TextView;
Marcus Hagerott819214d2016-09-29 14:58:27 -070043
Gary Mai69c182a2016-12-05 13:07:03 -080044import com.android.contacts.compat.CompatUtils;
45import com.android.contacts.database.SimContactDao;
Gary Mai0a49afa2016-12-05 15:53:58 -080046import com.android.contacts.editor.AccountHeaderPresenter;
Gary Mai69c182a2016-12-05 13:07:03 -080047import com.android.contacts.model.AccountTypeManager;
48import com.android.contacts.model.SimCard;
49import com.android.contacts.model.SimContact;
Marcus Hagerott75895e72016-12-12 17:21:57 -080050import com.android.contacts.model.account.AccountInfo;
Gary Mai69c182a2016-12-05 13:07:03 -080051import com.android.contacts.model.account.AccountWithDataSet;
52import com.android.contacts.preference.ContactsPreferences;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080053import com.android.contacts.util.concurrent.ContactsExecutors;
54import com.android.contacts.util.concurrent.ListenableFutureLoader;
55import com.google.common.base.Function;
56import com.google.common.util.concurrent.Futures;
57import com.google.common.util.concurrent.ListenableFuture;
Colin Crossa7987012019-04-11 14:32:53 -070058import com.google.common.util.concurrent.MoreExecutors;
Marcus Hagerott819214d2016-09-29 14:58:27 -070059
60import java.util.ArrayList;
Marcus Hagerotte1275792016-11-17 10:41:12 -080061import java.util.Arrays;
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -070062import java.util.Collections;
Marcus Hagerott48a29362016-11-14 10:09:46 -080063import java.util.List;
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -070064import java.util.Map;
Marcus Hagerott819214d2016-09-29 14:58:27 -070065import java.util.Set;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080066import java.util.concurrent.Callable;
Marcus Hagerott819214d2016-09-29 14:58:27 -070067
68/**
69 * Dialog that presents a list of contacts from a SIM card that can be imported into a selected
70 * account
71 */
Marcus Hagerotta8b448a2016-11-18 12:51:39 -080072public class SimImportFragment extends Fragment
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -070073 implements LoaderManager.LoaderCallbacks<SimImportFragment.LoaderResult>,
Marcus Hagerotte1275792016-11-17 10:41:12 -080074 AdapterView.OnItemClickListener, AbsListView.OnScrollListener {
Marcus Hagerott819214d2016-09-29 14:58:27 -070075
Marcus Hagerott48a29362016-11-14 10:09:46 -080076 private static final String KEY_SUFFIX_SELECTED_IDS = "_selectedIds";
Marcus Hagerott819214d2016-09-29 14:58:27 -070077 private static final String ARG_SUBSCRIPTION_ID = "subscriptionId";
Marcus Hagerott66e8b222016-10-23 15:41:55 -070078
Marcus Hagerott819214d2016-09-29 14:58:27 -070079 private ContactsPreferences mPreferences;
80 private AccountTypeManager mAccountTypeManager;
81 private SimContactAdapter mAdapter;
Marcus Hagerott7217e692016-11-10 10:18:28 -080082 private View mAccountHeaderContainer;
Marcus Hagerott819214d2016-09-29 14:58:27 -070083 private AccountHeaderPresenter mAccountHeaderPresenter;
Marcus Hagerott7217e692016-11-10 10:18:28 -080084 private float mAccountScrolledElevationPixels;
Marcus Hagerott819214d2016-09-29 14:58:27 -070085 private ContentLoadingProgressBar mLoadingIndicator;
86 private Toolbar mToolbar;
87 private ListView mListView;
88 private View mImportButton;
89
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080090 private Bundle mSavedInstanceState;
91
Marcus Hagerotte1275792016-11-17 10:41:12 -080092 private final Map<AccountWithDataSet, long[]> mPerAccountCheckedIds = new ArrayMap<>();
93
Marcus Hagerott819214d2016-09-29 14:58:27 -070094 private int mSubscriptionId;
95
96 @Override
97 public void onCreate(final Bundle savedInstanceState) {
98 super.onCreate(savedInstanceState);
Marcus Hagerott73b283f2016-10-21 15:42:00 -070099
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800100 mSavedInstanceState = savedInstanceState;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700101 mPreferences = new ContactsPreferences(getContext());
102 mAccountTypeManager = AccountTypeManager.getInstance(getActivity());
103 mAdapter = new SimContactAdapter(getActivity());
104
Marcus Hagerott819214d2016-09-29 14:58:27 -0700105 final Bundle args = getArguments();
Marcus Hagerott66e8b222016-10-23 15:41:55 -0700106 mSubscriptionId = args == null ? SimCard.NO_SUBSCRIPTION_ID :
107 args.getInt(ARG_SUBSCRIPTION_ID, SimCard.NO_SUBSCRIPTION_ID);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700108 }
109
110 @Override
111 public void onActivityCreated(Bundle savedInstanceState) {
112 super.onActivityCreated(savedInstanceState);
113 getLoaderManager().initLoader(0, null, this);
114 }
115
116 @Nullable
117 @Override
Marcus Hagerotta8b448a2016-11-18 12:51:39 -0800118 public View onCreateView(LayoutInflater inflater, ViewGroup container,
119 Bundle savedInstanceState) {
Marcus Hagerott819214d2016-09-29 14:58:27 -0700120 final View view = inflater.inflate(R.layout.fragment_sim_import, container, false);
121
Marcus Hagerott7217e692016-11-10 10:18:28 -0800122 mAccountHeaderContainer = view.findViewById(R.id.account_header_container);
123 mAccountScrolledElevationPixels = getResources()
124 .getDimension(R.dimen.contact_list_header_elevation);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700125 mAccountHeaderPresenter = new AccountHeaderPresenter(
Marcus Hagerott7217e692016-11-10 10:18:28 -0800126 mAccountHeaderContainer);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700127 if (savedInstanceState != null) {
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700128 mAccountHeaderPresenter.onRestoreInstanceState(savedInstanceState);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700129 } else {
Marcus Hagerotta181ca62016-12-21 14:42:13 -0800130 // Default may be null in which case the first account in the list will be selected
131 // after they are loaded.
132 mAccountHeaderPresenter.setCurrentAccount(mPreferences.getDefaultAccount());
Marcus Hagerott819214d2016-09-29 14:58:27 -0700133 }
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700134 mAccountHeaderPresenter.setObserver(new AccountHeaderPresenter.Observer() {
135 @Override
136 public void onChange(AccountHeaderPresenter sender) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800137 rememberSelectionsForCurrentAccount();
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700138 mAdapter.setAccount(sender.getCurrentAccount());
Marcus Hagerotte1275792016-11-17 10:41:12 -0800139 showSelectionsForCurrentAccount();
140 updateToolbarWithCurrentSelections();
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700141 }
142 });
143 mAdapter.setAccount(mAccountHeaderPresenter.getCurrentAccount());
Marcus Hagerott819214d2016-09-29 14:58:27 -0700144
145 mListView = (ListView) view.findViewById(R.id.list);
Marcus Hagerott7217e692016-11-10 10:18:28 -0800146 mListView.setOnScrollListener(this);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700147 mListView.setAdapter(mAdapter);
Marcus Hagerotte1275792016-11-17 10:41:12 -0800148 mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
149 mListView.setOnItemClickListener(this);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700150 mImportButton = view.findViewById(R.id.import_button);
151 mImportButton.setOnClickListener(new View.OnClickListener() {
152 @Override
153 public void onClick(View v) {
154 importCurrentSelections();
155 // Do we wait for import to finish?
Marcus Hagerotta8b448a2016-11-18 12:51:39 -0800156 getActivity().setResult(Activity.RESULT_OK);
157 getActivity().finish();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700158 }
159 });
Marcus Hagerott819214d2016-09-29 14:58:27 -0700160
161 mToolbar = (Toolbar) view.findViewById(R.id.toolbar);
162 mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
163 @Override
164 public void onClick(View v) {
Marcus Hagerotta8b448a2016-11-18 12:51:39 -0800165 getActivity().setResult(Activity.RESULT_CANCELED);
166 getActivity().finish();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700167 }
168 });
169
170 mLoadingIndicator = (ContentLoadingProgressBar) view.findViewById(R.id.loading_progress);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700171
172 return view;
173 }
174
Marcus Hagerotte1275792016-11-17 10:41:12 -0800175 private void rememberSelectionsForCurrentAccount() {
176 final AccountWithDataSet current = mAdapter.getAccount();
Marcus Hagerotta181ca62016-12-21 14:42:13 -0800177 if (current == null) {
178 return;
179 }
Marcus Hagerotte1275792016-11-17 10:41:12 -0800180 final long[] ids = mListView.getCheckedItemIds();
181 Arrays.sort(ids);
182 mPerAccountCheckedIds.put(current, ids);
183 }
184
185 private void showSelectionsForCurrentAccount() {
186 final long[] ids = mPerAccountCheckedIds.get(mAdapter.getAccount());
187 if (ids == null) {
188 selectAll();
189 return;
190 }
191 for (int i = 0, len = mListView.getCount(); i < len; i++) {
192 mListView.setItemChecked(i,
193 Arrays.binarySearch(ids, mListView.getItemIdAtPosition(i)) >= 0);
194 }
195 }
196
197 private void selectAll() {
198 for (int i = 0, len = mListView.getCount(); i < len; i++) {
199 mListView.setItemChecked(i, true);
200 }
201 }
202
203 private void updateToolbarWithCurrentSelections() {
204 // The ListView keeps checked state for items that are disabled but we only want to
205 // consider items that don't exist in the current account when updating the toolbar
206 int importableCount = 0;
207 final SparseBooleanArray checked = mListView.getCheckedItemPositions();
208 for (int i = 0; i < checked.size(); i++) {
Marcus Hagerottc8b04ba2016-11-23 18:49:57 -0800209 if (checked.valueAt(i) && !mAdapter.existsInCurrentAccount(checked.keyAt(i))) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800210 importableCount++;
211 }
212 }
213
214 if (importableCount == 0) {
215 mImportButton.setVisibility(View.GONE);
216 mToolbar.setTitle(R.string.sim_import_title_none_selected);
217 } else {
218 mToolbar.setTitle(String.valueOf(importableCount));
219 mImportButton.setVisibility(View.VISIBLE);
220 }
221 }
222
Marcus Hagerott819214d2016-09-29 14:58:27 -0700223 @Override
224 public void onStart() {
225 super.onStart();
226 if (mAdapter.isEmpty() && getLoaderManager().getLoader(0).isStarted()) {
227 mLoadingIndicator.show();
228 }
229 }
230
231 @Override
232 public void onSaveInstanceState(Bundle outState) {
Marcus Hagerottc8b04ba2016-11-23 18:49:57 -0800233 rememberSelectionsForCurrentAccount();
234 // We'll restore this manually so we don't need the list to preserve it's own state.
235 mListView.clearChoices();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700236 super.onSaveInstanceState(outState);
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700237 mAccountHeaderPresenter.onSaveInstanceState(outState);
Marcus Hagerott48a29362016-11-14 10:09:46 -0800238 saveAdapterSelectedStates(outState);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700239 }
240
241 @Override
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800242 public Loader<LoaderResult> onCreateLoader(int id, Bundle args) {
Marcus Hagerott819214d2016-09-29 14:58:27 -0700243 return new SimContactLoader(getContext(), mSubscriptionId);
244 }
245
246 @Override
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700247 public void onLoadFinished(Loader<LoaderResult> loader,
248 LoaderResult data) {
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700249 mLoadingIndicator.hide();
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700250 if (data == null) {
251 return;
252 }
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800253 mAccountHeaderPresenter.setAccounts(data.accounts);
254 restoreAdapterSelectedStates(data.accounts);
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700255 mAdapter.setData(data);
Marcus Hagerotte1275792016-11-17 10:41:12 -0800256 mListView.setEmptyView(getView().findViewById(R.id.empty_message));
257
258 showSelectionsForCurrentAccount();
259 updateToolbarWithCurrentSelections();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700260 }
261
262 @Override
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700263 public void onLoaderReset(Loader<LoaderResult> loader) {
Marcus Hagerott819214d2016-09-29 14:58:27 -0700264 }
265
Marcus Hagerott75895e72016-12-12 17:21:57 -0800266 private void restoreAdapterSelectedStates(List<AccountInfo> accounts) {
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800267 if (mSavedInstanceState == null) {
Marcus Hagerott48a29362016-11-14 10:09:46 -0800268 return;
269 }
270
Marcus Hagerott75895e72016-12-12 17:21:57 -0800271 for (AccountInfo account : accounts) {
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800272 final long[] selections = mSavedInstanceState.getLongArray(
Marcus Hagerott75895e72016-12-12 17:21:57 -0800273 account.getAccount().stringify() + KEY_SUFFIX_SELECTED_IDS);
274 mPerAccountCheckedIds.put(account.getAccount(), selections);
Marcus Hagerott48a29362016-11-14 10:09:46 -0800275 }
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800276 mSavedInstanceState = null;
Marcus Hagerott48a29362016-11-14 10:09:46 -0800277 }
278
279 private void saveAdapterSelectedStates(Bundle outState) {
280 if (mAdapter == null) {
281 return;
282 }
283
284 // Make sure the selections are up-to-date
Marcus Hagerotte1275792016-11-17 10:41:12 -0800285 for (Map.Entry<AccountWithDataSet, long[]> entry : mPerAccountCheckedIds.entrySet()) {
286 outState.putLongArray(entry.getKey().stringify() + KEY_SUFFIX_SELECTED_IDS,
287 entry.getValue());
Marcus Hagerott48a29362016-11-14 10:09:46 -0800288 }
289 }
290
Marcus Hagerott819214d2016-09-29 14:58:27 -0700291 private void importCurrentSelections() {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800292 final SparseBooleanArray checked = mListView.getCheckedItemPositions();
293 final ArrayList<SimContact> importableContacts = new ArrayList<>(checked.size());
294 for (int i = 0; i < checked.size(); i++) {
295 // It's possible for existing contacts to be "checked" but we only want to import the
296 // ones that don't already exist
297 if (checked.valueAt(i) && !mAdapter.existsInCurrentAccount(i)) {
Marcus Hagerottc8b04ba2016-11-23 18:49:57 -0800298 importableContacts.add(mAdapter.getItem(checked.keyAt(i)));
Marcus Hagerotte1275792016-11-17 10:41:12 -0800299 }
300 }
Marcus Hagerott95246bb2016-11-11 10:56:09 -0800301 SimImportService.startImport(getContext(), mSubscriptionId, importableContacts,
302 mAccountHeaderPresenter.getCurrentAccount());
Marcus Hagerott819214d2016-09-29 14:58:27 -0700303 }
304
Marcus Hagerotte1275792016-11-17 10:41:12 -0800305 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
306 if (mAdapter.existsInCurrentAccount(position)) {
307 Snackbar.make(getView(), R.string.sim_import_contact_exists_toast,
308 Snackbar.LENGTH_LONG).show();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700309 } else {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800310 updateToolbarWithCurrentSelections();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700311 }
312 }
313
314 public Context getContext() {
315 if (CompatUtils.isMarshmallowCompatible()) {
316 return super.getContext();
317 }
318 return getActivity();
319 }
320
Marcus Hagerott7217e692016-11-10 10:18:28 -0800321 @Override
322 public void onScrollStateChanged(AbsListView view, int scrollState) { }
323
324 @Override
325 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
326 int totalItemCount) {
327 int firstCompletelyVisibleItem = firstVisibleItem;
328 if (view != null && view.getChildAt(0) != null && view.getChildAt(0).getTop() < 0) {
329 firstCompletelyVisibleItem++;
330 }
331
332 if (firstCompletelyVisibleItem == 0) {
333 ViewCompat.setElevation(mAccountHeaderContainer, 0);
334 } else {
335 ViewCompat.setElevation(mAccountHeaderContainer, mAccountScrolledElevationPixels);
336 }
337 }
338
Marcus Hagerott819214d2016-09-29 14:58:27 -0700339 /**
340 * Creates a fragment that will display contacts stored on the default SIM card
341 */
342 public static SimImportFragment newInstance() {
343 return new SimImportFragment();
344 }
345
346 /**
347 * Creates a fragment that will display the contacts stored on the SIM card that has the
348 * provided subscriptionId
349 */
350 public static SimImportFragment newInstance(int subscriptionId) {
351 final SimImportFragment fragment = new SimImportFragment();
352 final Bundle args = new Bundle();
353 args.putInt(ARG_SUBSCRIPTION_ID, subscriptionId);
354 fragment.setArguments(args);
355 return fragment;
356 }
357
Marcus Hagerotte1275792016-11-17 10:41:12 -0800358 private static class SimContactAdapter extends ArrayAdapter<SimContact> {
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700359 private Map<AccountWithDataSet, Set<SimContact>> mExistingMap;
Marcus Hagerotte1275792016-11-17 10:41:12 -0800360 private AccountWithDataSet mSelectedAccount;
361 private LayoutInflater mInflater;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700362
363 public SimContactAdapter(Context context) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800364 super(context, 0);
365 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700366 }
367
368 @Override
Marcus Hagerotte1275792016-11-17 10:41:12 -0800369 public long getItemId(int position) {
Marcus Hagerottc8b04ba2016-11-23 18:49:57 -0800370 // This can be called by the framework when the adapter hasn't been initialized for
371 // checking the checked state of items. See b/33108913
372 if (position < 0 || position >= getCount()) {
373 return View.NO_ID;
374 }
Marcus Hagerotte1275792016-11-17 10:41:12 -0800375 return getItem(position).getId();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700376 }
377
378 @Override
Marcus Hagerotte1275792016-11-17 10:41:12 -0800379 public boolean hasStableIds() {
380 return true;
381 }
Marcus Hagerottda071fb2016-10-13 10:29:15 -0700382
Marcus Hagerotte1275792016-11-17 10:41:12 -0800383 @Override
384 public int getViewTypeCount() {
385 return 2;
386 }
387
388 @Override
389 public int getItemViewType(int position) {
390 return !existsInCurrentAccount(position) ? 0 : 1;
391 }
392
393 @NonNull
394 @Override
395 public View getView(int position, View convertView, ViewGroup parent) {
396 TextView text = (TextView) convertView;
397 if (text == null) {
398 final int layoutRes = existsInCurrentAccount(position) ?
399 R.layout.sim_import_list_item_disabled :
400 R.layout.sim_import_list_item;
401 text = (TextView) mInflater.inflate(layoutRes, parent, false);
402 }
403 text.setText(getItemLabel(getItem(position)));
404
405 return text;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700406 }
407
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700408 public void setData(LoaderResult result) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800409 clear();
410 addAll(result.contacts);
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700411 mExistingMap = result.accountsMap;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700412 }
413
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700414 public void setAccount(AccountWithDataSet account) {
Marcus Hagerott48a29362016-11-14 10:09:46 -0800415 mSelectedAccount = account;
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700416 notifyDataSetChanged();
417 }
418
Marcus Hagerotte1275792016-11-17 10:41:12 -0800419 public AccountWithDataSet getAccount() {
420 return mSelectedAccount;
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700421 }
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700422
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700423 public boolean existsInCurrentAccount(int position) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800424 return existsInCurrentAccount(getItem(position));
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700425 }
426
427 public boolean existsInCurrentAccount(SimContact contact) {
428 if (mSelectedAccount == null || !mExistingMap.containsKey(mSelectedAccount)) {
429 return false;
430 }
431 return mExistingMap.get(mSelectedAccount).contains(contact);
432 }
433
Marcus Hagerotte1275792016-11-17 10:41:12 -0800434 private String getItemLabel(SimContact contact) {
435 if (contact.hasName()) {
436 return contact.getName();
437 } else if (contact.hasPhone()) {
438 return contact.getPhone();
439 } else if (contact.hasEmails()) {
440 return contact.getEmails()[0];
441 } else {
442 // This isn't really possible because we skip empty SIM contacts during loading
443 return "";
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700444 }
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700445 }
Marcus Hagerott819214d2016-09-29 14:58:27 -0700446 }
447
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700448
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800449 private static class SimContactLoader extends ListenableFutureLoader<LoaderResult> {
Marcus Hagerott819214d2016-09-29 14:58:27 -0700450 private SimContactDao mDao;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800451 private AccountTypeManager mAccountTypeManager;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700452 private final int mSubscriptionId;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800453
Marcus Hagerott819214d2016-09-29 14:58:27 -0700454 public SimContactLoader(Context context, int subscriptionId) {
Marcus Hagerott75895e72016-12-12 17:21:57 -0800455 super(context, new IntentFilter(AccountTypeManager.BROADCAST_ACCOUNTS_CHANGED));
Marcus Hagerott66e8b222016-10-23 15:41:55 -0700456 mDao = SimContactDao.create(context);
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800457 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
Marcus Hagerott819214d2016-09-29 14:58:27 -0700458 mSubscriptionId = subscriptionId;
459 }
460
461 @Override
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800462 protected ListenableFuture<LoaderResult> loadData() {
463 final ListenableFuture<List<Object>> future = Futures.<Object>allAsList(
464 mAccountTypeManager
Marcus Hagerott75895e72016-12-12 17:21:57 -0800465 .filterAccountsAsync(AccountTypeManager.writableFilter()),
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800466 ContactsExecutors.getSimReadExecutor().<Object>submit(
467 new Callable<Object>() {
468 @Override
469 public LoaderResult call() throws Exception {
470 return loadFromSim();
471 }
472 }));
473 return Futures.transform(future, new Function<List<Object>, LoaderResult>() {
474 @Override
475 public LoaderResult apply(List<Object> input) {
Marcus Hagerott75895e72016-12-12 17:21:57 -0800476 final List<AccountInfo> accounts = (List<AccountInfo>) input.get(0);
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800477 final LoaderResult simLoadResult = (LoaderResult) input.get(1);
478 simLoadResult.accounts = accounts;
479 return simLoadResult;
480 }
Colin Crossa7987012019-04-11 14:32:53 -0700481 }, MoreExecutors.directExecutor());
Marcus Hagerott819214d2016-09-29 14:58:27 -0700482 }
483
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800484 private LoaderResult loadFromSim() {
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700485 final SimCard sim = mDao.getSimBySubscriptionId(mSubscriptionId);
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700486 LoaderResult result = new LoaderResult();
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700487 if (sim == null) {
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700488 result.contacts = new ArrayList<>();
489 result.accountsMap = Collections.emptyMap();
490 return result;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700491 }
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700492 result.contacts = mDao.loadContactsForSim(sim);
493 result.accountsMap = mDao.findAccountsOfExistingSimContacts(result.contacts);
494 return result;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700495 }
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700496 }
497
498 public static class LoaderResult {
Marcus Hagerott75895e72016-12-12 17:21:57 -0800499 public List<AccountInfo> accounts;
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700500 public ArrayList<SimContact> contacts;
501 public Map<AccountWithDataSet, Set<SimContact>> accountsMap;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700502 }
503}