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