blob: 8d8e2dbe13e94a6f3dd6f283303a53e8ee06a98b [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;
Marcus Hagerotte1275792016-11-17 10:41:12 -080026import android.support.annotation.NonNull;
Marcus Hagerott819214d2016-09-29 14:58:27 -070027import android.support.annotation.Nullable;
Marcus Hagerott2aa31982016-10-25 14:36:25 -070028import android.support.design.widget.Snackbar;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080029import android.support.v4.content.LocalBroadcastManager;
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -070030import android.support.v4.util.ArrayMap;
Marcus Hagerott7217e692016-11-10 10:18:28 -080031import android.support.v4.view.ViewCompat;
Marcus Hagerott819214d2016-09-29 14:58:27 -070032import android.support.v4.widget.ContentLoadingProgressBar;
33import android.support.v7.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;
50import com.android.contacts.model.account.AccountWithDataSet;
51import com.android.contacts.preference.ContactsPreferences;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080052import com.android.contacts.util.concurrent.ContactsExecutors;
53import com.android.contacts.util.concurrent.ListenableFutureLoader;
54import com.google.common.base.Function;
55import com.google.common.util.concurrent.Futures;
56import com.google.common.util.concurrent.ListenableFuture;
Marcus Hagerott819214d2016-09-29 14:58:27 -070057
58import java.util.ArrayList;
Marcus Hagerotte1275792016-11-17 10:41:12 -080059import java.util.Arrays;
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -070060import java.util.Collections;
Marcus Hagerott48a29362016-11-14 10:09:46 -080061import java.util.List;
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -070062import java.util.Map;
Marcus Hagerott819214d2016-09-29 14:58:27 -070063import java.util.Set;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080064import java.util.concurrent.Callable;
Marcus Hagerott819214d2016-09-29 14:58:27 -070065
66/**
67 * Dialog that presents a list of contacts from a SIM card that can be imported into a selected
68 * account
69 */
Marcus Hagerotta8b448a2016-11-18 12:51:39 -080070public class SimImportFragment extends Fragment
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -070071 implements LoaderManager.LoaderCallbacks<SimImportFragment.LoaderResult>,
Marcus Hagerotte1275792016-11-17 10:41:12 -080072 AdapterView.OnItemClickListener, AbsListView.OnScrollListener {
Marcus Hagerott819214d2016-09-29 14:58:27 -070073
Marcus Hagerott48a29362016-11-14 10:09:46 -080074 private static final String KEY_SUFFIX_SELECTED_IDS = "_selectedIds";
Marcus Hagerott819214d2016-09-29 14:58:27 -070075 private static final String ARG_SUBSCRIPTION_ID = "subscriptionId";
Marcus Hagerott66e8b222016-10-23 15:41:55 -070076
Marcus Hagerott819214d2016-09-29 14:58:27 -070077 private ContactsPreferences mPreferences;
78 private AccountTypeManager mAccountTypeManager;
79 private SimContactAdapter mAdapter;
Marcus Hagerott7217e692016-11-10 10:18:28 -080080 private View mAccountHeaderContainer;
Marcus Hagerott819214d2016-09-29 14:58:27 -070081 private AccountHeaderPresenter mAccountHeaderPresenter;
Marcus Hagerott7217e692016-11-10 10:18:28 -080082 private float mAccountScrolledElevationPixels;
Marcus Hagerott819214d2016-09-29 14:58:27 -070083 private ContentLoadingProgressBar mLoadingIndicator;
84 private Toolbar mToolbar;
85 private ListView mListView;
86 private View mImportButton;
87
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080088 private Bundle mSavedInstanceState;
89
Marcus Hagerotte1275792016-11-17 10:41:12 -080090 private final Map<AccountWithDataSet, long[]> mPerAccountCheckedIds = new ArrayMap<>();
91
Marcus Hagerott819214d2016-09-29 14:58:27 -070092 private int mSubscriptionId;
93
94 @Override
95 public void onCreate(final Bundle savedInstanceState) {
96 super.onCreate(savedInstanceState);
Marcus Hagerott73b283f2016-10-21 15:42:00 -070097
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -080098 mSavedInstanceState = savedInstanceState;
Marcus Hagerott819214d2016-09-29 14:58:27 -070099 mPreferences = new ContactsPreferences(getContext());
100 mAccountTypeManager = AccountTypeManager.getInstance(getActivity());
101 mAdapter = new SimContactAdapter(getActivity());
102
Marcus Hagerott819214d2016-09-29 14:58:27 -0700103 final Bundle args = getArguments();
Marcus Hagerott66e8b222016-10-23 15:41:55 -0700104 mSubscriptionId = args == null ? SimCard.NO_SUBSCRIPTION_ID :
105 args.getInt(ARG_SUBSCRIPTION_ID, SimCard.NO_SUBSCRIPTION_ID);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700106 }
107
108 @Override
109 public void onActivityCreated(Bundle savedInstanceState) {
110 super.onActivityCreated(savedInstanceState);
111 getLoaderManager().initLoader(0, null, this);
112 }
113
114 @Nullable
115 @Override
Marcus Hagerotta8b448a2016-11-18 12:51:39 -0800116 public View onCreateView(LayoutInflater inflater, ViewGroup container,
117 Bundle savedInstanceState) {
Marcus Hagerott819214d2016-09-29 14:58:27 -0700118 final View view = inflater.inflate(R.layout.fragment_sim_import, container, false);
119
Marcus Hagerott7217e692016-11-10 10:18:28 -0800120 mAccountHeaderContainer = view.findViewById(R.id.account_header_container);
121 mAccountScrolledElevationPixels = getResources()
122 .getDimension(R.dimen.contact_list_header_elevation);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700123 mAccountHeaderPresenter = new AccountHeaderPresenter(
Marcus Hagerott7217e692016-11-10 10:18:28 -0800124 mAccountHeaderContainer);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700125 if (savedInstanceState != null) {
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700126 mAccountHeaderPresenter.onRestoreInstanceState(savedInstanceState);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700127 } else {
128 final AccountWithDataSet currentDefaultAccount = AccountWithDataSet
129 .getDefaultOrBestFallback(mPreferences, mAccountTypeManager);
130 mAccountHeaderPresenter.setCurrentAccount(currentDefaultAccount);
131 }
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700132 mAccountHeaderPresenter.setObserver(new AccountHeaderPresenter.Observer() {
133 @Override
134 public void onChange(AccountHeaderPresenter sender) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800135 rememberSelectionsForCurrentAccount();
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700136 mAdapter.setAccount(sender.getCurrentAccount());
Marcus Hagerotte1275792016-11-17 10:41:12 -0800137 showSelectionsForCurrentAccount();
138 updateToolbarWithCurrentSelections();
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700139 }
140 });
141 mAdapter.setAccount(mAccountHeaderPresenter.getCurrentAccount());
Marcus Hagerott819214d2016-09-29 14:58:27 -0700142
143 mListView = (ListView) view.findViewById(R.id.list);
Marcus Hagerott7217e692016-11-10 10:18:28 -0800144 mListView.setOnScrollListener(this);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700145 mListView.setAdapter(mAdapter);
Marcus Hagerotte1275792016-11-17 10:41:12 -0800146 mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
147 mListView.setOnItemClickListener(this);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700148 mImportButton = view.findViewById(R.id.import_button);
149 mImportButton.setOnClickListener(new View.OnClickListener() {
150 @Override
151 public void onClick(View v) {
152 importCurrentSelections();
153 // Do we wait for import to finish?
Marcus Hagerotta8b448a2016-11-18 12:51:39 -0800154 getActivity().setResult(Activity.RESULT_OK);
155 getActivity().finish();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700156 }
157 });
Marcus Hagerott819214d2016-09-29 14:58:27 -0700158
159 mToolbar = (Toolbar) view.findViewById(R.id.toolbar);
160 mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
161 @Override
162 public void onClick(View v) {
Marcus Hagerotta8b448a2016-11-18 12:51:39 -0800163 getActivity().setResult(Activity.RESULT_CANCELED);
164 getActivity().finish();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700165 }
166 });
167
168 mLoadingIndicator = (ContentLoadingProgressBar) view.findViewById(R.id.loading_progress);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700169
170 return view;
171 }
172
Marcus Hagerotte1275792016-11-17 10:41:12 -0800173 private void rememberSelectionsForCurrentAccount() {
174 final AccountWithDataSet current = mAdapter.getAccount();
175 final long[] ids = mListView.getCheckedItemIds();
176 Arrays.sort(ids);
177 mPerAccountCheckedIds.put(current, ids);
178 }
179
180 private void showSelectionsForCurrentAccount() {
181 final long[] ids = mPerAccountCheckedIds.get(mAdapter.getAccount());
182 if (ids == null) {
183 selectAll();
184 return;
185 }
186 for (int i = 0, len = mListView.getCount(); i < len; i++) {
187 mListView.setItemChecked(i,
188 Arrays.binarySearch(ids, mListView.getItemIdAtPosition(i)) >= 0);
189 }
190 }
191
192 private void selectAll() {
193 for (int i = 0, len = mListView.getCount(); i < len; i++) {
194 mListView.setItemChecked(i, true);
195 }
196 }
197
198 private void updateToolbarWithCurrentSelections() {
199 // The ListView keeps checked state for items that are disabled but we only want to
200 // consider items that don't exist in the current account when updating the toolbar
201 int importableCount = 0;
202 final SparseBooleanArray checked = mListView.getCheckedItemPositions();
203 for (int i = 0; i < checked.size(); i++) {
Marcus Hagerottc8b04ba2016-11-23 18:49:57 -0800204 if (checked.valueAt(i) && !mAdapter.existsInCurrentAccount(checked.keyAt(i))) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800205 importableCount++;
206 }
207 }
208
209 if (importableCount == 0) {
210 mImportButton.setVisibility(View.GONE);
211 mToolbar.setTitle(R.string.sim_import_title_none_selected);
212 } else {
213 mToolbar.setTitle(String.valueOf(importableCount));
214 mImportButton.setVisibility(View.VISIBLE);
215 }
216 }
217
Marcus Hagerott819214d2016-09-29 14:58:27 -0700218 @Override
219 public void onStart() {
220 super.onStart();
221 if (mAdapter.isEmpty() && getLoaderManager().getLoader(0).isStarted()) {
222 mLoadingIndicator.show();
223 }
224 }
225
226 @Override
227 public void onSaveInstanceState(Bundle outState) {
Marcus Hagerottc8b04ba2016-11-23 18:49:57 -0800228 rememberSelectionsForCurrentAccount();
229 // We'll restore this manually so we don't need the list to preserve it's own state.
230 mListView.clearChoices();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700231 super.onSaveInstanceState(outState);
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700232 mAccountHeaderPresenter.onSaveInstanceState(outState);
Marcus Hagerott48a29362016-11-14 10:09:46 -0800233 saveAdapterSelectedStates(outState);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700234 }
235
236 @Override
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800237 public Loader<LoaderResult> onCreateLoader(int id, Bundle args) {
Marcus Hagerott819214d2016-09-29 14:58:27 -0700238 return new SimContactLoader(getContext(), mSubscriptionId);
239 }
240
241 @Override
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700242 public void onLoadFinished(Loader<LoaderResult> loader,
243 LoaderResult data) {
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700244 mLoadingIndicator.hide();
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700245 if (data == null) {
246 return;
247 }
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800248 mAccountHeaderPresenter.setAccounts(data.accounts);
249 restoreAdapterSelectedStates(data.accounts);
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700250 mAdapter.setData(data);
Marcus Hagerotte1275792016-11-17 10:41:12 -0800251 mListView.setEmptyView(getView().findViewById(R.id.empty_message));
252
253 showSelectionsForCurrentAccount();
254 updateToolbarWithCurrentSelections();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700255 }
256
257 @Override
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700258 public void onLoaderReset(Loader<LoaderResult> loader) {
Marcus Hagerott819214d2016-09-29 14:58:27 -0700259 }
260
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800261 private void restoreAdapterSelectedStates(List<AccountWithDataSet> accounts) {
262 if (mSavedInstanceState == null) {
Marcus Hagerott48a29362016-11-14 10:09:46 -0800263 return;
264 }
265
Marcus Hagerott48a29362016-11-14 10:09:46 -0800266 for (AccountWithDataSet account : accounts) {
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800267 final long[] selections = mSavedInstanceState.getLongArray(
Marcus Hagerott48a29362016-11-14 10:09:46 -0800268 account.stringify() + KEY_SUFFIX_SELECTED_IDS);
Marcus Hagerotte1275792016-11-17 10:41:12 -0800269 mPerAccountCheckedIds.put(account, selections);
Marcus Hagerott48a29362016-11-14 10:09:46 -0800270 }
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800271 mSavedInstanceState = null;
Marcus Hagerott48a29362016-11-14 10:09:46 -0800272 }
273
274 private void saveAdapterSelectedStates(Bundle outState) {
275 if (mAdapter == null) {
276 return;
277 }
278
279 // Make sure the selections are up-to-date
Marcus Hagerotte1275792016-11-17 10:41:12 -0800280 for (Map.Entry<AccountWithDataSet, long[]> entry : mPerAccountCheckedIds.entrySet()) {
281 outState.putLongArray(entry.getKey().stringify() + KEY_SUFFIX_SELECTED_IDS,
282 entry.getValue());
Marcus Hagerott48a29362016-11-14 10:09:46 -0800283 }
284 }
285
Marcus Hagerott819214d2016-09-29 14:58:27 -0700286 private void importCurrentSelections() {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800287 final SparseBooleanArray checked = mListView.getCheckedItemPositions();
288 final ArrayList<SimContact> importableContacts = new ArrayList<>(checked.size());
289 for (int i = 0; i < checked.size(); i++) {
290 // It's possible for existing contacts to be "checked" but we only want to import the
291 // ones that don't already exist
292 if (checked.valueAt(i) && !mAdapter.existsInCurrentAccount(i)) {
Marcus Hagerottc8b04ba2016-11-23 18:49:57 -0800293 importableContacts.add(mAdapter.getItem(checked.keyAt(i)));
Marcus Hagerotte1275792016-11-17 10:41:12 -0800294 }
295 }
Marcus Hagerott95246bb2016-11-11 10:56:09 -0800296 SimImportService.startImport(getContext(), mSubscriptionId, importableContacts,
297 mAccountHeaderPresenter.getCurrentAccount());
Marcus Hagerott819214d2016-09-29 14:58:27 -0700298 }
299
Marcus Hagerotte1275792016-11-17 10:41:12 -0800300 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
301 if (mAdapter.existsInCurrentAccount(position)) {
302 Snackbar.make(getView(), R.string.sim_import_contact_exists_toast,
303 Snackbar.LENGTH_LONG).show();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700304 } else {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800305 updateToolbarWithCurrentSelections();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700306 }
307 }
308
309 public Context getContext() {
310 if (CompatUtils.isMarshmallowCompatible()) {
311 return super.getContext();
312 }
313 return getActivity();
314 }
315
Marcus Hagerott7217e692016-11-10 10:18:28 -0800316 @Override
317 public void onScrollStateChanged(AbsListView view, int scrollState) { }
318
319 @Override
320 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
321 int totalItemCount) {
322 int firstCompletelyVisibleItem = firstVisibleItem;
323 if (view != null && view.getChildAt(0) != null && view.getChildAt(0).getTop() < 0) {
324 firstCompletelyVisibleItem++;
325 }
326
327 if (firstCompletelyVisibleItem == 0) {
328 ViewCompat.setElevation(mAccountHeaderContainer, 0);
329 } else {
330 ViewCompat.setElevation(mAccountHeaderContainer, mAccountScrolledElevationPixels);
331 }
332 }
333
Marcus Hagerott819214d2016-09-29 14:58:27 -0700334 /**
335 * Creates a fragment that will display contacts stored on the default SIM card
336 */
337 public static SimImportFragment newInstance() {
338 return new SimImportFragment();
339 }
340
341 /**
342 * Creates a fragment that will display the contacts stored on the SIM card that has the
343 * provided subscriptionId
344 */
345 public static SimImportFragment newInstance(int subscriptionId) {
346 final SimImportFragment fragment = new SimImportFragment();
347 final Bundle args = new Bundle();
348 args.putInt(ARG_SUBSCRIPTION_ID, subscriptionId);
349 fragment.setArguments(args);
350 return fragment;
351 }
352
Marcus Hagerotte1275792016-11-17 10:41:12 -0800353 private static class SimContactAdapter extends ArrayAdapter<SimContact> {
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700354 private Map<AccountWithDataSet, Set<SimContact>> mExistingMap;
Marcus Hagerotte1275792016-11-17 10:41:12 -0800355 private AccountWithDataSet mSelectedAccount;
356 private LayoutInflater mInflater;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700357
358 public SimContactAdapter(Context context) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800359 super(context, 0);
360 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700361 }
362
363 @Override
Marcus Hagerotte1275792016-11-17 10:41:12 -0800364 public long getItemId(int position) {
Marcus Hagerottc8b04ba2016-11-23 18:49:57 -0800365 // This can be called by the framework when the adapter hasn't been initialized for
366 // checking the checked state of items. See b/33108913
367 if (position < 0 || position >= getCount()) {
368 return View.NO_ID;
369 }
Marcus Hagerotte1275792016-11-17 10:41:12 -0800370 return getItem(position).getId();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700371 }
372
373 @Override
Marcus Hagerotte1275792016-11-17 10:41:12 -0800374 public boolean hasStableIds() {
375 return true;
376 }
Marcus Hagerottda071fb2016-10-13 10:29:15 -0700377
Marcus Hagerotte1275792016-11-17 10:41:12 -0800378 @Override
379 public int getViewTypeCount() {
380 return 2;
381 }
382
383 @Override
384 public int getItemViewType(int position) {
385 return !existsInCurrentAccount(position) ? 0 : 1;
386 }
387
388 @NonNull
389 @Override
390 public View getView(int position, View convertView, ViewGroup parent) {
391 TextView text = (TextView) convertView;
392 if (text == null) {
393 final int layoutRes = existsInCurrentAccount(position) ?
394 R.layout.sim_import_list_item_disabled :
395 R.layout.sim_import_list_item;
396 text = (TextView) mInflater.inflate(layoutRes, parent, false);
397 }
398 text.setText(getItemLabel(getItem(position)));
399
400 return text;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700401 }
402
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700403 public void setData(LoaderResult result) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800404 clear();
405 addAll(result.contacts);
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700406 mExistingMap = result.accountsMap;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700407 }
408
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700409 public void setAccount(AccountWithDataSet account) {
Marcus Hagerott48a29362016-11-14 10:09:46 -0800410 mSelectedAccount = account;
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700411 notifyDataSetChanged();
412 }
413
Marcus Hagerotte1275792016-11-17 10:41:12 -0800414 public AccountWithDataSet getAccount() {
415 return mSelectedAccount;
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700416 }
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700417
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700418 public boolean existsInCurrentAccount(int position) {
Marcus Hagerotte1275792016-11-17 10:41:12 -0800419 return existsInCurrentAccount(getItem(position));
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700420 }
421
422 public boolean existsInCurrentAccount(SimContact contact) {
423 if (mSelectedAccount == null || !mExistingMap.containsKey(mSelectedAccount)) {
424 return false;
425 }
426 return mExistingMap.get(mSelectedAccount).contains(contact);
427 }
428
Marcus Hagerotte1275792016-11-17 10:41:12 -0800429 private String getItemLabel(SimContact contact) {
430 if (contact.hasName()) {
431 return contact.getName();
432 } else if (contact.hasPhone()) {
433 return contact.getPhone();
434 } else if (contact.hasEmails()) {
435 return contact.getEmails()[0];
436 } else {
437 // This isn't really possible because we skip empty SIM contacts during loading
438 return "";
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700439 }
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700440 }
Marcus Hagerott819214d2016-09-29 14:58:27 -0700441 }
442
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700443
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800444 private static class SimContactLoader extends ListenableFutureLoader<LoaderResult> {
Marcus Hagerott819214d2016-09-29 14:58:27 -0700445 private SimContactDao mDao;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800446 private AccountTypeManager mAccountTypeManager;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700447 private final int mSubscriptionId;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800448
449 private BroadcastReceiver mReceiver;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700450
451 public SimContactLoader(Context context, int subscriptionId) {
452 super(context);
Marcus Hagerott66e8b222016-10-23 15:41:55 -0700453 mDao = SimContactDao.create(context);
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800454 mAccountTypeManager = AccountTypeManager.getInstance(getContext());
Marcus Hagerott819214d2016-09-29 14:58:27 -0700455 mSubscriptionId = subscriptionId;
456 }
457
458 @Override
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800459 protected ListenableFuture<LoaderResult> loadData() {
460 final ListenableFuture<List<Object>> future = Futures.<Object>allAsList(
461 mAccountTypeManager
462 .filterAccountsByTypeAsync(AccountTypeManager.writableFilter()),
463 ContactsExecutors.getSimReadExecutor().<Object>submit(
464 new Callable<Object>() {
465 @Override
466 public LoaderResult call() throws Exception {
467 return loadFromSim();
468 }
469 }));
470 return Futures.transform(future, new Function<List<Object>, LoaderResult>() {
471 @Override
472 public LoaderResult apply(List<Object> input) {
473 final List<AccountWithDataSet> accounts = (List<AccountWithDataSet>) input.get(0);
474 final LoaderResult simLoadResult = (LoaderResult) input.get(1);
475 simLoadResult.accounts = accounts;
476 return simLoadResult;
477 }
478 });
Marcus Hagerott819214d2016-09-29 14:58:27 -0700479 }
480
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800481 private LoaderResult loadFromSim() {
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700482 final SimCard sim = mDao.getSimBySubscriptionId(mSubscriptionId);
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700483 LoaderResult result = new LoaderResult();
Marcus Hagerott2aa31982016-10-25 14:36:25 -0700484 if (sim == null) {
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700485 result.contacts = new ArrayList<>();
486 result.accountsMap = Collections.emptyMap();
487 return result;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700488 }
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700489 result.contacts = mDao.loadContactsForSim(sim);
490 result.accountsMap = mDao.findAccountsOfExistingSimContacts(result.contacts);
491 return result;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700492 }
493
494 @Override
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800495 protected void onStartLoading() {
496 super.onStartLoading();
497 if (mReceiver == null) {
498 mReceiver = new ForceLoadReceiver();
499 LocalBroadcastManager.getInstance(getContext()).registerReceiver(mReceiver,
500 new IntentFilter(AccountTypeManager.BROADCAST_ACCOUNTS_CHANGED));
501 }
Marcus Hagerott819214d2016-09-29 14:58:27 -0700502 }
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700503
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800504 @Override
505 protected void onReset() {
506 super.onReset();
507 LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mReceiver);
508 }
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700509 }
510
511 public static class LoaderResult {
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800512 public List<AccountWithDataSet> accounts;
Marcus Hagerott6c42b4c2016-10-31 14:59:53 -0700513 public ArrayList<SimContact> contacts;
514 public Map<AccountWithDataSet, Set<SimContact>> accountsMap;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700515 }
516}