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