195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott/* 295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * Copyright (C) 2016 The Android Open Source Project 395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * 495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * Licensed under the Apache License, Version 2.0 (the "License"); 595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * you may not use this file except in compliance with the License. 695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * You may obtain a copy of the License at 795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * 895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * http://www.apache.org/licenses/LICENSE-2.0 995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * 1095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * Unless required by applicable law or agreed to in writing, software 1195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * distributed under the License is distributed on an "AS IS" BASIS, 1295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * See the License for the specific language governing permissions and 1495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * limitations under the License. 1595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott */ 1695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottpackage com.android.contacts; 1795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 1895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.app.Notification; 1995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.app.NotificationManager; 2095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.app.PendingIntent; 2195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.app.Service; 2295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.content.Context; 2395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.content.Intent; 2495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.content.OperationApplicationException; 2595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.os.AsyncTask; 2695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.os.IBinder; 2795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.os.RemoteException; 2895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.support.annotation.Nullable; 2995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.support.v4.app.NotificationCompat; 3095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.support.v4.content.LocalBroadcastManager; 3195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport android.util.TimingLogger; 3295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 3395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport com.android.contacts.activities.PeopleActivity; 3469c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.database.SimContactDao; 3569c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.model.SimCard; 3669c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.model.SimContact; 3769c182afb0e6d82a341a28b4317aa703af768906Gary Maiimport com.android.contacts.model.account.AccountWithDataSet; 387ae91369a3fae4da5e0be3bb683b8ca3713cc1ebWenyi Wangimport com.android.contacts.util.ContactsNotificationChannelsUtil; 3995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport com.android.contactsbind.FeedbackHelper; 4095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 4195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport java.util.ArrayList; 4295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport java.util.List; 4395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport java.util.concurrent.ExecutorService; 4495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottimport java.util.concurrent.Executors; 4595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 4695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott/** 4795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * Imports {@link SimContact}s from a background thread 4895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott */ 4995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerottpublic class SimImportService extends Service { 5095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 5195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private static final String TAG = "SimImportService"; 5295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 532bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott /** 542bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott * Wrapper around the service state for testability 552bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott */ 562bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott public interface StatusProvider { 572bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott 582bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott /** 592bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott * Returns whether there is any imports still pending 602bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott * 612bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott * <p>This should be called from the UI thread</p> 622bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott */ 632bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott boolean isRunning(); 642bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott 652bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott /** 662bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott * Returns whether an import for sim has been requested 672bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott * 682bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott * <p>This should be called from the UI thread</p> 692bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott */ 702bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott boolean isImporting(SimCard sim); 712bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott } 722bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott 7395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final String EXTRA_ACCOUNT = "account"; 7495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final String EXTRA_SIM_CONTACTS = "simContacts"; 7595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final String EXTRA_SIM_SUBSCRIPTION_ID = "simSubscriptionId"; 7695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final String EXTRA_RESULT_CODE = "resultCode"; 7795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final String EXTRA_RESULT_COUNT = "count"; 7895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final String EXTRA_OPERATION_REQUESTED_AT_TIME = "requestedTime"; 7995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 8095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final String BROADCAST_SERVICE_STATE_CHANGED = 8195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott SimImportService.class.getName() + "#serviceStateChanged"; 8295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final String BROADCAST_SIM_IMPORT_COMPLETE = 8395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott SimImportService.class.getName() + "#simImportComplete"; 8495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 8595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final int RESULT_UNKNOWN = 0; 8695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final int RESULT_SUCCESS = 1; 8795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static final int RESULT_FAILURE = 2; 8895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 8995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // VCardService uses jobIds for it's notifications which count up from 0 so we just use a 9095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // bigger number to prevent overlap. 9195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private static final int NOTIFICATION_ID = 100; 9295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 9395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); 9495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 9595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // Keeps track of current tasks. This is only modified from the UI thread. 9695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private static List<ImportTask> sPending = new ArrayList<>(); 9795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 982bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott private static StatusProvider sStatusProvider = new StatusProvider() { 992bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott @Override 1002bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott public boolean isRunning() { 1012bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott return !sPending.isEmpty(); 1022bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott } 1032bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott 1042bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott @Override 1052bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott public boolean isImporting(SimCard sim) { 1062bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott return SimImportService.isImporting(sim); 1072bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott } 1082bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott }; 1092bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott 11095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott /** 11195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * Returns whether an import for sim has been requested 11295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * 11395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * <p>This should be called from the UI thread</p> 11495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott */ 1152bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott private static boolean isImporting(SimCard sim) { 11695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott for (ImportTask task : sPending) { 11795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott if (task.getSim().equals(sim)) { 11895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return true; 11995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 12095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 12195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return false; 12295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 12395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 1242bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott public static StatusProvider getStatusProvider() { 1252bb4984ee2f2e345e9073b9cc40af2afc35b9809Marcus Hagerott return sStatusProvider; 12695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 12795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 12895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott /** 12995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * Starts an import of the contacts from the sim into the target account 13095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * 13195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * @param context context to use for starting the service 13295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * @param subscriptionId the subscriptionId of the SIM card that is being imported. See 13395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * {@link android.telephony.SubscriptionInfo#getSubscriptionId()}. 13495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * Upon completion the SIM for that subscription ID will be marked as 13595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * imported 13695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * @param contacts the contacts to import 13795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott * @param targetAccount the account import the contacts into 13895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott */ 13995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public static void startImport(Context context, int subscriptionId, 14095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott ArrayList<SimContact> contacts, AccountWithDataSet targetAccount) { 14195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott context.startService(new Intent(context, SimImportService.class) 14295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_SIM_CONTACTS, contacts) 14395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_SIM_SUBSCRIPTION_ID, subscriptionId) 14495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_ACCOUNT, targetAccount)); 14595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 14695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 14795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 14895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott @Nullable 14995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott @Override 15095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public IBinder onBind(Intent intent) { 15195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return null; 15295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 15395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 15495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott @Override 15595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public int onStartCommand(Intent intent, int flags, final int startId) { 156156fc9f68761dd3eeb6fb3d154fc35a201991ebbWenyi Wang ContactsNotificationChannelsUtil.createDefaultChannel(this); 15795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final ImportTask task = createTaskForIntent(intent, startId); 15895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott if (task == null) { 15995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott new StopTask(this, startId).executeOnExecutor(mExecutor); 16095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return START_NOT_STICKY; 16195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 16295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott sPending.add(task); 16395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott task.executeOnExecutor(mExecutor); 16495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott notifyStateChanged(); 16595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return START_REDELIVER_INTENT; 16695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 16795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 16895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott @Override 16995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public void onDestroy() { 17095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott super.onDestroy(); 17195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mExecutor.shutdown(); 17295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 17395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 17495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private ImportTask createTaskForIntent(Intent intent, int startId) { 17595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final AccountWithDataSet targetAccount = intent.getParcelableExtra(EXTRA_ACCOUNT); 17695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final ArrayList<SimContact> contacts = 17795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott intent.getParcelableArrayListExtra(EXTRA_SIM_CONTACTS); 17895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 17995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final int subscriptionId = intent.getIntExtra(EXTRA_SIM_SUBSCRIPTION_ID, 18095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott SimCard.NO_SUBSCRIPTION_ID); 18195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final SimContactDao dao = SimContactDao.create(this); 18295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final SimCard sim = dao.getSimBySubscriptionId(subscriptionId); 18395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott if (sim != null) { 18495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return new ImportTask(sim, contacts, targetAccount, dao, startId); 18595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } else { 18695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return null; 18795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 18895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 18995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 19095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private Notification getCompletedNotification() { 19195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final Intent intent = new Intent(this, PeopleActivity.class); 192580d1b50021e74f783adeaed660ad7b7d2e2a0edJulia Reynolds final NotificationCompat.Builder builder = new NotificationCompat.Builder( 193580d1b50021e74f783adeaed660ad7b7d2e2a0edJulia Reynolds this, ContactsNotificationChannelsUtil.DEFAULT_CHANNEL); 19495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott builder.setOngoing(false) 19595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setAutoCancel(true) 19695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setContentTitle(this.getString(R.string.importing_sim_finished_title)) 19795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setColor(this.getResources().getColor(R.color.dialtacts_theme_color)) 198bd9ef3c60669d71543b3506d4959c1fe4be409dcJohn Shao .setSmallIcon(R.drawable.quantum_ic_done_vd_theme_24) 19995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0)); 20095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return builder.build(); 20195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 20295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 20395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private Notification getFailedNotification() { 20495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final Intent intent = new Intent(this, PeopleActivity.class); 205580d1b50021e74f783adeaed660ad7b7d2e2a0edJulia Reynolds final NotificationCompat.Builder builder = new NotificationCompat.Builder( 206580d1b50021e74f783adeaed660ad7b7d2e2a0edJulia Reynolds this, ContactsNotificationChannelsUtil.DEFAULT_CHANNEL); 20795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott builder.setOngoing(false) 20895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setAutoCancel(true) 20995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setContentTitle(this.getString(R.string.importing_sim_failed_title)) 2104dc1224d470a11aa47de43654fd53292df5833d6Marcus Hagerott .setContentText(this.getString(R.string.importing_sim_failed_message)) 21195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setColor(this.getResources().getColor(R.color.dialtacts_theme_color)) 212bd9ef3c60669d71543b3506d4959c1fe4be409dcJohn Shao .setSmallIcon(R.drawable.quantum_ic_error_vd_theme_24) 21395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0)); 21495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return builder.build(); 21595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 21695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 21795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private Notification getImportingNotification() { 218580d1b50021e74f783adeaed660ad7b7d2e2a0edJulia Reynolds final NotificationCompat.Builder builder = new NotificationCompat.Builder( 219580d1b50021e74f783adeaed660ad7b7d2e2a0edJulia Reynolds this, ContactsNotificationChannelsUtil.DEFAULT_CHANNEL); 22095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final String description = getString(R.string.importing_sim_in_progress_title); 22195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott builder.setOngoing(true) 22295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setProgress(/* current */ 0, /* max */ 100, /* indeterminate */ true) 22395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setContentTitle(description) 22495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setColor(this.getResources().getColor(R.color.dialtacts_theme_color)) 22595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .setSmallIcon(android.R.drawable.stat_sys_download); 22695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return builder.build(); 22795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 22895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 22995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private void notifyStateChanged() { 23095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott LocalBroadcastManager.getInstance(this).sendBroadcast( 23195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott new Intent(BROADCAST_SERVICE_STATE_CHANGED)); 23295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 23395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 23495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // Schedule a task that calls stopSelf when it completes. This is used to ensure that the 23595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // calls to stopSelf occur in the correct order (because this service uses a single thread 23695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // executor this won't run until all work that was requested before it has finished) 23795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private static class StopTask extends AsyncTask<Void, Void, Void> { 23895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private Service mHost; 23995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private final int mStartId; 24095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 24195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private StopTask(Service host, int startId) { 24295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mHost = host; 24395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mStartId = startId; 24495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 24595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 24695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott @Override 24795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott protected Void doInBackground(Void... params) { 24895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return null; 24995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 25095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 25195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott @Override 25295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott protected void onPostExecute(Void aVoid) { 25395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott super.onPostExecute(aVoid); 25495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mHost.stopSelf(mStartId); 25595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 25695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 25795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 25895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private class ImportTask extends AsyncTask<Void, Void, Boolean> { 25995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private final SimCard mSim; 26095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private final List<SimContact> mContacts; 26195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private final AccountWithDataSet mTargetAccount; 26295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private final SimContactDao mDao; 26395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private final NotificationManager mNotificationManager; 26495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private final int mStartId; 26595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott private final long mStartTime; 26695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 26795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public ImportTask(SimCard sim, List<SimContact> contacts, AccountWithDataSet targetAccount, 26895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott SimContactDao dao, int startId) { 26995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mSim = sim; 27095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mContacts = contacts; 27195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mTargetAccount = targetAccount; 27295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mDao = dao; 27395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 27495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mStartId = startId; 27595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mStartTime = System.currentTimeMillis(); 27695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 27795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 27895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott @Override 27995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott protected void onPreExecute() { 28095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott super.onPreExecute(); 28195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott startForeground(NOTIFICATION_ID, getImportingNotification()); 28295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 28395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 28495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott @Override 28595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott protected Boolean doInBackground(Void... params) { 28695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final TimingLogger timer = new TimingLogger(TAG, "import"); 28795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott try { 28895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // Just import them all at once. 28995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // Experimented with using smaller batches (e.g. 25 and 50) so that percentage 29095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // progress could be displayed however this slowed down the import by over a factor 29195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // of 2. If the batch size is over a 100 then most cases will only require a single 29295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // batch so we don't even worry about displaying accurate progress 29395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mDao.importContacts(mContacts, mTargetAccount); 29495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mDao.persistSimState(mSim.withImportedState(true)); 29595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott timer.addSplit("done"); 29695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott timer.dumpToLog(); 29795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } catch (RemoteException|OperationApplicationException e) { 29895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott FeedbackHelper.sendFeedback(SimImportService.this, TAG, 29995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott "Failed to import contacts from SIM card", e); 30095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return false; 30195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 30295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return true; 30395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 30495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 30595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott public SimCard getSim() { 30695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott return mSim; 30795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 30895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 30995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott @Override 31095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott protected void onPostExecute(Boolean success) { 31195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott super.onPostExecute(success); 31295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott stopSelf(mStartId); 31395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 31495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott Intent result; 31595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott final Notification notification; 31695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott if (success) { 31795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott result = new Intent(BROADCAST_SIM_IMPORT_COMPLETE) 31895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_RESULT_CODE, RESULT_SUCCESS) 31995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_RESULT_COUNT, mContacts.size()) 32095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_OPERATION_REQUESTED_AT_TIME, mStartTime) 32195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_SIM_SUBSCRIPTION_ID, mSim.getSubscriptionId()); 32295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 32395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott notification = getCompletedNotification(); 32495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } else { 32595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott result = new Intent(BROADCAST_SIM_IMPORT_COMPLETE) 32695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_RESULT_CODE, RESULT_FAILURE) 32795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_OPERATION_REQUESTED_AT_TIME, mStartTime) 32895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott .putExtra(EXTRA_SIM_SUBSCRIPTION_ID, mSim.getSubscriptionId()); 32995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 33095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott notification = getFailedNotification(); 33195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 33295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott LocalBroadcastManager.getInstance(SimImportService.this).sendBroadcast(result); 33395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 33495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott sPending.remove(this); 33595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott 33695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // Only notify of completion if all the import requests have finished. We're using 33795246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // the same notification for imports so in the rare case that a user has started 33895246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott // multiple imports the notification won't go away until all of them complete. 33995246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott if (sPending.isEmpty()) { 34095246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott stopForeground(false); 34195246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott mNotificationManager.notify(NOTIFICATION_ID, notification); 34295246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 34395246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott notifyStateChanged(); 34495246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 34595246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott } 34695246bb89a1c45459fe15eefdc6d924963247944Marcus Hagerott} 347