/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.services; import static android.os.SystemClock.elapsedRealtime; import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.Shared.EXTRA_STACK; import static com.android.documentsui.Shared.asArrayList; import static com.android.documentsui.Shared.getQuantityString; import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL; import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID; import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION; import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST; import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_PARENT; import static com.android.documentsui.services.FileOperationService.OPERATION_COPY; import static com.android.documentsui.services.FileOperationService.OPERATION_DELETE; import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.os.Parcelable; import android.support.annotation.VisibleForTesting; import android.support.design.widget.Snackbar; import android.util.Log; import com.android.documentsui.R; import com.android.documentsui.Snackbars; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.services.FileOperationService.OpType; import java.util.List; /** * Helper functions for starting various file operations. */ public final class FileOperations { private static final String TAG = "FileOperations"; private static final IdBuilder idBuilder = new IdBuilder(); private FileOperations() {} public static String createJobId() { return idBuilder.getNext(); } /** * Tries to start the activity. Returns the job id. */ public static String start( Activity activity, List srcDocs, DocumentStack stack, int operationType) { if (DEBUG) Log.d(TAG, "Handling generic 'start' call."); switch (operationType) { case OPERATION_COPY: return FileOperations.copy(activity, srcDocs, stack); case OPERATION_MOVE: throw new IllegalArgumentException("Moving requires providing the source parent."); case OPERATION_DELETE: throw new UnsupportedOperationException("Delete isn't currently supported."); default: throw new UnsupportedOperationException("Unknown operation: " + operationType); } } /** * Tries to start the activity. Returns the job id. */ public static String start( Activity activity, List srcDocs, DocumentInfo srcParent, DocumentStack stack, int operationType) { if (DEBUG) Log.d(TAG, "Handling generic 'start' call."); switch (operationType) { case OPERATION_COPY: return FileOperations.copy(activity, srcDocs, stack); case OPERATION_MOVE: return FileOperations.move(activity, srcDocs, srcParent, stack); case OPERATION_DELETE: throw new UnsupportedOperationException("Delete isn't currently supported."); default: throw new UnsupportedOperationException("Unknown operation: " + operationType); } } @VisibleForTesting public static void cancel(Activity activity, String jobId) { if (DEBUG) Log.d(TAG, "Attempting to canceling operation: " + jobId); Intent intent = new Intent(activity, FileOperationService.class); intent.putExtra(EXTRA_CANCEL, true); intent.putExtra(EXTRA_JOB_ID, jobId); activity.startService(intent); } @VisibleForTesting public static String copy( Activity activity, List srcDocs, DocumentStack destination) { String jobId = createJobId(); if (DEBUG) Log.d(TAG, "Initiating 'copy' operation id: " + jobId); Intent intent = createBaseIntent(OPERATION_COPY, activity, jobId, srcDocs, destination); createSharedSnackBar(activity, R.plurals.copy_begin, srcDocs.size()) .show(); activity.startService(intent); return jobId; } /** * Starts the service for a move operation. * * @param jobId A unique jobid for this job. * Use {@link #createJobId} if you don't have one handy. * @param srcDocs A list of src files to copy. * @param srcParent Parent of all the source documents. * @param destination The move destination stack. */ public static String move( Activity activity, List srcDocs, DocumentInfo srcParent, DocumentStack destination) { String jobId = createJobId(); if (DEBUG) Log.d(TAG, "Initiating 'move' operation id: " + jobId); Intent intent = createBaseIntent(OPERATION_MOVE, activity, jobId, srcDocs, srcParent, destination); createSharedSnackBar(activity, R.plurals.move_begin, srcDocs.size()) .show(); activity.startService(intent); return jobId; } /** * Starts the service for a delete operation. * * @param jobId A unique jobid for this job. * Use {@link #createJobId} if you don't have one handy. * @param srcDocs A list of src files to delete. * @param srcParent Parent of all the source documents. * @return Id of the job. */ public static String delete( Activity activity, List srcDocs, DocumentInfo srcParent, DocumentStack location) { String jobId = createJobId(); if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id " + jobId + "."); Intent intent = createBaseIntent(OPERATION_DELETE, activity, jobId, srcDocs, srcParent, location); activity.startService(intent); return jobId; } /** * Starts the service for an operation. * * @param jobId A unique jobid for this job. * Use {@link #createJobId} if you don't have one handy. * @param srcDocs A list of src files for an operation. * @return Id of the job. */ public static Intent createBaseIntent( @OpType int operationType, Context context, String jobId, List srcDocs, DocumentStack localeStack) { Intent intent = new Intent(context, FileOperationService.class); intent.putExtra(EXTRA_JOB_ID, jobId); intent.putParcelableArrayListExtra(EXTRA_SRC_LIST, asArrayList(srcDocs)); intent.putExtra(EXTRA_STACK, (Parcelable) localeStack); intent.putExtra(EXTRA_OPERATION, operationType); return intent; } /** * Starts the service for an operation. * * @param jobId A unique jobid for this job. * Use {@link #createJobId} if you don't have one handy. * @param srcDocs A list of src files to copy. * @param srcParent Parent of all the source documents. * @return Id of the job. */ public static Intent createBaseIntent( @OpType int operationType, Context context, String jobId, List srcDocs, DocumentInfo srcParent, DocumentStack localeStack) { Intent intent = new Intent(context, FileOperationService.class); intent.putExtra(EXTRA_JOB_ID, jobId); intent.putParcelableArrayListExtra(EXTRA_SRC_LIST, asArrayList(srcDocs)); intent.putExtra(EXTRA_SRC_PARENT, srcParent); intent.putExtra(EXTRA_STACK, (Parcelable) localeStack); intent.putExtra(EXTRA_OPERATION, operationType); return intent; } private static Snackbar createSharedSnackBar(Activity activity, int contentId, int fileCount) { Resources res = activity.getResources(); return Snackbars.makeSnackbar( activity, getQuantityString(activity, contentId, fileCount), Snackbar.LENGTH_SHORT); } private static final class IdBuilder { // Remember last job time so we can guard against collisions. private long mLastJobTime; // If we detect a collision, use subId to make distinct. private int mSubId; public synchronized String getNext() { long time = elapsedRealtime(); if (time == mLastJobTime) { mSubId++; } else { mSubId = 0; } mLastJobTime = time; return String.valueOf(mLastJobTime) + "-" + String.valueOf(mSubId); } } }