2 * Copyright (C) 2012 The CyanogenMod Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.cyanogenmod.filemanager.ui.policy;
19 import android.app.AlertDialog;
20 import android.content.Context;
21 import android.content.DialogInterface;
22 import android.text.Html;
23 import android.text.Spanned;
25 import com.cyanogenmod.filemanager.R;
26 import com.cyanogenmod.filemanager.console.ExecutionException;
27 import com.cyanogenmod.filemanager.console.RelaunchableException;
28 import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
29 import com.cyanogenmod.filemanager.listeners.OnSelectionListener;
30 import com.cyanogenmod.filemanager.model.FileSystemObject;
31 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
32 import com.cyanogenmod.filemanager.util.CommandHelper;
33 import com.cyanogenmod.filemanager.util.DialogHelper;
34 import com.cyanogenmod.filemanager.util.ExceptionUtil;
35 import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
36 import com.cyanogenmod.filemanager.util.FileHelper;
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.Comparator;
41 import java.util.List;
45 * A class with the convenience methods for resolve delete related actions
47 public final class DeleteActionPolicy extends ActionsPolicy {
50 * Method that remove an existing file system object.
52 * @param ctx The current context
53 * @param fso The file system object to remove
54 * @param onSelectionListener The listener for obtain selection information (required)
55 * @param onRequestRefreshListener The listener for request a refresh (optional)
56 * @param onItemFlingerResponder The flinger responder, only if the action was initialized
57 * by a flinger gesture (optional)
59 public static void removeFileSystemObject(
60 final Context ctx, final FileSystemObject fso,
61 final OnSelectionListener onSelectionListener,
62 final OnRequestRefreshListener onRequestRefreshListener,
63 final OnItemFlingerResponder onItemFlingerResponder) {
64 // Generate an array and invoke internal method
65 List<FileSystemObject> files = new ArrayList<FileSystemObject>(1);
67 removeFileSystemObjects(
68 ctx, files, onSelectionListener,
69 onRequestRefreshListener, onItemFlingerResponder);
73 * Method that remove an existing file system object.
75 * @param ctx The current context
76 * @param files The list of files to remove
77 * @param onSelectionListener The listener for obtain selection information (required)
78 * @param onRequestRefreshListener The listener for request a refresh (optional)
79 * @param onItemFlingerResponder The flinger responder, only if the action was initialized
80 * by a flinger gesture (optional)
82 public static void removeFileSystemObjects(
83 final Context ctx, final List<FileSystemObject> files,
84 final OnSelectionListener onSelectionListener,
85 final OnRequestRefreshListener onRequestRefreshListener,
86 final OnItemFlingerResponder onItemFlingerResponder) {
88 // Ask the user before remove
89 AlertDialog dialog = DialogHelper.createYesNoDialog(
91 R.string.confirm_deletion,
92 R.string.actions_ask_undone_operation_msg,
93 new DialogInterface.OnClickListener() {
95 public void onClick(DialogInterface alertDialog, int which) {
96 if (which == DialogInterface.BUTTON_POSITIVE) {
98 removeFileSystemObjectsInBackground(
102 onRequestRefreshListener,
103 onItemFlingerResponder);
105 // Flinger operation should be cancelled
106 if (onItemFlingerResponder != null) {
107 onItemFlingerResponder.cancel();
112 DialogHelper.delegateDialogShow(ctx, dialog);
116 * Method that remove an existing file system object in background.
118 * @param ctx The current context
119 * @param files The list of files to remove
120 * @param onSelectionListener The listener for obtain selection information (optional)
121 * @param onRequestRefreshListener The listener for request a refresh (optional)
122 * @param onItemFlingerResponder The flinger responder, only if the action was initialized
123 * by a flinger gesture (optional)
126 static void removeFileSystemObjectsInBackground(
127 final Context ctx, final List<FileSystemObject> files,
128 final OnSelectionListener onSelectionListener,
129 final OnRequestRefreshListener onRequestRefreshListener,
130 final OnItemFlingerResponder onItemFlingerResponder) {
132 // Some previous checks prior to execute
133 // 1.- Check the operation consistency (only if it is viable)
134 if (onSelectionListener != null) {
135 final String currentDirectory = onSelectionListener.onRequestCurrentDir();
136 if (!checkRemoveConsistency(ctx, files, currentDirectory)) {
140 // 2.- Sort the items by path to avoid delete parents fso prior to child fso
141 final List<FileSystemObject> sortedFsos = new ArrayList<FileSystemObject>(files);
142 Collections.sort(sortedFsos, new Comparator<FileSystemObject>() {
144 public int compare(FileSystemObject lhs, FileSystemObject rhs) {
145 return lhs.compareTo(rhs) * -1; //Reverse
149 // The callable interface
150 final BackgroundCallable callable = new BackgroundCallable() {
152 private int mCurrent = 0;
153 final Context mCtx = ctx;
154 final List<FileSystemObject> mFiles = sortedFsos;
155 final OnRequestRefreshListener mOnRequestRefreshListener = onRequestRefreshListener;
157 final Object mSync = new Object();
161 public int getDialogTitle() {
162 return R.string.waiting_dialog_deleting_title;
165 public int getDialogIcon() {
169 public boolean isDialogCancellable() {
174 public Spanned requestProgress() {
175 FileSystemObject fso = this.mFiles.get(this.mCurrent);
177 // Return the current operation
179 this.mCtx.getResources().
181 R.string.waiting_dialog_deleting_msg,
183 return Html.fromHtml(progress);
187 public void onSuccess() {
188 //Operation complete.
190 // Confirms flinger operation
191 if (onItemFlingerResponder != null) {
192 onItemFlingerResponder.accept();
196 if (this.mOnRequestRefreshListener != null) {
197 // The reference is not the same, so refresh the complete navigation view
198 if (files != null && files.size() == 1) {
199 this.mOnRequestRefreshListener.onRequestRemove(files.get(0), true);
201 this.mOnRequestRefreshListener.onRequestRemove(null, true);
204 ActionsPolicy.showOperationSuccessMsg(ctx);
208 public void doInBackground(Object... params) throws Throwable {
211 // This method expect to receive
212 // 1.- BackgroundAsyncTask
213 BackgroundAsyncTask task = (BackgroundAsyncTask)params[0];
215 int cc = this.mFiles.size();
216 for (int i = 0; i < cc; i++) {
217 FileSystemObject fso = this.mFiles.get(i);
219 doOperation(this.mCtx, fso);
223 if (this.mCurrent < this.mFiles.size()) {
224 task.onRequestProgress();
230 * Method that deletes the file or directory
232 * @param ctx The current context
233 * @param fso The file or folder to be deleted
235 @SuppressWarnings("hiding")
236 private void doOperation(
237 final Context ctx, final FileSystemObject fso) throws Throwable {
240 if (FileHelper.isDirectory(fso)) {
241 CommandHelper.deleteDirectory(ctx, fso.getFullPath(), null);
243 CommandHelper.deleteFile(ctx, fso.getFullPath(), null);
245 } catch (Exception e) {
246 // Need to be relaunched?
247 if (e instanceof RelaunchableException) {
248 OnRelaunchCommandResult rl = new OnRelaunchCommandResult() {
250 @SuppressWarnings("unqualified-field-access")
251 public void onSuccess() {
252 synchronized (mSync) {
258 @SuppressWarnings("unqualified-field-access")
259 public void onFailed(Throwable cause) {
261 synchronized (mSync) {
266 @SuppressWarnings("unqualified-field-access")
267 public void onCancelled() {
268 synchronized (mSync) {
274 // Translate the exception (and wait for the result)
275 ExceptionUtil.translateException(ctx, e, false, true, rl);
276 synchronized (this.mSync) {
280 // Persist the exception?
281 if (this.mCause != null) {
282 // Cancels the flinger
283 if (onItemFlingerResponder != null) {
284 onItemFlingerResponder.cancel();
287 // The exception must be elevated
292 // Cancels the flinger
293 if (onItemFlingerResponder != null) {
294 onItemFlingerResponder.cancel();
297 // The exception must be elevated
302 // Check that the operation was completed retrieving the deleted fso
303 boolean failed = false;
305 CommandHelper.getFileInfo(ctx, fso.getFullPath(), false, null);
306 FileSystemObject fso2 =
307 CommandHelper.getFileInfo(ctx, fso.getFullPath(), false, null);
309 // Failed. The file still exists
313 } catch (Throwable e) {
314 // Operation complete successfully
317 // Cancels the flinger
318 if (onItemFlingerResponder != null) {
319 onItemFlingerResponder.cancel();
322 throw new ExecutionException(
324 "Failed to delete file: %s", fso.getFullPath())); //$NON-NLS-1$
328 final BackgroundAsyncTask task = new BackgroundAsyncTask(ctx, callable);
330 // Execute background task
335 * Method that check the consistency of delete operations.<br/>
337 * The method checks the following rules:<br/>
339 * <li>Any of the files of the move or delete operation can not include the
340 * current directory.</li>
343 * @param ctx The current context
344 * @param files The list of source/destination files
345 * @param currentDirectory The current directory
346 * @return boolean If the consistency is validate successfully
348 private static boolean checkRemoveConsistency(
349 Context ctx, List<FileSystemObject> files, String currentDirectory) {
350 int cc = files.size();
351 for (int i = 0; i < cc; i++) {
352 FileSystemObject fso = files.get(i);
354 // 1.- Current directory can't be deleted
355 if (currentDirectory.startsWith(fso.getFullPath())) {
356 // Operation not allowed
358 DialogHelper.createWarningDialog(
360 R.string.warning_title,
361 R.string.msgs_unresolved_inconsistencies);
362 DialogHelper.delegateDialogShow(ctx, dialog);