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