• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package com.android.documentsui.services;
18 
19 import static com.android.documentsui.services.FileOperationService.OPERATION_COPY;
20 import static com.android.documentsui.services.FileOperationService.OPERATION_COMPRESS;
21 import static com.android.documentsui.services.FileOperationService.OPERATION_EXTRACT;
22 import static com.android.documentsui.services.FileOperationService.OPERATION_DELETE;
23 import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE;
24 import static com.android.documentsui.services.FileOperationService.OPERATION_UNKNOWN;
25 
26 import android.content.Context;
27 import android.net.Uri;
28 import android.os.Handler;
29 import android.os.Looper;
30 import android.os.Message;
31 import android.os.Messenger;
32 import android.os.Parcel;
33 import android.os.Parcelable;
34 import android.support.annotation.VisibleForTesting;
35 
36 import com.android.documentsui.base.DocumentStack;
37 import com.android.documentsui.base.Features;
38 import com.android.documentsui.clipping.UrisSupplier;
39 import com.android.documentsui.services.FileOperationService.OpType;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 
44 import javax.annotation.Nullable;
45 
46 /**
47  * FileOperation describes a file operation, such as move/copy/delete etc.
48  */
49 public abstract class FileOperation implements Parcelable {
50     private final @OpType int mOpType;
51 
52     private final UrisSupplier mSrcs;
53     private final List<Handler.Callback> mMessageListeners = new ArrayList<>();
54     private DocumentStack mDestination;
55     private Messenger mMessenger = new Messenger(
56             new Handler(Looper.getMainLooper(), this::onMessage));
57 
58     @VisibleForTesting
FileOperation(@pType int opType, UrisSupplier srcs, DocumentStack destination)59     FileOperation(@OpType int opType, UrisSupplier srcs, DocumentStack destination) {
60         assert(opType != OPERATION_UNKNOWN);
61         assert(srcs.getItemCount() > 0);
62 
63         mOpType = opType;
64         mSrcs = srcs;
65         mDestination = destination;
66     }
67 
68     @Override
describeContents()69     public int describeContents() {
70         return 0;
71     }
72 
getOpType()73     public @OpType int getOpType() {
74         return mOpType;
75     }
76 
getSrc()77     public UrisSupplier getSrc() {
78         return mSrcs;
79     }
80 
getDestination()81     public DocumentStack getDestination() {
82         return mDestination;
83     }
84 
getMessenger()85     public Messenger getMessenger() {
86         return mMessenger;
87     }
88 
setDestination(DocumentStack destination)89     public void setDestination(DocumentStack destination) {
90         mDestination = destination;
91     }
92 
dispose()93     public void dispose() {
94         mSrcs.dispose();
95     }
96 
createJob(Context service, Job.Listener listener, String id, Features features)97     abstract Job createJob(Context service, Job.Listener listener, String id, Features features);
98 
appendInfoTo(StringBuilder builder)99     private void appendInfoTo(StringBuilder builder) {
100         builder.append("opType=").append(mOpType);
101         builder.append(", srcs=").append(mSrcs.toString());
102         builder.append(", destination=").append(mDestination.toString());
103     }
104 
105     @Override
writeToParcel(Parcel out, int flag)106     public void writeToParcel(Parcel out, int flag) {
107         out.writeInt(mOpType);
108         out.writeParcelable(mSrcs, flag);
109         out.writeParcelable(mDestination, flag);
110         out.writeParcelable(mMessenger, flag);
111     }
112 
FileOperation(Parcel in)113     private FileOperation(Parcel in) {
114         mOpType = in.readInt();
115         mSrcs = in.readParcelable(FileOperation.class.getClassLoader());
116         mDestination = in.readParcelable(FileOperation.class.getClassLoader());
117         mMessenger = in.readParcelable(FileOperation.class.getClassLoader());
118     }
119 
120     public static class CopyOperation extends FileOperation {
CopyOperation(UrisSupplier srcs, DocumentStack destination)121         private CopyOperation(UrisSupplier srcs, DocumentStack destination) {
122             super(OPERATION_COPY, srcs, destination);
123         }
124 
125         @Override
toString()126         public String toString() {
127             StringBuilder builder = new StringBuilder();
128 
129             builder.append("CopyOperation{");
130             super.appendInfoTo(builder);
131             builder.append("}");
132 
133             return builder.toString();
134         }
135 
createJob(Context service, Job.Listener listener, String id, Features features)136         CopyJob createJob(Context service, Job.Listener listener, String id, Features features) {
137             return new CopyJob(
138                     service, listener, id, getDestination(), getSrc(), getMessenger(), features);
139         }
140 
CopyOperation(Parcel in)141         private CopyOperation(Parcel in) {
142             super(in);
143         }
144 
145         public static final Parcelable.Creator<CopyOperation> CREATOR =
146                 new Parcelable.Creator<CopyOperation>() {
147 
148                     @Override
149                     public CopyOperation createFromParcel(Parcel source) {
150                         return new CopyOperation(source);
151                     }
152 
153                     @Override
154                     public CopyOperation[] newArray(int size) {
155                         return new CopyOperation[size];
156                     }
157                 };
158     }
159 
160     public static class CompressOperation extends FileOperation {
CompressOperation(UrisSupplier srcs, DocumentStack destination)161         private CompressOperation(UrisSupplier srcs, DocumentStack destination) {
162             super(OPERATION_COMPRESS, srcs, destination);
163         }
164 
165         @Override
toString()166         public String toString() {
167             StringBuilder builder = new StringBuilder();
168 
169             builder.append("CompressOperation{");
170             super.appendInfoTo(builder);
171             builder.append("}");
172 
173             return builder.toString();
174         }
175 
createJob(Context service, Job.Listener listener, String id, Features features)176         CopyJob createJob(Context service, Job.Listener listener, String id, Features features) {
177             return new CompressJob(service, listener, id, getDestination(), getSrc(),
178                     getMessenger(), features);
179         }
180 
CompressOperation(Parcel in)181         private CompressOperation(Parcel in) {
182             super(in);
183         }
184 
185         public static final Parcelable.Creator<CompressOperation> CREATOR =
186                 new Parcelable.Creator<CompressOperation>() {
187 
188                     @Override
189                     public CompressOperation createFromParcel(Parcel source) {
190                         return new CompressOperation(source);
191                     }
192 
193                     @Override
194                     public CompressOperation[] newArray(int size) {
195                         return new CompressOperation[size];
196                     }
197                 };
198     }
199 
200     public static class ExtractOperation extends FileOperation {
ExtractOperation(UrisSupplier srcs, DocumentStack destination)201         private ExtractOperation(UrisSupplier srcs, DocumentStack destination) {
202             super(OPERATION_EXTRACT, srcs, destination);
203         }
204 
205         @Override
toString()206         public String toString() {
207             StringBuilder builder = new StringBuilder();
208 
209             builder.append("ExtractOperation{");
210             super.appendInfoTo(builder);
211             builder.append("}");
212 
213             return builder.toString();
214         }
215 
216         // TODO: Replace CopyJob with ExtractJob.
createJob(Context service, Job.Listener listener, String id, Features features)217         CopyJob createJob(Context service, Job.Listener listener, String id, Features features) {
218             return new CopyJob(
219                     service, listener, id, getDestination(), getSrc(), getMessenger(), features);
220         }
221 
ExtractOperation(Parcel in)222         private ExtractOperation(Parcel in) {
223             super(in);
224         }
225 
226         public static final Parcelable.Creator<ExtractOperation> CREATOR =
227                 new Parcelable.Creator<ExtractOperation>() {
228 
229                     @Override
230                     public ExtractOperation createFromParcel(Parcel source) {
231                         return new ExtractOperation(source);
232                     }
233 
234                     @Override
235                     public ExtractOperation[] newArray(int size) {
236                         return new ExtractOperation[size];
237                     }
238                 };
239     }
240 
241     public static class MoveDeleteOperation extends FileOperation {
242         private final @Nullable Uri mSrcParent;
243 
MoveDeleteOperation(@pType int opType, UrisSupplier srcs, DocumentStack destination, @Nullable Uri srcParent)244         private MoveDeleteOperation(@OpType int opType, UrisSupplier srcs,
245                 DocumentStack destination, @Nullable Uri srcParent) {
246             super(opType, srcs, destination);
247 
248             mSrcParent = srcParent;
249         }
250 
251         @Override
createJob(Context service, Job.Listener listener, String id, Features features)252         Job createJob(Context service, Job.Listener listener, String id, Features features) {
253             switch(getOpType()) {
254                 case OPERATION_MOVE:
255                     return new MoveJob(
256                             service, listener, id, getDestination(), getSrc(), mSrcParent,
257                             getMessenger(), features);
258                 case OPERATION_DELETE:
259                     return new DeleteJob(service, listener, id, getDestination(), getSrc(),
260                             mSrcParent, features);
261                 default:
262                     throw new UnsupportedOperationException("Unsupported op type: " + getOpType());
263             }
264         }
265 
266         @Override
toString()267         public String toString() {
268             StringBuilder builder = new StringBuilder();
269 
270             builder.append("MoveDeleteOperation{");
271             super.appendInfoTo(builder);
272             builder.append(", srcParent=").append(mSrcParent.toString());
273             builder.append("}");
274 
275             return builder.toString();
276         }
277 
278         @Override
writeToParcel(Parcel out, int flag)279         public void writeToParcel(Parcel out, int flag) {
280             super.writeToParcel(out, flag);
281             out.writeParcelable(mSrcParent, flag);
282         }
283 
MoveDeleteOperation(Parcel in)284         private MoveDeleteOperation(Parcel in) {
285             super(in);
286             mSrcParent = in.readParcelable(null);
287         }
288 
289         public static final Parcelable.Creator<MoveDeleteOperation> CREATOR =
290                 new Parcelable.Creator<MoveDeleteOperation>() {
291 
292 
293             @Override
294             public MoveDeleteOperation createFromParcel(Parcel source) {
295                 return new MoveDeleteOperation(source);
296             }
297 
298             @Override
299             public MoveDeleteOperation[] newArray(int size) {
300                 return new MoveDeleteOperation[size];
301             }
302         };
303     }
304 
305     public static class Builder {
306         private @OpType int mOpType;
307         private Uri mSrcParent;
308         private UrisSupplier mSrcs;
309         private DocumentStack mDestination;
310 
withOpType(@pType int opType)311         public Builder withOpType(@OpType int opType) {
312             mOpType = opType;
313             return this;
314         }
315 
withSrcParent(@ullable Uri srcParent)316         public Builder withSrcParent(@Nullable Uri srcParent) {
317             mSrcParent = srcParent;
318             return this;
319         }
320 
withSrcs(UrisSupplier srcs)321         public Builder withSrcs(UrisSupplier srcs) {
322             mSrcs = srcs;
323             return this;
324         }
325 
withDestination(DocumentStack destination)326         public Builder withDestination(DocumentStack destination) {
327             mDestination = destination;
328             return this;
329         }
330 
build()331         public FileOperation build() {
332             switch (mOpType) {
333                 case OPERATION_COPY:
334                     return new CopyOperation(mSrcs, mDestination);
335                 case OPERATION_COMPRESS:
336                     return new CompressOperation(mSrcs, mDestination);
337                 case OPERATION_EXTRACT:
338                     return new ExtractOperation(mSrcs, mDestination);
339                 case OPERATION_MOVE:
340                 case OPERATION_DELETE:
341                     return new MoveDeleteOperation(mOpType, mSrcs, mDestination, mSrcParent);
342                 default:
343                     throw new UnsupportedOperationException("Unsupported op type: " + mOpType);
344             }
345         }
346     }
347 
onMessage(Message message)348     boolean onMessage(Message message) {
349         for (Handler.Callback listener : mMessageListeners) {
350             if (listener.handleMessage(message)) {
351               return true;
352             }
353         }
354         return false;
355     }
356 
357     /**
358      * Registers a listener for messages from the service job.
359      *
360      * Callbacks must return true if the message is handled, and false if not.
361      * Once handled, consecutive callbacks will not be called.
362      */
addMessageListener(Handler.Callback handler)363     public void addMessageListener(Handler.Callback handler) {
364         mMessageListeners.add(handler);
365     }
366 
removeMessageListener(Handler.Callback handler)367     public void removeMessageListener(Handler.Callback handler) {
368         mMessageListeners.remove(handler);
369     }
370 }
371