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 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;
     32 
     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;
     41 
     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;
     47 
     48 import java.util.List;
     49 
     50 /**
     51  * Helper functions for starting various file operations.
     52  */
     53 public final class FileOperations {
     54 
     55     private static final String TAG = "FileOperations";
     56 
     57     private static final IdBuilder idBuilder = new IdBuilder();
     58 
     59     private FileOperations() {}
     60 
     61     public static String createJobId() {
     62         return idBuilder.getNext();
     63     }
     64 
     65     /**
     66      * Tries to start the activity. Returns the job id.
     67      */
     68     public static String start(
     69             Activity activity, List<DocumentInfo> srcDocs,
     70             DocumentStack stack, int operationType) {
     71 
     72         if (DEBUG) Log.d(TAG, "Handling generic 'start' call.");
     73 
     74         switch (operationType) {
     75             case OPERATION_COPY:
     76                 return FileOperations.copy(activity, srcDocs, stack);
     77             case OPERATION_MOVE:
     78                 throw new IllegalArgumentException("Moving requires providing the source parent.");
     79             case OPERATION_DELETE:
     80                 throw new UnsupportedOperationException("Delete isn't currently supported.");
     81             default:
     82                 throw new UnsupportedOperationException("Unknown operation: " + operationType);
     83         }
     84     }
     85 
     86     /**
     87      * Tries to start the activity. Returns the job id.
     88      */
     89     public static String start(
     90             Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent,
     91             DocumentStack stack, int operationType) {
     92 
     93         if (DEBUG) Log.d(TAG, "Handling generic 'start' call.");
     94 
     95         switch (operationType) {
     96             case OPERATION_COPY:
     97                 return FileOperations.copy(activity, srcDocs, stack);
     98             case OPERATION_MOVE:
     99                 return FileOperations.move(activity, srcDocs, srcParent, stack);
    100             case OPERATION_DELETE:
    101                 throw new UnsupportedOperationException("Delete isn't currently supported.");
    102             default:
    103                 throw new UnsupportedOperationException("Unknown operation: " + operationType);
    104         }
    105     }
    106 
    107     @VisibleForTesting
    108     public static void cancel(Activity activity, String jobId) {
    109         if (DEBUG) Log.d(TAG, "Attempting to canceling operation: " + jobId);
    110 
    111         Intent intent = new Intent(activity, FileOperationService.class);
    112         intent.putExtra(EXTRA_CANCEL, true);
    113         intent.putExtra(EXTRA_JOB_ID, jobId);
    114 
    115         activity.startService(intent);
    116     }
    117 
    118     @VisibleForTesting
    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);
    123 
    124         Intent intent = createBaseIntent(OPERATION_COPY, activity, jobId, srcDocs, destination);
    125 
    126         createSharedSnackBar(activity, R.plurals.copy_begin, srcDocs.size())
    127                 .show();
    128 
    129         activity.startService(intent);
    130 
    131         return jobId;
    132     }
    133 
    134     /**
    135      * Starts the service for a move operation.
    136      *
    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.
    142      */
    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);
    148 
    149         Intent intent = createBaseIntent(OPERATION_MOVE, activity, jobId, srcDocs, srcParent,
    150                 destination);
    151 
    152         createSharedSnackBar(activity, R.plurals.move_begin, srcDocs.size())
    153                 .show();
    154 
    155         activity.startService(intent);
    156 
    157         return jobId;
    158     }
    159 
    160     /**
    161      * Starts the service for a delete operation.
    162      *
    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 delete.
    166      * @param srcParent Parent of all the source documents.
    167      * @return Id of the job.
    168      */
    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 + ".");
    174 
    175         Intent intent = createBaseIntent(OPERATION_DELETE, activity, jobId, srcDocs, srcParent,
    176                 location);
    177         activity.startService(intent);
    178 
    179         return jobId;
    180     }
    181 
    182     /**
    183      * Starts the service for an operation.
    184      *
    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 for an operation.
    188      * @return Id of the job.
    189      */
    190     public static Intent createBaseIntent(
    191             @OpType int operationType, Context context, String jobId, List<DocumentInfo> srcDocs,
    192             DocumentStack localeStack) {
    193 
    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);
    199 
    200         return intent;
    201     }
    202 
    203     /**
    204      * Starts the service for an operation.
    205      *
    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.
    211      */
    212     public static Intent createBaseIntent(
    213             @OpType int operationType, Context context, String jobId,
    214             List<DocumentInfo> srcDocs, DocumentInfo srcParent, DocumentStack localeStack) {
    215 
    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);
    222 
    223         return intent;
    224     }
    225 
    226     private static Snackbar createSharedSnackBar(Activity activity, int contentId, int fileCount) {
    227         Resources res = activity.getResources();
    228         return Snackbars.makeSnackbar(
    229                 activity,
    230                 getQuantityString(activity, contentId, fileCount),
    231                 Snackbar.LENGTH_SHORT);
    232     }
    233 
    234     private static final class IdBuilder {
    235 
    236         // Remember last job time so we can guard against collisions.
    237         private long mLastJobTime;
    238 
    239         // If we detect a collision, use subId to make distinct.
    240         private int mSubId;
    241 
    242         public synchronized String getNext() {
    243             long time = elapsedRealtime();
    244             if (time == mLastJobTime) {
    245                 mSubId++;
    246             } else {
    247                 mSubId = 0;
    248             }
    249             mLastJobTime = time;
    250             return String.valueOf(mLastJobTime) + "-" + String.valueOf(mSubId);
    251         }
    252     }
    253 }
    254