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