diff options
| author | jruesga <jorge@ruesga.com> | 2012-10-19 01:05:28 +0200 |
|---|---|---|
| committer | jruesga <jorge@ruesga.com> | 2012-10-19 01:05:28 +0200 |
| commit | 54e70eae6bdedc0f11fbd36e576162b8ba1e63b0 (patch) | |
| tree | 4489e24d9f363aa6c728a2bb877da1aaaefb2aff /src | |
| parent | f3dd56e14219da4edaea88f1d3942412a854d816 (diff) | |
New fso action "Extract"
Diffstat (limited to 'src')
| -rw-r--r-- | src/com/cyanogenmod/explorer/ui/dialogs/ActionsDialog.java | 17 | ||||
| -rw-r--r-- | src/com/cyanogenmod/explorer/ui/policy/ActionsPolicy.java | 323 |
2 files changed, 333 insertions, 7 deletions
diff --git a/src/com/cyanogenmod/explorer/ui/dialogs/ActionsDialog.java b/src/com/cyanogenmod/explorer/ui/dialogs/ActionsDialog.java index 82e649a..d83f7df 100644 --- a/src/com/cyanogenmod/explorer/ui/dialogs/ActionsDialog.java +++ b/src/com/cyanogenmod/explorer/ui/dialogs/ActionsDialog.java @@ -298,6 +298,14 @@ public class ActionsDialog implements OnItemClickListener, OnItemLongClickListen } break; + //- Uncompress + case R.id.mnu_actions_extract: + ActionsPolicy.uncompress( + this.mContext, + this.mFso, + this.mOnRequestRefreshListener); + break; + //- Create copy case R.id.mnu_actions_create_copy: // Create a copy of the fso @@ -549,6 +557,15 @@ public class ActionsDialog implements OnItemClickListener, OnItemLongClickListen } } } + + // Compress/Uncompress (only when selection is available) + if (this.mOnSelectionListener != null) { + //Uncompress + if (!this.mGlobal && !FileHelper.isSupportedUncompressedFile(this.mFso)) { + menu.removeItem(R.id.mnu_actions_extract); + } + } + } /** diff --git a/src/com/cyanogenmod/explorer/ui/policy/ActionsPolicy.java b/src/com/cyanogenmod/explorer/ui/policy/ActionsPolicy.java index 1b9f2e3..2fc8679 100644 --- a/src/com/cyanogenmod/explorer/ui/policy/ActionsPolicy.java +++ b/src/com/cyanogenmod/explorer/ui/policy/ActionsPolicy.java @@ -29,9 +29,12 @@ import android.text.Spanned; import android.util.Log; import android.widget.Toast; +import com.cyanogenmod.explorer.ExplorerApplication; import com.cyanogenmod.explorer.R; import com.cyanogenmod.explorer.commands.AsyncResultListener; import com.cyanogenmod.explorer.commands.ExecExecutable; +import com.cyanogenmod.explorer.commands.UncompressExecutable; +import com.cyanogenmod.explorer.console.ConsoleBuilder; import com.cyanogenmod.explorer.console.ExecutionException; import com.cyanogenmod.explorer.console.RelaunchableException; import com.cyanogenmod.explorer.listeners.OnRequestRefreshListener; @@ -49,6 +52,7 @@ import com.cyanogenmod.explorer.util.DialogHelper; import com.cyanogenmod.explorer.util.ExceptionUtil; import com.cyanogenmod.explorer.util.ExceptionUtil.OnRelaunchCommandResult; import com.cyanogenmod.explorer.util.FileHelper; +import com.cyanogenmod.explorer.util.FixedQueue; import com.cyanogenmod.explorer.util.MimeTypeHelper; import java.io.File; @@ -256,6 +260,55 @@ public final class ActionsPolicy { } /** + * A class that holds a listener for compression/uncompression operations + */ + private static class CompressListener implements AsyncResultListener { + + Object mSync; + final FixedQueue<String> mQueue; + boolean mEnd; + Throwable mCause; + + /** + * Constructor of <code>CompressListener</code> + */ + public CompressListener() { + super(); + this.mEnd = false; + this.mSync = new Object(); + this.mQueue = new FixedQueue<String>(2); //Holds only one item + this.mCause = null; + } + + @Override + public void onPartialResult(Object result) { + synchronized (this.mSync) { + this.mQueue.insert((String)result); + } + } + + @Override + public void onException(Exception cause) { + synchronized (this.mSync) { + this.mCause = cause; + } + } + + @Override + public void onAsyncStart() {/**NON BLOCK**/} + + @Override + public void onAsyncEnd(boolean canceled) {/**NON BLOCK**/} + + @Override + public void onAsyncExitCode(int exitCode) { + synchronized (this.mSync) { + this.mEnd = true; + } + } + } + + /** * @hide */ private enum COPY_MOVE_OPERATION { @@ -495,8 +548,8 @@ public final class ActionsPolicy { // Execute the script ExecExecutable cmd = - (ExecExecutable)CommandHelper.exec( - ctx, fso.getFullPath(), listener, null); + CommandHelper.exec( + ctx, fso.getFullPath(), listener, null); dialog.setCmd(cmd); } catch (Exception e) { listener.onException(e); @@ -850,12 +903,10 @@ public final class ActionsPolicy { } /** - * Method that copies on file to other location + * Method that deletes the file or directory * * @param ctx The current context - * @param src The source file - * @param dst The destination file - * @param move Indicates if the files are going to be moved (true) or copied (false) + * @param fso The file or folder to be deleted */ @SuppressWarnings("hiding") private void doOperation( @@ -1175,7 +1226,7 @@ public final class ActionsPolicy { } /** - * Method that copies on file to other location + * Method that copy or move the file to another location * * @param ctx The current context * @param src The source file @@ -1290,6 +1341,264 @@ public final class ActionsPolicy { } /** + * Method that uncompress a compressed file. + * + * @param ctx The current context + * @param fso The compressed file + * @param onRequestRefreshListener The listener for request a refresh (optional) + * @hide + */ + public static void uncompress( + final Context ctx, final FileSystemObject fso, + final OnRequestRefreshListener onRequestRefreshListener) { + + // The callable interface + final BackgroundCallable callable = new BackgroundCallable() { + // The current items + final Context mCtx = ctx; + final FileSystemObject mFso = fso; + final OnRequestRefreshListener mOnRequestRefreshListener = onRequestRefreshListener; + + final Object mSync = new Object(); + Throwable mCause; + + final CompressListener mListener = + new CompressListener(); + private String mMsg; + private boolean mStarted = false; + + @Override + public int getDialogTitle() { + return R.string.waiting_dialog_extracting_title; + } + @Override + public int getDialogIcon() { + return R.drawable.ic_holo_light_operation; + } + + @Override + public Spanned requestProgress() { + // Initializing the dialog + if (!this.mStarted) { + String progress = + this.mCtx.getResources(). + getString( + R.string.waiting_dialog_extracting_analizing_msg); + return Html.fromHtml(progress); + } + + // Return the current operation + String msg = (this.mMsg == null) ? "" : this.mMsg; //$NON-NLS-1$ + String progress = + this.mCtx.getResources(). + getString( + R.string.waiting_dialog_extracting_msg, + msg); + return Html.fromHtml(progress); + } + + @Override + public void onSuccess() { + //Operation complete. Refresh + if (this.mOnRequestRefreshListener != null) { + // The reference is not the same, so refresh the complete navigation view + this.mOnRequestRefreshListener.onRequestRefresh(null); + } + ActionsPolicy.showOperationSuccessMsg(ctx); + } + + @Override + public void doInBackground(Object... params) throws Throwable { + this.mCause = null; + this.mStarted = true; + + // This method expect to receive + // 1.- BackgroundAsyncTask + BackgroundAsyncTask task = (BackgroundAsyncTask)params[0]; + String out = null; + try { + UncompressExecutable cmd = + CommandHelper.uncompress( + ctx, + this.mFso.getFullPath(), + this.mListener, null); + out = cmd.getOutUncompressedFile(); + + // Request paint the + this.mListener.mQueue.insert(out); + task.onRequestProgress(); + + // Don't use an active blocking because this suppose that all message + // will be processed by the UI. Instead, refresh with a delay and + // display the active file + while (!this.mListener.mEnd) { + // Sleep to don't saturate the UI thread + Thread.sleep(50L); + + List<String> msgs = this.mListener.mQueue.peekAll(); + if (msgs.size() > 0) { + this.mMsg = msgs.get(msgs.size()-1); + task.onRequestProgress(); + } + } + + // Dialog is ended. Force the last redraw + List<String> msgs = this.mListener.mQueue.peekAll(); + if (msgs.size() > 0) { + this.mMsg = msgs.get(msgs.size()-1); + task.onRequestProgress(); + } + + } catch (Exception e) { + // Need to be relaunched? + if (e instanceof RelaunchableException) { + OnRelaunchCommandResult rl = new OnRelaunchCommandResult() { + @Override + @SuppressWarnings("unqualified-field-access") + public void onSuccess() { + synchronized (mSync) { + mSync.notify(); + } + } + + @Override + @SuppressWarnings("unqualified-field-access") + public void onFailed(Throwable cause) { + mCause = cause; + synchronized (mSync) { + mSync.notify(); + } + } + @Override + @SuppressWarnings("unqualified-field-access") + public void onCanceled() { + synchronized (mSync) { + mSync.notify(); + } + } + }; + + // Translate the exception (and wait for the result) + ExceptionUtil.translateException(ctx, e, false, true, rl); + synchronized (this.mSync) { + this.mSync.wait(); + } + + // Persist the exception? + if (this.mCause != null) { + // The exception must be elevated + throw this.mCause; + } + + } else { + // The exception must be elevated + throw e; + } + } + + + // Any exception? + if (this.mListener.mCause != null) { + throw this.mListener.mCause; + } + + // Check that the operation was completed retrieving the extracted file or folder + boolean failed = true; + try { + CommandHelper.getFileInfo(ctx, out, false, null); + + // Failed. The file exists + failed = false; + + } catch (Throwable e) { + // Operation complete successfully + } + if (failed) { + throw new ExecutionException( + String.format( + "Failed to extract file: %s", //$NON-NLS-1$ + this.mFso.getFullPath())); + } + } + }; + final BackgroundAsyncTask task = new BackgroundAsyncTask(ctx, callable); + + // Check if the output exists + boolean askUser = false; + try { + UncompressExecutable ucmd = + ExplorerApplication.getBackgroundConsole(). + getExecutableFactory().newCreator(). + createUncompressExecutable(fso.getFullPath(), null); + String dst = ucmd.getOutUncompressedFile(); + FileSystemObject info = CommandHelper.getFileInfo(ctx, dst, null); + if (info != null) { + askUser = true; + } + } catch (Exception e) {/**NON BLOCK**/} + + // Ask the user because the destination file or folder exists + if (askUser) { + //Show a dialog asking the user for overwrite the files + AlertDialog dialog = + DialogHelper.createTwoButtonsQuestionDialog( + ctx, + android.R.string.cancel, + R.string.overwrite, + ctx.getString(R.string.msgs_overwrite_files), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface alertDialog, int which) { + // NEGATIVE (overwrite) POSITIVE (cancel) + if (which == DialogInterface.BUTTON_NEGATIVE) { + // Check if the necessary to display a warning because + // security issues + checkZipSecurityWarning(ctx, task, fso); + } + } + }); + dialog.show(); + } else { + // Execute background task + task.execute(task); + } + } + + /** + * Method that checks if it is necessary to display a warning dialog because + * the privileged extraction of a zip file. + * + * @param ctx The current context + * @param task The task + * @param fso The zip file + * @hide + */ + static void checkZipSecurityWarning( + final Context ctx, final BackgroundAsyncTask task, FileSystemObject fso) { + // WARNING! Extracting a ZIP file with relatives or absolutes path could break + // the system and is need a security alert that the user can confirm prior to + // make the extraction + String ext = FileHelper.getExtension(fso); + if (ConsoleBuilder.isPrivileged() && ext.compareTo("zip") == 0) { //$NON-NLS-1$ + AlertDialog dialog =DialogHelper.createYesNoDialog( + ctx, R.string.security_warning_extract, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface alertDialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + // Execute background task + task.execute(task); + } + } + }); + dialog.show(); + } else { + // Execute background task + task.execute(task); + } + } + + /** * Method that check if is needed to prompt the user for overwrite prior to do * the operation. * |
