2 * Copyright (C) 2016 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.documentsui.services;
19 import static android.os.SystemClock.elapsedRealtime;
20 import static com.android.documentsui.Shared.DEBUG;
21 import static com.android.documentsui.Shared.EXTRA_STACK;
22 import static com.android.documentsui.Shared.asArrayList;
23 import static com.android.documentsui.Shared.getQuantityString;
24 import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL;
25 import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID;
26 import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION;
27 import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST;
28 import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_PARENT;
29 import static com.android.documentsui.services.FileOperationService.OPERATION_COPY;
30 import static com.android.documentsui.services.FileOperationService.OPERATION_DELETE;
31 import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE;
33 import android.app.Activity;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.res.Resources;
37 import android.os.Parcelable;
38 import android.support.annotation.VisibleForTesting;
39 import android.support.design.widget.Snackbar;
40 import android.util.Log;
42 import com.android.documentsui.R;
43 import com.android.documentsui.Snackbars;
44 import com.android.documentsui.model.DocumentInfo;
45 import com.android.documentsui.model.DocumentStack;
46 import com.android.documentsui.services.FileOperationService.OpType;
48 import java.util.List;
51 * Helper functions for starting various file operations.
53 public final class FileOperations {
55 private static final String TAG = "FileOperations";
57 private static final IdBuilder idBuilder = new IdBuilder();
59 private FileOperations() {}
61 public static String createJobId() {
62 return idBuilder.getNext();
66 * Tries to start the activity. Returns the job id.
68 public static String start(
69 Activity activity, List<DocumentInfo> srcDocs,
70 DocumentStack stack, int operationType) {
72 if (DEBUG) Log.d(TAG, "Handling generic 'start' call.");
74 switch (operationType) {
76 return FileOperations.copy(activity, srcDocs, stack);
78 throw new IllegalArgumentException("Moving requires providing the source parent.");
79 case OPERATION_DELETE:
80 throw new UnsupportedOperationException("Delete isn't currently supported.");
82 throw new UnsupportedOperationException("Unknown operation: " + operationType);
87 * Tries to start the activity. Returns the job id.
89 public static String start(
90 Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent,
91 DocumentStack stack, int operationType) {
93 if (DEBUG) Log.d(TAG, "Handling generic 'start' call.");
95 switch (operationType) {
97 return FileOperations.copy(activity, srcDocs, stack);
99 return FileOperations.move(activity, srcDocs, srcParent, stack);
100 case OPERATION_DELETE:
101 throw new UnsupportedOperationException("Delete isn't currently supported.");
103 throw new UnsupportedOperationException("Unknown operation: " + operationType);
108 public static void cancel(Activity activity, String jobId) {
109 if (DEBUG) Log.d(TAG, "Attempting to canceling operation: " + jobId);
111 Intent intent = new Intent(activity, FileOperationService.class);
112 intent.putExtra(EXTRA_CANCEL, true);
113 intent.putExtra(EXTRA_JOB_ID, jobId);
115 activity.startService(intent);
119 public static String copy(
120 Activity activity, List<DocumentInfo> srcDocs, DocumentStack destination) {
121 String jobId = createJobId();
122 if (DEBUG) Log.d(TAG, "Initiating 'copy' operation id: " + jobId);
124 Intent intent = createBaseIntent(OPERATION_COPY, activity, jobId, srcDocs, destination);
126 createSharedSnackBar(activity, R.plurals.copy_begin, srcDocs.size())
129 activity.startService(intent);
135 * Starts the service for a move operation.
137 * @param jobId A unique jobid for this job.
138 * Use {@link #createJobId} if you don't have one handy.
139 * @param srcDocs A list of src files to copy.
140 * @param srcParent Parent of all the source documents.
141 * @param destination The move destination stack.
143 public static String move(
144 Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent,
145 DocumentStack destination) {
146 String jobId = createJobId();
147 if (DEBUG) Log.d(TAG, "Initiating 'move' operation id: " + jobId);
149 Intent intent = createBaseIntent(OPERATION_MOVE, activity, jobId, srcDocs, srcParent,
152 createSharedSnackBar(activity, R.plurals.move_begin, srcDocs.size())
155 activity.startService(intent);
161 * Starts the service for a move operation.
163 * @param jobId A unique jobid for this job.
164 * Use {@link #createJobId} if you don't have one handy.
165 * @param srcDocs A list of src files to copy.
166 * @param srcParent Parent of all the source documents.
167 * @return Id of the job.
169 public static String delete(
170 Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent,
171 DocumentStack location) {
172 String jobId = createJobId();
173 if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id " + jobId + ".");
175 Intent intent = createBaseIntent(OPERATION_DELETE, activity, jobId, srcDocs, srcParent,
177 activity.startService(intent);
183 * Starts the service for an operation.
185 * @param jobId A unique jobid for this job.
186 * Use {@link #createJobId} if you don't have one handy.
187 * @param srcDocs A list of src files to copy.
188 * @return Id of the job.
190 public static Intent createBaseIntent(
191 @OpType int operationType, Context context, String jobId, List<DocumentInfo> srcDocs,
192 DocumentStack localeStack) {
194 Intent intent = new Intent(context, FileOperationService.class);
195 intent.putExtra(EXTRA_JOB_ID, jobId);
196 intent.putParcelableArrayListExtra(EXTRA_SRC_LIST, asArrayList(srcDocs));
197 intent.putExtra(EXTRA_STACK, (Parcelable) localeStack);
198 intent.putExtra(EXTRA_OPERATION, operationType);
204 * Starts the service for an operation.
206 * @param jobId A unique jobid for this job.
207 * Use {@link #createJobId} if you don't have one handy.
208 * @param srcDocs A list of src files to copy.
209 * @param srcParent Parent of all the source documents.
210 * @return Id of the job.
212 public static Intent createBaseIntent(
213 @OpType int operationType, Context context, String jobId,
214 List<DocumentInfo> srcDocs, DocumentInfo srcParent, DocumentStack localeStack) {
216 Intent intent = new Intent(context, FileOperationService.class);
217 intent.putExtra(EXTRA_JOB_ID, jobId);
218 intent.putParcelableArrayListExtra(EXTRA_SRC_LIST, asArrayList(srcDocs));
219 intent.putExtra(EXTRA_SRC_PARENT, srcParent);
220 intent.putExtra(EXTRA_STACK, (Parcelable) localeStack);
221 intent.putExtra(EXTRA_OPERATION, operationType);
226 private static Snackbar createSharedSnackBar(Activity activity, int contentId, int fileCount) {
227 Resources res = activity.getResources();
228 return Snackbars.makeSnackbar(
230 getQuantityString(activity, contentId, fileCount),
231 Snackbar.LENGTH_SHORT);
234 private static final class IdBuilder {
236 // Remember last job time so we can guard against collisions.
237 private long mLastJobTime;
239 // If we detect a collision, use subId to make distinct.
242 public synchronized String getNext() {
243 long time = elapsedRealtime();
244 if (time == mLastJobTime) {
250 return String.valueOf(mLastJobTime) + "-" + String.valueOf(mSubId);