1c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay/*
2c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * Copyright (C) 2016 The Android Open Source Project
3c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay *
4c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * Licensed under the Apache License, Version 2.0 (the "License");
5c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * you may not use this file except in compliance with the License.
6c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * You may obtain a copy of the License at
7c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay *
8c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay *      http://www.apache.org/licenses/LICENSE-2.0
9c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay *
10c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * Unless required by applicable law or agreed to in writing, software
11c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * distributed under the License is distributed on an "AS IS" BASIS,
12c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * See the License for the specific language governing permissions and
14c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * limitations under the License.
15c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay */
16c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
17c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKaypackage com.android.documentsui.services;
18c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
19bbeba5265c5baa42c6db93fc8030c6055747da4dSteve McKayimport static android.os.SystemClock.elapsedRealtime;
20d9caa6ab53aa784acaf241c0ded3c4ae2d342bf8Steve McKayimport static com.android.documentsui.base.Shared.DEBUG;
21c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL;
22c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID;
234833477d7d42fa79ee42956bae4aebad77074e4bGarfield, Tanimport static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION;
24c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
254d00914fee8486df13b5cc4013a2cad093990267Garfield, Tanimport android.annotation.IntDef;
26c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport android.app.Activity;
27bbeba5265c5baa42c6db93fc8030c6055747da4dSteve McKayimport android.content.Context;
28c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport android.content.Intent;
29e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewskiimport android.support.annotation.VisibleForTesting;
30c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport android.util.Log;
31c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
32c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport com.android.documentsui.services.FileOperationService.OpType;
33c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
344d00914fee8486df13b5cc4013a2cad093990267Garfield, Tanimport java.lang.annotation.Retention;
354d00914fee8486df13b5cc4013a2cad093990267Garfield, Tanimport java.lang.annotation.RetentionPolicy;
36c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
370d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewskiimport javax.annotation.Nullable;
380d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski
39c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay/**
40c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay * Helper functions for starting various file operations.
41c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay */
42c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKaypublic final class FileOperations {
43c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
44c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    private static final String TAG = "FileOperations";
45c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
4697b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay    private static final IdBuilder idBuilder = new IdBuilder();
4797b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay
48c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    private FileOperations() {}
49c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
50bbeba5265c5baa42c6db93fc8030c6055747da4dSteve McKay    public static String createJobId() {
5197b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay        return idBuilder.getNext();
52bbeba5265c5baa42c6db93fc8030c6055747da4dSteve McKay    }
53bbeba5265c5baa42c6db93fc8030c6055747da4dSteve McKay
54c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    /**
55c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay     * Tries to start the activity. Returns the job id.
560d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski     * @param jobId Optional job id. If null, then it will be auto-generated.
57c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay     */
580d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski    public static String start(Context context, FileOperation operation, Callback callback,
590d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski            @Nullable String jobId) {
60c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
61c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay        if (DEBUG) Log.d(TAG, "Handling generic 'start' call.");
62c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
630d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski        String newJobId = jobId != null ? jobId : createJobId();
640d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski        Intent intent = createBaseIntent(context, newJobId, operation);
65c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
664833477d7d42fa79ee42956bae4aebad77074e4bGarfield, Tan        callback.onOperationResult(Callback.STATUS_ACCEPTED, operation.getOpType(),
674833477d7d42fa79ee42956bae4aebad77074e4bGarfield, Tan                operation.getSrc().getItemCount());
68e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski
69edce554c3eaff20a9bf349c1bc18c0b49e812c74Garfield, Tan        context.startService(intent);
70e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski
710d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski        return newJobId;
72e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski    }
73e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski
74e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski    @VisibleForTesting
75c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    public static void cancel(Activity activity, String jobId) {
76c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay        if (DEBUG) Log.d(TAG, "Attempting to canceling operation: " + jobId);
77c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
78c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay        Intent intent = new Intent(activity, FileOperationService.class);
79c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay        intent.putExtra(EXTRA_CANCEL, true);
80c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay        intent.putExtra(EXTRA_JOB_ID, jobId);
81c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
82c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay        activity.startService(intent);
83c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    }
84c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
85c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    /**
86e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski     * Starts the service for an operation.
87e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski     *
88e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski     * @param jobId A unique jobid for this job.
89e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski     *     Use {@link #createJobId} if you don't have one handy.
90e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski     * @return Id of the job.
91e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski     */
92e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski    public static Intent createBaseIntent(
934833477d7d42fa79ee42956bae4aebad77074e4bGarfield, Tan            Context context, String jobId, FileOperation operation) {
94e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski
95e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski        Intent intent = new Intent(context, FileOperationService.class);
96e0094419cd5f4e1cb55bcabc30c7e066d065827cTomasz Mikolajewski        intent.putExtra(EXTRA_JOB_ID, jobId);
974833477d7d42fa79ee42956bae4aebad77074e4bGarfield, Tan        intent.putExtra(EXTRA_OPERATION, operation);
98c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
99c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay        return intent;
100c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    }
101c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
10297b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay    private static final class IdBuilder {
10397b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay
10497b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay        // Remember last job time so we can guard against collisions.
10597b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay        private long mLastJobTime;
10697b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay
10797b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay        // If we detect a collision, use subId to make distinct.
10897b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay        private int mSubId;
10997b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay
11097b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay        public synchronized String getNext() {
11197b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay            long time = elapsedRealtime();
11297b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay            if (time == mLastJobTime) {
11397b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay                mSubId++;
11497b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay            } else {
11597b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay                mSubId = 0;
11697b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay            }
11797b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay            mLastJobTime = time;
11897b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay            return String.valueOf(mLastJobTime) + "-" + String.valueOf(mSubId);
11997b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay        }
12097b4be4638f1697895e5d1f3fdf8bf28a3bb9c13Steve McKay    }
1214d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan
1224d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan    /**
1234d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan     * A functional callback called when the file operation starts or fails to start.
1244d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan     */
1254d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan    @FunctionalInterface
1264d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan    public interface Callback {
1274d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan        @Retention(RetentionPolicy.SOURCE)
1284d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan        @IntDef({STATUS_ACCEPTED, STATUS_REJECTED})
1294d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan        @interface Status {}
1304d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan        static final int STATUS_ACCEPTED = 0;
1314d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan        static final int STATUS_REJECTED = 1;
132f657025bfc915df39e06e8e21067af2fd045759dJon Mann        static final int STATUS_FAILED = 2;
1334d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan
1344d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan        /**
1354d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan         * Performs operation when the file operation starts or fails to start.
1364d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan         *
1370d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski         * @param status {@link Status} of this operation.
1384d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan         * @param opType file operation type {@link OpType}.
1394d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan         * @param docCount number of documents operated.
1404d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan         */
1414d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan        void onOperationResult(@Status int status, @OpType int opType, int docCount);
1424d00914fee8486df13b5cc4013a2cad093990267Garfield, Tan    }
143c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay}
144