OSDN Git Service

Implement ability to cancel file copy.
authorMartin Brabham <optedoblivion@cyngn.com>
Fri, 3 Apr 2015 03:33:41 +0000 (20:33 -0700)
committerMatt Garnes <matt@cyngn.com>
Mon, 13 Apr 2015 17:20:34 +0000 (17:20 +0000)
- Short circuit Java CopyCommand for move/copy when the user cancels
  the dialog.
- Bubble up CancelledOperationException when the Cancel/Move operation
  is cancelled. Handle cancellation differently than success.

Change-Id: I3e4426aaccf42e12bf299041d489e72b3b76a626
(cherry picked from commit 7e13ec2fa4fc052c2a880a8dba8ed871b3bc10ca)

src/com/cyanogenmod/filemanager/commands/java/CopyCommand.java
src/com/cyanogenmod/filemanager/commands/java/MoveCommand.java
src/com/cyanogenmod/filemanager/commands/java/Program.java
src/com/cyanogenmod/filemanager/console/java/JavaConsole.java
src/com/cyanogenmod/filemanager/console/shell/ShellConsole.java
src/com/cyanogenmod/filemanager/ui/policy/ActionsPolicy.java
src/com/cyanogenmod/filemanager/ui/policy/CompressActionPolicy.java
src/com/cyanogenmod/filemanager/ui/policy/CopyMoveActionPolicy.java
src/com/cyanogenmod/filemanager/ui/policy/DeleteActionPolicy.java
src/com/cyanogenmod/filemanager/util/CommandHelper.java
src/com/cyanogenmod/filemanager/util/FileHelper.java

index 0c7832c..5d8073f 100644 (file)
@@ -19,6 +19,7 @@ package com.cyanogenmod.filemanager.commands.java;
 import android.util.Log;
 
 import com.cyanogenmod.filemanager.commands.CopyExecutable;
+import com.cyanogenmod.filemanager.console.CancelledOperationException;
 import com.cyanogenmod.filemanager.console.ExecutionException;
 import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
 import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
@@ -64,7 +65,8 @@ public class CopyCommand extends Program implements CopyExecutable {
      */
     @Override
     public void execute()
-            throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+            throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException,
+                   CancelledOperationException {
         if (isTrace()) {
             Log.v(TAG,
                     String.format("Moving from %s to %s", //$NON-NLS-1$
@@ -81,7 +83,7 @@ public class CopyCommand extends Program implements CopyExecutable {
         }
 
         //Copy recursively
-        if (!FileHelper.copyRecursive(s, d, getBufferSize())) {
+        if (!FileHelper.copyRecursive(s, d, getBufferSize(), this)) {
             if (isTrace()) {
                 Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
             }
index b0c85fb..c4943b2 100644 (file)
@@ -19,6 +19,7 @@ package com.cyanogenmod.filemanager.commands.java;
 import android.util.Log;
 
 import com.cyanogenmod.filemanager.commands.MoveExecutable;
+import com.cyanogenmod.filemanager.console.CancelledOperationException;
 import com.cyanogenmod.filemanager.console.ExecutionException;
 import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
 import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
@@ -64,7 +65,8 @@ public class MoveCommand extends Program implements MoveExecutable {
      */
     @Override
     public void execute()
-            throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+            throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException,
+                   CancelledOperationException {
         if (isTrace()) {
             Log.v(TAG,
                     String.format("Creating from %s to %s", this.mSrc, this.mDst)); //$NON-NLS-1$
@@ -81,7 +83,7 @@ public class MoveCommand extends Program implements MoveExecutable {
 
         //Move or copy recursively
         if (d.exists()) {
-            if (!FileHelper.copyRecursive(s, d, getBufferSize())) {
+            if (!FileHelper.copyRecursive(s, d, getBufferSize(), this)) {
                 if (isTrace()) {
                     Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
                 }
@@ -95,7 +97,7 @@ public class MoveCommand extends Program implements MoveExecutable {
         } else {
             // Move between filesystem is not allow. If rename fails then use copy operation
             if (!s.renameTo(d)) {
-                if (!FileHelper.copyRecursive(s, d, getBufferSize())) {
+                if (!FileHelper.copyRecursive(s, d, getBufferSize(), this)) {
                     if (isTrace()) {
                         Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
                     }
index b5eeebd..34e70cc 100644 (file)
@@ -17,6 +17,7 @@
 package com.cyanogenmod.filemanager.commands.java;
 
 import com.cyanogenmod.filemanager.commands.Executable;
+import com.cyanogenmod.filemanager.console.CancelledOperationException;
 import com.cyanogenmod.filemanager.console.ExecutionException;
 import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
 import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
@@ -29,6 +30,7 @@ public abstract class Program implements Executable {
 
     private boolean mTrace;
     private int mBufferSize;
+    private boolean mCancelled = false;
 
     /**
      * Constructor of <code>Program</code>
@@ -92,6 +94,14 @@ public abstract class Program implements Executable {
      * @throws ExecutionException If the operation returns a invalid exit code
      */
     public abstract void execute()
-            throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException;
+            throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException,
+                   CancelledOperationException;
 
+    public void requestCancel() {
+        mCancelled = true;
+    }
+
+    public boolean isCancelled() {
+        return mCancelled;
+    }
 }
index f59f746..741fc8f 100644 (file)
@@ -23,6 +23,7 @@ import com.cyanogenmod.filemanager.commands.Executable;
 import com.cyanogenmod.filemanager.commands.ExecutableFactory;
 import com.cyanogenmod.filemanager.commands.java.JavaExecutableFactory;
 import com.cyanogenmod.filemanager.commands.java.Program;
+import com.cyanogenmod.filemanager.console.CancelledOperationException;
 import com.cyanogenmod.filemanager.console.CommandNotFoundException;
 import com.cyanogenmod.filemanager.console.ConsoleAllocException;
 import com.cyanogenmod.filemanager.console.ExecutionException;
@@ -43,6 +44,7 @@ public final class JavaConsole extends VirtualConsole {
     private static final String TAG = "JavaConsole"; //$NON-NLS-1$
 
     private final int mBufferSize;
+    private Program mActiveProgram;
 
     /**
      * Constructor of <code>JavaConsole</code>
@@ -78,8 +80,8 @@ public final class JavaConsole extends VirtualConsole {
     @Override
     public synchronized void execute(Executable executable, Context ctx)
             throws ConsoleAllocException, InsufficientPermissionsException, NoSuchFileOrDirectory,
-            OperationTimeoutException, ExecutionException, CommandNotFoundException,
-            ReadOnlyFilesystemException {
+                OperationTimeoutException, ExecutionException, CommandNotFoundException,
+                   CancelledOperationException, ReadOnlyFilesystemException {
         // Check that the program is a java program
         try {
             Program p = (Program)executable;
@@ -98,6 +100,7 @@ public final class JavaConsole extends VirtualConsole {
 
         // Execute the program
         final Program program = (Program)executable;
+        mActiveProgram = program;
         program.setTrace(isTrace());
         program.setBufferSize(this.mBufferSize);
         if (program.isAsynchronous()) {
@@ -123,4 +126,12 @@ public final class JavaConsole extends VirtualConsole {
         }
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onCancel() {
+        mActiveProgram.requestCancel();
+        return true;
+    }
 }
\ No newline at end of file
index 8800607..b9464d2 100644 (file)
@@ -1252,8 +1252,13 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
                     }
                 } catch (Throwable ex) {
                     Log.w(TAG,
-                            String.format("Unable to kill current program: %s", //$NON-NLS-1$
-                                    this.mActiveCommand.getCommand()), ex);
+                            String.format("Unable to kill current program: %s",
+                                    (
+                                            (this.mActiveCommand == null) ?
+                                                    "" :
+                                                    this.mActiveCommand.getCommand()
+                                    )
+                            ), ex);
                 }
             }
         }
index 8964c5e..c127d3f 100644 (file)
@@ -77,6 +77,12 @@ public abstract class ActionsPolicy {
          * Method invoked when the operation was successfully
          */
         void onSuccess();
+
+        /**
+         * Method invoked to handle cancelling
+         */
+        void onCancel();
+
     }
 
     /**
@@ -117,7 +123,9 @@ public abstract class ActionsPolicy {
             this.mDialog.setOnCancelListener(new MessageProgressDialog.OnCancelListener() {
                 @Override
                 public boolean onCancel() {
-                    return task.cancel(true);
+                    mCallable.onCancel();
+                    task.cancel(true);
+                    return true;
                 }
             });
             Spanned progress = this.mCallable.requestProgress();
@@ -155,8 +163,7 @@ public abstract class ActionsPolicy {
 
         @Override
         protected void onCancelled() {
-            //Operation complete.
-            this.mCallable.onSuccess();
+            this.mCallable.onCancel();
         }
 
         @Override
@@ -182,4 +189,4 @@ public abstract class ActionsPolicy {
     protected static void showOperationSuccessMsg(Context ctx) {
         DialogHelper.showToast(ctx, R.string.msgs_success, Toast.LENGTH_SHORT);
     }
-}
\ No newline at end of file
+}
index c2c10e5..b5021b8 100644 (file)
@@ -266,6 +266,11 @@ public final class CompressActionPolicy extends ActionsPolicy {
             }
 
             @Override
+            public void onCancel() {
+                // nop
+            }
+
+            @Override
             public void doInBackground(Object... params) throws Throwable {
                 this.mCause = null;
                 this.mStarted = true;
@@ -568,6 +573,11 @@ public final class CompressActionPolicy extends ActionsPolicy {
             }
 
             @Override
+            public void onCancel() {
+                // nop
+            }
+
+            @Override
             public void doInBackground(Object... params) throws Throwable {
                 this.mCause = null;
                 this.mStarted = true;
@@ -842,4 +852,4 @@ public final class CompressActionPolicy extends ActionsPolicy {
     protected static void showOperationSuccessMsg(Context ctx, int res, String dst) {
         DialogHelper.showToast(ctx, ctx.getString(res, dst), Toast.LENGTH_SHORT);
     }
-}
\ No newline at end of file
+}
index 33e0b98..c2dde32 100644 (file)
@@ -19,10 +19,13 @@ package com.cyanogenmod.filemanager.ui.policy;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
 import android.text.Html;
 import android.text.Spanned;
 
 import com.cyanogenmod.filemanager.R;
+import com.cyanogenmod.filemanager.console.CancelledOperationException;
+import com.cyanogenmod.filemanager.console.Console;
 import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
 import com.cyanogenmod.filemanager.console.RelaunchableException;
 import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
@@ -280,7 +283,7 @@ public final class CopyMoveActionPolicy extends ActionsPolicy {
             }
             @Override
             public boolean isDialogCancellable() {
-                return false;
+                return true;
             }
 
             @Override
@@ -301,8 +304,7 @@ public final class CopyMoveActionPolicy extends ActionsPolicy {
                 return Html.fromHtml(progress);
             }
 
-            @Override
-            public void onSuccess() {
+            private void refreshUIAfterCompletion() {
                 // Remove orphan bookmark paths
                 if (files != null) {
                     for (LinkedResource linkedFiles : files) {
@@ -315,6 +317,11 @@ public final class CopyMoveActionPolicy extends ActionsPolicy {
                   // The reference is not the same, so refresh the complete navigation view
                   this.mOnRequestRefreshListener.onRequestRefresh(null, true);
                 }
+            }
+
+            @Override
+            public void onSuccess() {
+                refreshUIAfterCompletion();
                 ActionsPolicy.showOperationSuccessMsg(ctx);
             }
 
@@ -341,6 +348,21 @@ public final class CopyMoveActionPolicy extends ActionsPolicy {
                 }
             }
 
+            @Override
+            public void onCancel() {
+                if (mSrcConsole != null) {
+                    mSrcConsole.onCancel();
+                }
+                if (mDstConsole != null) {
+                    mDstConsole.onCancel();
+                }
+                refreshUIAfterCompletion();
+            }
+
+            // Handles required for issuing command death to the consoles
+            private Console mSrcConsole;
+            private Console mDstConsole;
+
             /**
              * Method that copy or move the file to another location
              *
@@ -360,6 +382,21 @@ public final class CopyMoveActionPolicy extends ActionsPolicy {
                     // under using absolute paths) Issue: CYAN-2791
                     String source = src.getAbsolutePath() +
                             (src.isDirectory() ? File.separator : "");
+                    String dest = dst.getAbsolutePath() +
+                            (dst.isDirectory() ? File.separator : "");
+
+                    /*
+                        There is a possibility that the src and dst can have different consoles.
+                        A possible case:
+                          - src is from sd card and dst is secure storage
+                        This could happen with anything that goes from a real console to a virtual
+                        console or visa versa.  Here we grab a handle on the console such that we
+                        may explicitly kill the actions happening in both consoles.
+                     */
+                    // Need to derive the console for the source
+                    mSrcConsole = CommandHelper.ensureConsoleForFile(ctx, null, source);
+                    // Need to derive the console for the destination
+                    mDstConsole = CommandHelper.ensureConsoleForFile(ctx, null, dest);
 
                     // Copy or move?
                     if (operation.compareTo(COPY_MOVE_OPERATION.MOVE) == 0 ||
@@ -368,13 +405,13 @@ public final class CopyMoveActionPolicy extends ActionsPolicy {
                                 ctx,
                                 source,
                                 dst.getAbsolutePath(),
-                                null);
+                                mSrcConsole);
                     } else {
                         CommandHelper.copy(
                                 ctx,
                                 source,
                                 dst.getAbsolutePath(),
-                                null);
+                                mSrcConsole);
                     }
                 } catch (Exception e) {
                     // Need to be relaunched?
@@ -545,4 +582,4 @@ public final class CopyMoveActionPolicy extends ActionsPolicy {
         }
         return true;
     }
-}
\ No newline at end of file
+}
index 843afe2..003ed46 100644 (file)
@@ -213,6 +213,11 @@ public final class DeleteActionPolicy extends ActionsPolicy {
             }
 
             @Override
+            public void onCancel() {
+
+            }
+
+            @Override
             public void doInBackground(Object... params) throws Throwable {
                 this.mCause = null;
 
@@ -372,4 +377,4 @@ public final class DeleteActionPolicy extends ActionsPolicy {
         }
         return true;
     }
-}
\ No newline at end of file
+}
index de71798..33ddc40 100644 (file)
@@ -2012,7 +2012,7 @@ public final class CommandHelper {
      * @throws IOException If initial directory couldn't be checked
      * @throws FileNotFoundException If the initial directory not exists
      */
-    private static Console ensureConsoleForFile(Context context, Console console, String src)
+    public static Console ensureConsoleForFile(Context context, Console console, String src)
             throws FileNotFoundException, IOException, InvalidCommandDefinitionException,
             ConsoleAllocException, InsufficientPermissionsException {
 
index 14059d6..0cf98fe 100644 (file)
@@ -26,7 +26,9 @@ import android.util.Log;
 import com.cyanogenmod.filemanager.FileManagerApplication;
 import com.cyanogenmod.filemanager.R;
 import com.cyanogenmod.filemanager.commands.SyncResultExecutable;
+import com.cyanogenmod.filemanager.commands.java.Program;
 import com.cyanogenmod.filemanager.commands.shell.ResolveLinkCommand;
+import com.cyanogenmod.filemanager.console.CancelledOperationException;
 import com.cyanogenmod.filemanager.console.Console;
 import com.cyanogenmod.filemanager.console.ExecutionException;
 import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
@@ -1105,7 +1107,8 @@ public final class FileHelper {
      * @throws ExecutionException If a problem was detected in the operation
      */
     public static boolean copyRecursive(
-            final File src, final File dst, int bufferSize) throws ExecutionException {
+            final File src, final File dst, int bufferSize, Program program)
+                throws ExecutionException, CancelledOperationException {
         if (src.isDirectory()) {
             // Create the directory
             if (dst.exists() && !dst.isDirectory()) {
@@ -1122,14 +1125,20 @@ public final class FileHelper {
             File[] files = src.listFiles();
             if (files != null) {
                 for (int i = 0; i < files.length; i++) {
-                    if (!copyRecursive(files[i], new File(dst, files[i].getName()), bufferSize)) {
+                    // Short circuit if we've been cancelled. Show's over :(
+                    if (program.isCancelled()) {
+                        throw new CancelledOperationException();
+                    }
+
+                    if (!copyRecursive(files[i], new File(dst, files[i].getName()), bufferSize,
+                                       program)) {
                         return false;
                     }
                 }
             }
         } else {
             // Copy the directory
-            if (!bufferedCopy(src, dst,bufferSize)) {
+            if (!bufferedCopy(src, dst,bufferSize, program)) {
                 return false;
             }
         }
@@ -1145,7 +1154,8 @@ public final class FileHelper {
      * @return boolean If the operation complete successfully
      */
     public static boolean bufferedCopy(final File src, final File dst,
-        int bufferSize) throws ExecutionException {
+        int bufferSize, Program program)
+            throws ExecutionException, CancelledOperationException {
         BufferedInputStream bis = null;
         BufferedOutputStream bos = null;
         try {
@@ -1154,6 +1164,10 @@ public final class FileHelper {
             int read = 0;
             byte[] data = new byte[bufferSize];
             while ((read = bis.read(data, 0, bufferSize)) != -1) {
+                // Short circuit if we've been cancelled. Show's over :(
+                if (program.isCancelled()) {
+                    throw new CancelledOperationException();
+                }
                 bos.write(data, 0, read);
             }
             return true;
@@ -1167,6 +1181,9 @@ public final class FileHelper {
             if (e.getCause() instanceof ErrnoException
                         && ((ErrnoException)e.getCause()).errno == OsConstants.ENOSPC) {
                 throw new ExecutionException(R.string.msgs_no_disk_space);
+            } if (e instanceof CancelledOperationException) {
+                // If the user cancelled this operation, let it through.
+                throw (CancelledOperationException)e;
             }
 
             return false;