aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/layout/bookmarks.xml2
-rw-r--r--res/layout/navigation_view_details.xml3
-rw-r--r--res/layout/navigation_view_simple.xml3
-rw-r--r--res/layout/search.xml2
-rw-r--r--res/values/strings.xml6
-rw-r--r--res/xml/preferences_general.xml8
-rw-r--r--src/com/cyanogenmod/filemanager/activities/BookmarksActivity.java85
-rw-r--r--src/com/cyanogenmod/filemanager/activities/NavigationActivity.java15
-rw-r--r--src/com/cyanogenmod/filemanager/activities/SearchActivity.java70
-rw-r--r--src/com/cyanogenmod/filemanager/activities/preferences/SettingsPreferences.java7
-rw-r--r--src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java5
-rw-r--r--src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java6
-rw-r--r--src/com/cyanogenmod/filemanager/ui/policy/DeleteActionPolicy.java60
-rw-r--r--src/com/cyanogenmod/filemanager/ui/widgets/FlingerListView.java446
-rw-r--r--src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java114
15 files changed, 810 insertions, 22 deletions
diff --git a/res/layout/bookmarks.xml b/res/layout/bookmarks.xml
index 5ae022e1..f2a0854a 100644
--- a/res/layout/bookmarks.xml
+++ b/res/layout/bookmarks.xml
@@ -17,7 +17,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ListView
+ <com.cyanogenmod.filemanager.ui.widgets.FlingerListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bookmarks_listview"
android:layout_width="match_parent"
diff --git a/res/layout/navigation_view_details.xml b/res/layout/navigation_view_details.xml
index d66977fb..b3c078f3 100644
--- a/res/layout/navigation_view_details.xml
+++ b/res/layout/navigation_view_details.xml
@@ -13,7 +13,8 @@
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License. -->
-<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.cyanogenmod.filemanager.ui.widgets.FlingerListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigation_view_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
diff --git a/res/layout/navigation_view_simple.xml b/res/layout/navigation_view_simple.xml
index d66977fb..b3c078f3 100644
--- a/res/layout/navigation_view_simple.xml
+++ b/res/layout/navigation_view_simple.xml
@@ -13,7 +13,8 @@
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License. -->
-<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.cyanogenmod.filemanager.ui.widgets.FlingerListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigation_view_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
diff --git a/res/layout/search.xml b/res/layout/search.xml
index 6e6863cd..befc89e6 100644
--- a/res/layout/search.xml
+++ b/res/layout/search.xml
@@ -68,7 +68,7 @@
android:textAppearance="@style/secondary_text_appearance" />
</RelativeLayout>
- <ListView
+ <com.cyanogenmod.filemanager.ui.widgets.FlingerListView
android:id="@+id/search_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 93cd3b99..85c00ad8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -630,7 +630,11 @@
<!-- Preferences * General * Compute folder statistics summary on -->
<string name="pref_compute_folder_statistics_on">Warning! The computation of folder statistics is costly in time and
system resources</string>
- <!-- Preferences * General * Advanced settings category -->
+ <!-- Preferences * General * Use flinger detection -->
+ <string name="pref_use_flinger">Use swipe gestures</string>
+ <!-- Preferences * General * Use flinger detection summary -->
+ <string name="pref_use_flinger_summary">Use swipe left to right gesture detection to delete files or folders.</string>
+ <!-- Preferences * General * Advanced settings category -->
<string name="pref_general_advanced_settings_category">Advanced</string>
<!-- Preferences * General * Access mode -->
<string name="pref_access_mode">Access mode</string>
diff --git a/res/xml/preferences_general.xml b/res/xml/preferences_general.xml
index e195e187..1a286a50 100644
--- a/res/xml/preferences_general.xml
+++ b/res/xml/preferences_general.xml
@@ -46,6 +46,14 @@
android:persistent="true"
android:defaultValue="false" />
+ <!-- Use flinger -->
+ <CheckBoxPreference
+ android:key="cm_filemanager_use_flinger"
+ android:title="@string/pref_use_flinger"
+ android:summary="@string/pref_use_flinger_summary"
+ android:persistent="true"
+ android:defaultValue="true" />
+
</PreferenceCategory>
<!-- Advanced settings -->
diff --git a/src/com/cyanogenmod/filemanager/activities/BookmarksActivity.java b/src/com/cyanogenmod/filemanager/activities/BookmarksActivity.java
index 84880dd6..c2d1b1d5 100644
--- a/src/com/cyanogenmod/filemanager/activities/BookmarksActivity.java
+++ b/src/com/cyanogenmod/filemanager/activities/BookmarksActivity.java
@@ -49,6 +49,9 @@ import com.cyanogenmod.filemanager.preferences.Bookmarks;
import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
import com.cyanogenmod.filemanager.preferences.Preferences;
import com.cyanogenmod.filemanager.ui.dialogs.InitialDirectoryDialog;
+import com.cyanogenmod.filemanager.ui.widgets.FlingerListView;
+import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerListener;
+import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
import com.cyanogenmod.filemanager.util.CommandHelper;
import com.cyanogenmod.filemanager.util.DialogHelper;
import com.cyanogenmod.filemanager.util.ExceptionUtil;
@@ -67,6 +70,62 @@ public class BookmarksActivity extends Activity implements OnItemClickListener,
private static boolean DEBUG = false;
+ /**
+ * A listener for flinging events from {@link FlingerListView}
+ */
+ private final OnItemFlingerListener mOnItemFlingerListener = new OnItemFlingerListener() {
+
+ @Override
+ public boolean onItemFlingerStart(
+ AdapterView<?> parent, View view, int position, long id) {
+ try {
+ // Response if the item can be removed
+ BookmarksAdapter adapter = (BookmarksAdapter)parent.getAdapter();
+ Bookmark bookmark = adapter.getItem(position);
+ if (bookmark != null &&
+ bookmark.mType.compareTo(BOOKMARK_TYPE.USER_DEFINED) == 0) {
+ return true;
+ }
+ } catch (Exception e) {
+ ExceptionUtil.translateException(BookmarksActivity.this, e, true, false);
+ }
+ return false;
+ }
+
+ @Override
+ public void onItemFlingerEnd(OnItemFlingerResponder responder,
+ AdapterView<?> parent, View view, int position, long id) {
+
+ try {
+ // Response if the item can be removed
+ BookmarksAdapter adapter = (BookmarksAdapter)parent.getAdapter();
+ Bookmark bookmark = adapter.getItem(position);
+ if (bookmark != null &&
+ bookmark.mType.compareTo(BOOKMARK_TYPE.USER_DEFINED) == 0) {
+ boolean result = Bookmarks.removeBookmark(BookmarksActivity.this, bookmark);
+ if (!result) {
+ //Show warning
+ DialogHelper.showToast(BookmarksActivity.this,
+ R.string.msgs_operation_failure, Toast.LENGTH_SHORT);
+ responder.cancel();
+ return;
+ }
+ responder.accept();
+ adapter.remove(bookmark);
+ adapter.notifyDataSetChanged();
+ return;
+ }
+
+ // Cancels the flinger operation
+ responder.cancel();
+
+ } catch (Exception e) {
+ ExceptionUtil.translateException(BookmarksActivity.this, e, true, false);
+ responder.cancel();
+ }
+ }
+ };
+
// Bookmark list XML tags
private static final String TAG_BOOKMARKS = "Bookmarks"; //$NON-NLS-1$
private static final String TAG_BOOKMARK = "bookmark"; //$NON-NLS-1$
@@ -143,6 +202,20 @@ public class BookmarksActivity extends Activity implements OnItemClickListener,
BookmarksAdapter adapter = new BookmarksAdapter(this, bookmarks, this);
this.mBookmarksListView.setAdapter(adapter);
this.mBookmarksListView.setOnItemClickListener(this);
+
+ // If we should set the listview to response to flinger gesture detection
+ boolean useFlinger =
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
+ ((Boolean)FileManagerSettings.
+ SETTINGS_USE_FLINGER.
+ getDefaultValue()).booleanValue());
+ if (useFlinger) {
+ ((FlingerListView)this.mBookmarksListView).
+ setOnItemFlingerListener(this.mOnItemFlingerListener);
+ }
+
+ // Reload the data
refresh();
}
@@ -243,9 +316,9 @@ public class BookmarksActivity extends Activity implements OnItemClickListener,
@Override
public void onClick(View v) {
//Retrieve the position
- int position = ((Integer)v.getTag()).intValue();
- BookmarksAdapter adapter = (BookmarksAdapter)this.mBookmarksListView.getAdapter();
- Bookmark bookmark = adapter.getItem(position);
+ final int position = ((Integer)v.getTag()).intValue();
+ final BookmarksAdapter adapter = (BookmarksAdapter)this.mBookmarksListView.getAdapter();
+ final Bookmark bookmark = adapter.getItem(position);
//Configure home
if (bookmark.mType.compareTo(BOOKMARK_TYPE.HOME) == 0) {
@@ -254,7 +327,8 @@ public class BookmarksActivity extends Activity implements OnItemClickListener,
dialog.setOnValueChangedListener(new InitialDirectoryDialog.OnValueChangedListener() {
@Override
public void onValueChanged(String newInitialDir) {
- refresh();
+ adapter.getItem(position).mPath = newInitialDir;
+ adapter.notifyDataSetChanged();
}
});
dialog.show();
@@ -269,7 +343,8 @@ public class BookmarksActivity extends Activity implements OnItemClickListener,
DialogHelper.showToast(this, R.string.msgs_operation_failure, Toast.LENGTH_SHORT);
return;
}
- refresh();
+ adapter.remove(bookmark);
+ adapter.notifyDataSetChanged();
return;
}
}
diff --git a/src/com/cyanogenmod/filemanager/activities/NavigationActivity.java b/src/com/cyanogenmod/filemanager/activities/NavigationActivity.java
index a25c650c..ad402c3c 100644
--- a/src/com/cyanogenmod/filemanager/activities/NavigationActivity.java
+++ b/src/com/cyanogenmod/filemanager/activities/NavigationActivity.java
@@ -195,6 +195,19 @@ public class NavigationActivity extends Activity
return;
}
+ // Use flinger
+ if (key.compareTo(FileManagerSettings.
+ SETTINGS_USE_FLINGER.getId()) == 0) {
+ boolean useFlinger =
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
+ ((Boolean)FileManagerSettings.
+ SETTINGS_USE_FLINGER.
+ getDefaultValue()).booleanValue());
+ getCurrentNavigationView().setUseFlinger(useFlinger);
+ return;
+ }
+
// Access mode
if (key.compareTo(FileManagerSettings.
SETTINGS_ACCESS_MODE.getId()) == 0) {
@@ -862,6 +875,8 @@ public class NavigationActivity extends Activity
//Remove from history
removeFromHistory((FileSystemObject)o);
+ } else {
+ onRequestRefresh(null);
}
this.getCurrentNavigationView().onDeselectAll();
}
diff --git a/src/com/cyanogenmod/filemanager/activities/SearchActivity.java b/src/com/cyanogenmod/filemanager/activities/SearchActivity.java
index 8a6161fa..0375b997 100644
--- a/src/com/cyanogenmod/filemanager/activities/SearchActivity.java
+++ b/src/com/cyanogenmod/filemanager/activities/SearchActivity.java
@@ -56,6 +56,7 @@ import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
import com.cyanogenmod.filemanager.model.Directory;
import com.cyanogenmod.filemanager.model.FileSystemObject;
+import com.cyanogenmod.filemanager.model.ParentDirectory;
import com.cyanogenmod.filemanager.model.Query;
import com.cyanogenmod.filemanager.model.SearchResult;
import com.cyanogenmod.filemanager.model.Symlink;
@@ -67,8 +68,12 @@ import com.cyanogenmod.filemanager.providers.RecentSearchesContentProvider;
import com.cyanogenmod.filemanager.tasks.SearchResultDrawingAsyncTask;
import com.cyanogenmod.filemanager.ui.dialogs.ActionsDialog;
import com.cyanogenmod.filemanager.ui.dialogs.MessageProgressDialog;
+import com.cyanogenmod.filemanager.ui.policy.DeleteActionPolicy;
import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
import com.cyanogenmod.filemanager.ui.widgets.ButtonItem;
+import com.cyanogenmod.filemanager.ui.widgets.FlingerListView;
+import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerListener;
+import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
import com.cyanogenmod.filemanager.util.CommandHelper;
import com.cyanogenmod.filemanager.util.DialogHelper;
import com.cyanogenmod.filemanager.util.ExceptionUtil;
@@ -139,6 +144,59 @@ public class SearchActivity extends Activity
};
/**
+ * A listener for flinging events from {@link FlingerListView}
+ */
+ private final OnItemFlingerListener mOnItemFlingerListener = new OnItemFlingerListener() {
+
+ @Override
+ public boolean onItemFlingerStart(
+ AdapterView<?> parent, View view, int position, long id) {
+ try {
+ // Response if the item can be removed
+ SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter();
+ SearchResult result = adapter.getItem(position);
+ if (result != null && result.getFso() != null) {
+ if (result.getFso() instanceof ParentDirectory) {
+ // This is not possible ...
+ return false;
+ }
+ return true;
+ }
+ } catch (Exception e) {
+ ExceptionUtil.translateException(SearchActivity.this, e, true, false);
+ }
+ return false;
+ }
+
+ @Override
+ public void onItemFlingerEnd(OnItemFlingerResponder responder,
+ AdapterView<?> parent, View view, int position, long id) {
+
+ try {
+ // Response if the item can be removed
+ SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter();
+ SearchResult result = adapter.getItem(position);
+ if (result != null && result.getFso() != null) {
+ DeleteActionPolicy.removeFileSystemObject(
+ SearchActivity.this,
+ result.getFso(),
+ null,
+ SearchActivity.this,
+ responder);
+ return;
+ }
+
+ // Cancels the flinger operation
+ responder.cancel();
+
+ } catch (Exception e) {
+ ExceptionUtil.translateException(SearchActivity.this, e, true, false);
+ responder.cancel();
+ }
+ }
+ };
+
+ /**
* @hide
*/
MessageProgressDialog mDialog = null;
@@ -345,6 +403,18 @@ public class SearchActivity extends Activity
this.mSearchListView.setOnItemClickListener(this);
this.mSearchListView.setOnItemLongClickListener(this);
+ // If we should set the listview to response to flinger gesture detection
+ boolean useFlinger =
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
+ ((Boolean)FileManagerSettings.
+ SETTINGS_USE_FLINGER.
+ getDefaultValue()).booleanValue());
+ if (useFlinger) {
+ ((FlingerListView)this.mSearchListView).
+ setOnItemFlingerListener(this.mOnItemFlingerListener);
+ }
+
//Other components
this.mSearchWaiting = (ProgressBar)findViewById(R.id.search_waiting);
this.mSearchFoundItems = (TextView)findViewById(R.id.search_status_found_items);
diff --git a/src/com/cyanogenmod/filemanager/activities/preferences/SettingsPreferences.java b/src/com/cyanogenmod/filemanager/activities/preferences/SettingsPreferences.java
index b253b679..53dc9d49 100644
--- a/src/com/cyanogenmod/filemanager/activities/preferences/SettingsPreferences.java
+++ b/src/com/cyanogenmod/filemanager/activities/preferences/SettingsPreferences.java
@@ -123,6 +123,7 @@ public class SettingsPreferences extends PreferenceActivity {
private CheckBoxPreference mCaseSensitiveSort;
private ListPreference mFreeDiskSpaceWarningLevel;
private CheckBoxPreference mComputeFolderStatistics;
+ private CheckBoxPreference mUseFlinger;
private ListPreference mAccessMode;
private CheckBoxPreference mDebugTraces;
@@ -235,6 +236,12 @@ public class SettingsPreferences extends PreferenceActivity {
FileManagerSettings.SETTINGS_COMPUTE_FOLDER_STATISTICS.getId());
this.mComputeFolderStatistics.setOnPreferenceChangeListener(this.mOnChangeListener);
+ // Use flinger
+ this.mUseFlinger =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_USE_FLINGER.getId());
+ this.mUseFlinger.setOnPreferenceChangeListener(this.mOnChangeListener);
+
// Access mode
this.mAccessMode =
(ListPreference)findPreference(
diff --git a/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java b/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java
index a3188734..ca4620bc 100644
--- a/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java
+++ b/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java
@@ -91,6 +91,11 @@ public enum FileManagerSettings {
*/
SETTINGS_COMPUTE_FOLDER_STATISTICS(
"cm_filemanager_compute_folder_statistics", Boolean.FALSE), //$NON-NLS-1$
+ /**
+ * Whether use flinger to remove items
+ * @hide
+ */
+ SETTINGS_USE_FLINGER("cm_filemanager_use_flinger", Boolean.TRUE), //$NON-NLS-1$
/**
* When to highlight the terms of the search in the search results
diff --git a/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java b/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java
index 7bc7ed08..0e193433 100644
--- a/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java
+++ b/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java
@@ -234,7 +234,8 @@ public class ActionsDialog implements OnItemClickListener, OnItemLongClickListen
this.mContext,
this.mFso,
this.mOnSelectionListener,
- this.mOnRequestRefreshListener);
+ this.mOnRequestRefreshListener,
+ null);
break;
//- Refresh
@@ -318,7 +319,8 @@ public class ActionsDialog implements OnItemClickListener, OnItemLongClickListen
this.mContext,
selection,
this.mOnSelectionListener,
- this.mOnRequestRefreshListener);
+ this.mOnRequestRefreshListener,
+ null);
}
break;
diff --git a/src/com/cyanogenmod/filemanager/ui/policy/DeleteActionPolicy.java b/src/com/cyanogenmod/filemanager/ui/policy/DeleteActionPolicy.java
index a7c38922..238e84f8 100644
--- a/src/com/cyanogenmod/filemanager/ui/policy/DeleteActionPolicy.java
+++ b/src/com/cyanogenmod/filemanager/ui/policy/DeleteActionPolicy.java
@@ -28,6 +28,7 @@ import com.cyanogenmod.filemanager.console.RelaunchableException;
import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
import com.cyanogenmod.filemanager.listeners.OnSelectionListener;
import com.cyanogenmod.filemanager.model.FileSystemObject;
+import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
import com.cyanogenmod.filemanager.util.CommandHelper;
import com.cyanogenmod.filemanager.util.DialogHelper;
import com.cyanogenmod.filemanager.util.ExceptionUtil;
@@ -52,15 +53,20 @@ public final class DeleteActionPolicy extends ActionsPolicy {
* @param fso The file system object to remove
* @param onSelectionListener The listener for obtain selection information (required)
* @param onRequestRefreshListener The listener for request a refresh (optional)
+ * @param onItemFlingerResponder The flinger responder, only if the action was initialized
+ * by a flinger gesture (optional)
*/
public static void removeFileSystemObject(
final Context ctx, final FileSystemObject fso,
final OnSelectionListener onSelectionListener,
- final OnRequestRefreshListener onRequestRefreshListener) {
+ final OnRequestRefreshListener onRequestRefreshListener,
+ final OnItemFlingerResponder onItemFlingerResponder) {
// Generate an array and invoke internal method
List<FileSystemObject> files = new ArrayList<FileSystemObject>(1);
files.add(fso);
- removeFileSystemObjects(ctx, files, onSelectionListener, onRequestRefreshListener);
+ removeFileSystemObjects(
+ ctx, files, onSelectionListener,
+ onRequestRefreshListener, onItemFlingerResponder);
}
/**
@@ -70,11 +76,14 @@ public final class DeleteActionPolicy extends ActionsPolicy {
* @param files The list of files to remove
* @param onSelectionListener The listener for obtain selection information (required)
* @param onRequestRefreshListener The listener for request a refresh (optional)
+ * @param onItemFlingerResponder The flinger responder, only if the action was initialized
+ * by a flinger gesture (optional)
*/
public static void removeFileSystemObjects(
final Context ctx, final List<FileSystemObject> files,
final OnSelectionListener onSelectionListener,
- final OnRequestRefreshListener onRequestRefreshListener) {
+ final OnRequestRefreshListener onRequestRefreshListener,
+ final OnItemFlingerResponder onItemFlingerResponder) {
// Ask the user before remove
AlertDialog dialog = DialogHelper.createYesNoDialog(
@@ -90,7 +99,13 @@ public final class DeleteActionPolicy extends ActionsPolicy {
ctx,
files,
onSelectionListener,
- onRequestRefreshListener);
+ onRequestRefreshListener,
+ onItemFlingerResponder);
+ } else {
+ // Flinger operation should be cancelled
+ if (onItemFlingerResponder != null) {
+ onItemFlingerResponder.cancel();
+ }
}
}
});
@@ -104,12 +119,15 @@ public final class DeleteActionPolicy extends ActionsPolicy {
* @param files The list of files to remove
* @param onSelectionListener The listener for obtain selection information (optional)
* @param onRequestRefreshListener The listener for request a refresh (optional)
+ * @param onItemFlingerResponder The flinger responder, only if the action was initialized
+ * by a flinger gesture (optional)
* @hide
*/
static void removeFileSystemObjectsInBackground(
final Context ctx, final List<FileSystemObject> files,
final OnSelectionListener onSelectionListener,
- final OnRequestRefreshListener onRequestRefreshListener) {
+ final OnRequestRefreshListener onRequestRefreshListener,
+ final OnItemFlingerResponder onItemFlingerResponder) {
// Some previous checks prior to execute
// 1.- Check the operation consistency (only if it is viable)
@@ -167,10 +185,21 @@ public final class DeleteActionPolicy extends ActionsPolicy {
@Override
public void onSuccess() {
- //Operation complete. Refresh
+ //Operation complete.
+
+ // Confirms flinger operation
+ if (onItemFlingerResponder != null) {
+ onItemFlingerResponder.accept();
+ }
+
+ // Refresh
if (this.mOnRequestRefreshListener != null) {
- // The reference is not the same, so refresh the complete navigation view
- this.mOnRequestRefreshListener.onRequestRefresh(null);
+ // The reference is not the same, so refresh the complete navigation view
+ if (files != null && files.size() == 1) {
+ this.mOnRequestRefreshListener.onRequestRemove(files.get(0));
+ } else {
+ this.mOnRequestRefreshListener.onRequestRemove(null);
+ }
}
ActionsPolicy.showOperationSuccessMsg(ctx);
}
@@ -250,11 +279,21 @@ public final class DeleteActionPolicy extends ActionsPolicy {
// Persist the exception?
if (this.mCause != null) {
+ // Cancels the flinger
+ if (onItemFlingerResponder != null) {
+ onItemFlingerResponder.cancel();
+ }
+
// The exception must be elevated
throw this.mCause;
}
} else {
+ // Cancels the flinger
+ if (onItemFlingerResponder != null) {
+ onItemFlingerResponder.cancel();
+ }
+
// The exception must be elevated
throw e;
}
@@ -275,6 +314,11 @@ public final class DeleteActionPolicy extends ActionsPolicy {
// Operation complete successfully
}
if (failed) {
+ // Cancels the flinger
+ if (onItemFlingerResponder != null) {
+ onItemFlingerResponder.cancel();
+ }
+
throw new ExecutionException(
String.format(
"Failed to delete file: %s", fso.getFullPath())); //$NON-NLS-1$
diff --git a/src/com/cyanogenmod/filemanager/ui/widgets/FlingerListView.java b/src/com/cyanogenmod/filemanager/ui/widgets/FlingerListView.java
new file mode 100644
index 00000000..1fd5846f
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/ui/widgets/FlingerListView.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cyanogenmod.filemanager.ui.widgets;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+/**
+ * A {@link ListView} implementation for remove items using flinging gesture.
+ */
+public class FlingerListView extends ListView {
+
+ /**
+ * An interface for dispatch flinging gestures
+ */
+ public interface OnItemFlingerListener {
+ /**
+ * Method invoke when a row item is going to be flinging.
+ *
+ * @param parent The AbsListView where the flinging happened
+ * @param view The view within the AbsListView that was flingered
+ * @param position The position of the view in the list
+ * @param id The row id of the item that was flingered
+ * @return boolean If the flinging operation must continue
+ */
+ boolean onItemFlingerStart(AdapterView<?> parent, View view, int position, long id);
+
+ /**
+ * Method invoke when a row item was flingered.
+ *
+ * @param responder The responder to the flinging action. You MUST be invoke one
+ * the option methods of this interface (accept or cancel).
+ * @param parent The AbsListView where the flinging happened
+ * @param view The view within the AbsListView that was flingered
+ * @param position The position of the view in the list
+ * @param id The row id of the item that was flingered
+ */
+ void onItemFlingerEnd(
+ OnItemFlingerResponder responder,
+ AdapterView<?> parent, View view, int position, long id);
+ }
+
+ /**
+ * An interface for response to {@link OnItemFlingerListener#onItemFlingerEnd(
+ * OnItemFlingerResponder, AdapterView, View, int, long)} event.
+ */
+ public interface OnItemFlingerResponder {
+ /**
+ * Method that indicates that the item was removed. This method MUST be called
+ * after the remove of item (that it's responsibility of the invoker) to ensure
+ * that all references are cleaned.
+ */
+ void accept();
+
+ /**
+ * Method that indicates that the action must be cancelled, and the item
+ * MUST NOT be removed.
+ */
+ void cancel();
+ }
+
+ /**
+ * An implementation of {@link OnItemFlingerResponder}
+ */
+ private class ItemFlingerResponder implements OnItemFlingerResponder {
+ /**
+ * @hide
+ */
+ View mItemView;
+
+ /**
+ * Constructor of <code></code>. For synthetic-access only.
+ */
+ public ItemFlingerResponder() {
+ super();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void accept() {
+ // Remove the flinger effect
+ this.mItemView.setTranslationX(0);
+ clearVars();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void cancel() {
+ // Remove the flinger effect
+ this.mItemView.setTranslationX(0);
+ clearVars();
+ }
+ }
+
+ /**
+ * The time after that the pressed is sending to the view.
+ */
+ private static final long PRESSED_DELAY_TIME = 250L;
+
+ /**
+ * The default percentage for flinging remove event.
+ */
+ private static final float DEFAULT_FLING_REMOVE_PERCENTAJE = 0.60f;
+
+ // Flinging data
+ private int mTranslationX = 0;
+ private int mStartX = 0;
+ private int mStartY = 0;
+ private int mCurrentX = 0;
+ private int mCurrentY = 0;
+ private int mFlingingViewPos;
+ private View mFlingingView;
+ private boolean mFlingingViewPressed;
+ private int mFlingingViewHeight;
+ private int mFlingingViewWidth;
+ private boolean mScrolling;
+ private boolean mFlinging;
+ private boolean mFlingingStarted;
+ private boolean mMoveStarted;
+ private boolean mLongPress;
+ private Runnable mLongPressDetection;
+
+ private float mFlingRemovePercentaje;
+ private OnItemFlingerListener mOnItemFlingerListener;
+
+ /**
+ * Constructor of <code>FlingerListView</code>.
+ *
+ * @param context The current context
+ */
+ public FlingerListView(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Constructor of <code>FlingerListView</code>.
+ *
+ * @param context The current context
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ */
+ public FlingerListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ /**
+ * Constructor of <code>FlingerListView</code>.
+ *
+ * @param context The current context
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyle The default style to apply to this view. If 0, no style
+ * will be applied (beyond what is included in the theme). This may
+ * either be an attribute resource, whose value will be retrieved
+ * from the current theme, or an explicit style resource.
+ */
+ public FlingerListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ /**
+ * Method that initializes the view
+ */
+ private void init() {
+ //Initialize variables
+ this.mFlingRemovePercentaje = DEFAULT_FLING_REMOVE_PERCENTAJE;
+ }
+
+ /**
+ * Method that returns the percentage (from 0 to 1) of the item view width on which
+ * an OnItemFlinger event occurs
+ *
+ * @return float The percentage (from 0 to 1) of the item view width
+ */
+ public float getFlingRemovePercentaje() {
+ return this.mFlingRemovePercentaje;
+ }
+
+ /**
+ * Method that sets the percentage (from 0 to 1) of the item view width on which
+ * an OnItemFlinger event occurs
+ *
+ * @param flingRemovePercentaje The percentage (from 0 to 1) of the item view width
+ */
+ public void setFlingRemovePercentaje(float flingRemovePercentaje) {
+ if (flingRemovePercentaje < 0) {
+ this.mFlingRemovePercentaje = 0;
+ } else if (flingRemovePercentaje > 1) {
+ this.mFlingRemovePercentaje = 1;
+ } else {
+ this.mFlingRemovePercentaje = flingRemovePercentaje;
+ }
+ }
+
+ /**
+ * Method that sets the listener for listen flinging events
+ *
+ * @param mOnItemFlingerListener The flinging listener
+ */
+ public void setOnItemFlingerListener(OnItemFlingerListener mOnItemFlingerListener) {
+ this.mOnItemFlingerListener = mOnItemFlingerListener;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // Get information about the x and y
+ int x = (int) ev.getX();
+ int y = (int) ev.getY();
+
+ // Detect the motion
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ // Clean variables
+ this.mScrolling = false;
+ this.mFlinging = false;
+ this.mLongPress = false;
+ this.mFlingingStarted = false;
+ this.mMoveStarted = false;
+ if (this.mFlingingView != null) {
+ this.mFlingingView.setTranslationX(0);
+ }
+
+ // Get the view to fling
+ this.mFlingingViewPos = pointToPosition(x, y);
+ if (this.mFlingingViewPos != INVALID_POSITION) {
+ this.mStartX = (int) ev.getX();
+ this.mCurrentX = (int) ev.getX();
+ this.mStartY = (int) ev.getY();
+ this.mCurrentY = (int) ev.getY();
+ this.mTranslationX = 0;
+ this.mFlingingView =
+ getChildAt(this.mFlingingViewPos - getFirstVisiblePosition());
+ this.mFlingingViewPressed = true;
+
+ // Detect long press event
+ if (getOnItemLongClickListener() != null) {
+ this.mLongPressDetection = new Runnable() {
+ @Override
+ @SuppressWarnings({"synthetic-access" })
+ public void run() {
+ if (!FlingerListView.this.mFlingingStarted &&
+ !FlingerListView.this.mMoveStarted) {
+ // Notify the long-click
+ FlingerListView.this.mLongPress = true;
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ FlingerListView.this.mFlingingView.setPressed(false);
+ getOnItemLongClickListener().onItemLongClick(
+ FlingerListView.this,
+ FlingerListView.this.mFlingingView,
+ FlingerListView.this.mFlingingViewPos,
+ FlingerListView.this.mFlingingView.getId());
+ }
+ }
+ };
+ this.mFlingingView.postDelayed(
+ this.mLongPressDetection,
+ ViewConfiguration.getLongPressTimeout());
+ }
+
+ // Calculate the item size
+ Rect r = new Rect();
+ this.mFlingingView.getDrawingRect(r);
+ this.mFlingingViewWidth = r.width();
+ this.mFlingingViewHeight = r.height();
+
+ // Set the pressed state
+ this.mFlingingView.postDelayed(new Runnable() {
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void run() {
+ if (FlingerListView.this.mFlingingViewPressed) {
+ FlingerListView.this.mFlingingView.setPressed(true);
+ }
+ }
+ }, PRESSED_DELAY_TIME);
+
+ return true;
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ this.mMoveStarted = true;
+ if (this.mFlingingView != null) {
+ this.mFlingingView.removeCallbacks(this.mLongPressDetection);
+ this.mFlingingViewPressed = false;
+ FlingerListView.this.mFlingingView.setPressed(false);
+ }
+
+ // With flinging support
+ if (this.mFlingingView != null && this.mOnItemFlingerListener != null) {
+ // Detect scrolling
+ this.mCurrentY = (int)ev.getY();
+ this.mScrolling =
+ Math.abs(this.mCurrentY - this.mStartY) > this.mFlingingViewHeight;
+
+
+ // Only if event has changed (and only to the right and if not scrolling)
+ if (!this.mScrolling) {
+ if (ev.getX() >= this.mStartX && (ev.getX() - this.mCurrentX != 0)) {
+ // Started
+ if (!this.mFlingingStarted) {
+ // Flinging starting
+ if (!this.mOnItemFlingerListener.onItemFlingerStart(
+ this,
+ this.mFlingingView,
+ this.mFlingingViewPos,
+ this.mFlingingView.getId())) {
+ break;
+ }
+ this.mFlingingStarted = true;
+ }
+
+
+ this.mCurrentX = (int)ev.getX();
+ this.mTranslationX = this.mCurrentX - this.mStartX;
+ this.mFlingingView.setTranslationX(this.mTranslationX);
+ this.mFlingingView.setPressed(false);
+
+ // Detect if flinging occurs
+ float flingLimit =
+ (this.mFlingingViewWidth * this.mFlingRemovePercentaje);
+ if (!this.mFlinging && this.mTranslationX > flingLimit) {
+ // Flinging occurs. Mark and raise an event
+ this.mFlinging = true;
+ final ItemFlingerResponder responder =
+ new ItemFlingerResponder();
+ responder.mItemView = this.mFlingingView;
+
+ // Request a response (we need to do this in background for
+ // get new events)
+ this.mFlingingView.post(new Runnable() {
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void run() {
+ FlingerListView.
+ this.mOnItemFlingerListener.onItemFlingerEnd(
+ responder,
+ FlingerListView.this,
+ FlingerListView.this.mFlingingView,
+ FlingerListView.this.mFlingingViewPos,
+ FlingerListView.this.mFlingingView.getId());
+ }
+ });
+ }
+ }
+ }
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ // Clear flinging (only if not waiting confirmation)
+ // On scrolling, flinging has no effect
+ if (!this.mFlinging || this.mScrolling) {
+ this.mStartX = 0;
+ this.mCurrentX = 0;
+ this.mTranslationX = 0;
+ if (this.mFlingingView != null) {
+ this.mFlingingView.setTranslationX(0);
+ }
+ } else {
+ // Force to display at the limit
+ if (this.mFlingingView != null) {
+ float flingLimit =
+ (this.mFlingingViewWidth * this.mFlingRemovePercentaje);
+ this.mFlingingView.setTranslationX(flingLimit);
+ }
+ }
+
+ // What is the motion
+ if (!this.mScrolling && this.mFlingingView != null &&
+ this.mOnItemFlingerListener != null) {
+ if (!this.mMoveStarted) {
+ if (!this.mLongPress) {
+ this.mFlingingView.removeCallbacks(this.mLongPressDetection);
+ this.mFlingingView.setPressed(true);
+ this.mFlingingView.postDelayed(new Runnable() {
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void run() {
+ FlingerListView.this.mFlingingView.setPressed(false);
+ }
+ }, PRESSED_DELAY_TIME);
+ performItemClick(
+ this.mFlingingView,
+ this.mFlingingViewPos,
+ this.mFlingingView.getId());
+ }
+ }
+
+ // Handled
+ return true;
+ }
+
+ // Scrolling -> Remove any status (don't handle event)
+ if (this.mFlingingView != null) {
+ this.mFlingingView.setPressed(false);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return super.onTouchEvent(ev);
+ }
+
+ /**
+ * Method that clean the internal variables
+ * @hide
+ */
+ void clearVars() {
+ this.mScrolling = false;
+ this.mFlinging = false;
+ this.mLongPress = false;
+ this.mFlingingStarted = false;
+ this.mMoveStarted = false;
+ this.mFlingingView = null;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java b/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java
index dccbd0f7..2533944f 100644
--- a/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java
+++ b/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java
@@ -49,7 +49,10 @@ import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
import com.cyanogenmod.filemanager.preferences.NavigationLayoutMode;
import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
import com.cyanogenmod.filemanager.preferences.Preferences;
+import com.cyanogenmod.filemanager.ui.policy.DeleteActionPolicy;
import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
+import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerListener;
+import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
import com.cyanogenmod.filemanager.util.CommandHelper;
import com.cyanogenmod.filemanager.util.DialogHelper;
import com.cyanogenmod.filemanager.util.ExceptionUtil;
@@ -69,6 +72,8 @@ public class NavigationView extends RelativeLayout implements
AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener,
BreadcrumbListener, OnSelectionChangedListener, OnSelectionListener, OnRequestRefreshListener {
+ private static final String TAG = "NavigationView"; //$NON-NLS-1$
+
/**
* An interface to communicate selection changes events.
*/
@@ -124,7 +129,57 @@ public class NavigationView extends RelativeLayout implements
PICKABLE,
}
- private static final String TAG = "NavigationView"; //$NON-NLS-1$
+ /**
+ * A listener for flinging events from {@link FlingerListView}
+ */
+ private final OnItemFlingerListener mOnItemFlingerListener = new OnItemFlingerListener() {
+
+ @Override
+ public boolean onItemFlingerStart(
+ AdapterView<?> parent, View view, int position, long id) {
+ try {
+ // Response if the item can be removed
+ FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
+ FileSystemObject fso = adapter.getItem(position);
+ if (fso != null) {
+ if (fso instanceof ParentDirectory) {
+ return false;
+ }
+ return true;
+ }
+ } catch (Exception e) {
+ ExceptionUtil.translateException(getContext(), e, true, false);
+ }
+ return false;
+ }
+
+ @Override
+ public void onItemFlingerEnd(OnItemFlingerResponder responder,
+ AdapterView<?> parent, View view, int position, long id) {
+
+ try {
+ // Response if the item can be removed
+ FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
+ FileSystemObject fso = adapter.getItem(position);
+ if (fso != null) {
+ DeleteActionPolicy.removeFileSystemObject(
+ getContext(),
+ fso,
+ NavigationView.this,
+ NavigationView.this,
+ responder);
+ return;
+ }
+
+ // Cancels the flinger operation
+ responder.cancel();
+
+ } catch (Exception e) {
+ ExceptionUtil.translateException(getContext(), e, true, false);
+ responder.cancel();
+ }
+ }
+ };
private int mId;
private String mCurrentDir;
@@ -413,6 +468,29 @@ public class NavigationView extends RelativeLayout implements
}
/**
+ * Method that sets if the view should use flinger gesture detection.
+ *
+ * @param useFlinger If the view should use flinger gesture detection
+ */
+ public void setUseFlinger(boolean useFlinger) {
+ if (this.mCurrentMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
+ // Not supported
+ return;
+ }
+ // Set the flinger listener (only when navigate)
+ if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
+ if (this.mAdapterView instanceof FlingerListView) {
+ if (useFlinger) {
+ ((FlingerListView)this.mAdapterView).
+ setOnItemFlingerListener(this.mOnItemFlingerListener);
+ } else {
+ ((FlingerListView)this.mAdapterView).setOnItemFlingerListener(null);
+ }
+ }
+ }
+ }
+
+ /**
* Method that forces the view to scroll to the file system object passed.
*
* @param fso The file system object
@@ -425,6 +503,8 @@ public class NavigationView extends RelativeLayout implements
} catch (Exception e) {
this.mAdapterView.setSelection(0);
}
+ } else {
+ this.mAdapterView.setSelection(0);
}
}
@@ -471,6 +551,14 @@ public class NavigationView extends RelativeLayout implements
return;
}
+ // If we should set the listview to response to flinger gesture detection
+ boolean useFlinger =
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
+ ((Boolean)FileManagerSettings.
+ SETTINGS_USE_FLINGER.
+ getDefaultValue()).booleanValue());
+
//Creates the new layout
AdapterView<ListAdapter> newView = null;
int itemResourceId = -1;
@@ -478,14 +566,32 @@ public class NavigationView extends RelativeLayout implements
newView = (AdapterView<ListAdapter>)inflate(
getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
itemResourceId = RESOURCE_MODE_ICONS_ITEM;
+
} else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
newView = (AdapterView<ListAdapter>)inflate(
getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
+
+ // Set the flinger listener (only when navigate)
+ if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
+ if (useFlinger && newView instanceof FlingerListView) {
+ ((FlingerListView)newView).
+ setOnItemFlingerListener(this.mOnItemFlingerListener);
+ }
+ }
+
} else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
newView = (AdapterView<ListAdapter>)inflate(
getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
+
+ // Set the flinger listener (only when navigate)
+ if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
+ if (useFlinger && newView instanceof FlingerListView) {
+ ((FlingerListView)newView).
+ setOnItemFlingerListener(this.mOnItemFlingerListener);
+ }
+ }
}
//Get the current adapter and its adapter list
@@ -917,6 +1023,8 @@ public class NavigationView extends RelativeLayout implements
public void onRequestRefresh(Object o) {
if (o instanceof FileSystemObject) {
refresh((FileSystemObject)o);
+ } else if (o == null) {
+ refresh();
}
onDeselectAll();
}
@@ -926,8 +1034,10 @@ public class NavigationView extends RelativeLayout implements
*/
@Override
public void onRequestRemove(Object o) {
- if (o instanceof FileSystemObject) {
+ if (o != null && o instanceof FileSystemObject) {
removeItem((FileSystemObject)o);
+ } else {
+ onRequestRefresh(null);
}
onDeselectAll();
}