Home | History | Annotate | Download | only in services
      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
     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
     69     public int describeContents() {
     70         return 0;
     71     }
     72 
     73     public @OpType int getOpType() {
     74         return mOpType;
     75     }
     76 
     77     public UrisSupplier getSrc() {
     78         return mSrcs;
     79     }
     80 
     81     public DocumentStack getDestination() {
     82         return mDestination;
     83     }
     84 
     85     public Messenger getMessenger() {
     86         return mMessenger;
     87     }
     88 
     89     public void setDestination(DocumentStack destination) {
     90         mDestination = destination;
     91     }
     92 
     93     public void dispose() {
     94         mSrcs.dispose();
     95     }
     96 
     97     abstract Job createJob(Context service, Job.Listener listener, String id, Features features);
     98 
     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
    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 
    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 {
    121         private CopyOperation(UrisSupplier srcs, DocumentStack destination) {
    122             super(OPERATION_COPY, srcs, destination);
    123         }
    124 
    125         @Override
    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 
    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 
    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 {
    161         private CompressOperation(UrisSupplier srcs, DocumentStack destination) {
    162             super(OPERATION_COMPRESS, srcs, destination);
    163         }
    164 
    165         @Override
    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 
    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 
    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 {
    201         private ExtractOperation(UrisSupplier srcs, DocumentStack destination) {
    202             super(OPERATION_EXTRACT, srcs, destination);
    203         }
    204 
    205         @Override
    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.
    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 
    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 
    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
    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
    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
    279         public void writeToParcel(Parcel out, int flag) {
    280             super.writeToParcel(out, flag);
    281             out.writeParcelable(mSrcParent, flag);
    282         }
    283 
    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 
    311         public Builder withOpType(@OpType int opType) {
    312             mOpType = opType;
    313             return this;
    314         }
    315 
    316         public Builder withSrcParent(@Nullable Uri srcParent) {
    317             mSrcParent = srcParent;
    318             return this;
    319         }
    320 
    321         public Builder withSrcs(UrisSupplier srcs) {
    322             mSrcs = srcs;
    323             return this;
    324         }
    325 
    326         public Builder withDestination(DocumentStack destination) {
    327             mDestination = destination;
    328             return this;
    329         }
    330 
    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 
    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      */
    363     public void addMessageListener(Handler.Callback handler) {
    364         mMessageListeners.add(handler);
    365     }
    366 
    367     public void removeMessageListener(Handler.Callback handler) {
    368         mMessageListeners.remove(handler);
    369     }
    370 }
    371