package com.holo.fileexplorer; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Set; import java.util.Stack; import java.util.concurrent.TimeoutException; import org.apache.commons.io.FileUtils; import com.beacon.crawlers.Mover; import com.beacon.crawlers.Trasher; import com.holo.actions.FileActionSupport; import com.holo.actions.FileActions; import com.holo.fileexplorer.R; import com.holo.root.ExtendedMount; import com.stericson.RootTools.RootTools; import com.stericson.RootTools.RootToolsException; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.ActionBar.OnNavigationListener; import com.actionbarsherlock.app.SherlockListFragment; import com.actionbarsherlock.widget.ShareActionProvider; import android.text.TextUtils; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; import com.actionbarsherlock.view.ActionMode; import android.view.LayoutInflater; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.SpinnerAdapter; import android.widget.Toast; public class FileListFragment extends SherlockListFragment implements OnNavigationListener, OnItemLongClickListener, FilesAdapter.Callback { // Sort Constants public final static int SORT_ALPHABETICAL = 1; public final static int SORT_LAST_MODIFIED = 2; public final static int SORT_ASC = 100; public final static int SORT_DESC = 101; private int currentSortType = SORT_ALPHABETICAL; private boolean sortReversed = false; final Comparator<FileMeta> LAST_MODIFIED_COMPARATOR = new Comparator<FileMeta>() { public int compare(FileMeta f1, FileMeta f2) { int comparatorResult; if (!sortReversed) { // For when the 'sort reversed' option is unchecked. Last // Modified scheme should go Newest-Oldest by default comparatorResult = f2.mModifiedDate.compareTo(f1.mModifiedDate); } else { comparatorResult = f1.mModifiedDate.compareTo(f2.mModifiedDate); } return comparatorResult; } }; final Comparator<FileMeta> ALPHABETICAL_COMPARATOR = new Comparator<FileMeta>() { public int compare(FileMeta f1, FileMeta f2) { int comparatorResult; if (!sortReversed) { // For when the 'sort reversed' option is unchecked. // Alphabetical scheme should go A-Z by default comparatorResult = f1.mLowerCaseName .compareTo(f2.mLowerCaseName); } else { comparatorResult = f2.mLowerCaseName .compareTo(f1.mLowerCaseName); } return comparatorResult; } }; // UI Support private MainActivity mActivity; private SharedPreferences mPrefs; private Callback mCallback = EmptyCallback.INSTANCE; private boolean mIsViewCreated; // List variables private int mNum; private String mPath; private List<FileMeta> mFileItems; private List<FileMeta> dir; private List<FileMeta> fls; private ArrayList<String> mNavItems; private ArrayList<String> mNavTitles; private ArrayList<String> mNavPaths; private Stack<String> mHistory = new Stack<String>(); private Stack<Integer> mHistoryScroll = new Stack<Integer>(); private Stack<Integer> mHistoryScrollTop = new Stack<Integer>(); // Navigation dropdown variables private int mNavSelected; private boolean mPauseNavSelect = false; // Misc Variables final String root = "/"; private FilesAdapter mListAdapter; private boolean isSystemDir = false; /** * If true, we disable the CAB even if there are selected messages. It's * used in portrait on the tablet when the message view becomes visible and * the message list gets pushed out of the screen, in which case we want to * keep the selection but the CAB should be gone. */ private boolean mDisableCab; /** * {@link ActionMode} shown when 1 or more message is selected. */ private ActionMode mSelectionMode; private SelectionModeCallback mLastSelectionModeCallback; /** * Callback interface that owning activities must implement (future tablet * stuff pulled from the ICS email app source) */ public interface Callback { public static final int TYPE_REGULAR = 0; public static final int TYPE_DRAFT = 1; public static final int TYPE_TRASH = 2; /** * Called when an operation is initiated that can potentially advance * the current message selection (e.g. a delete operation may advance * the selection). * * @param affectedMessages * the messages the operation will apply to */ public void onAdvancingOpAccepted(Set<Long> affectedFiles); /** * Called when a drag & drop is initiated. * * @return true if drag & drop is allowed */ public boolean onDragStarted(); /** * Called when a drag & drop is ended. */ public void onDragEnded(); } /** * This class's actual implementation of the Callback Interface. (future * tablet stuff pulled from the ICS email app source) */ private static final class EmptyCallback implements Callback { public static final Callback INSTANCE = new EmptyCallback(); @Override public void onAdvancingOpAccepted(Set<Long> affectedMessages) { } @Override public boolean onDragStarted() { return false; // We don't know -- err on the safe side. } @Override public void onDragEnded() { } } /** * Create a new instance of FileListFragment, providing "num" as an * argument. */ static FileListFragment newInstance(int num) { FileListFragment f = new FileListFragment(); // Supply num input as an argument. Bundle args = new Bundle(); args.putInt("num", num); f.setArguments(args); return f; } /** * When creating, retrieve this instance's number from its arguments. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mNum = getArguments() != null ? getArguments().getInt("num") : 1; mActivity = (MainActivity) getActivity(); mPrefs = mActivity.getSharedPreferences("preference_headers", Context.MODE_WORLD_READABLE); mFileItems = new ArrayList<FileMeta>(); // TODO mnum not set Log.i("FragmentList", "Fragment #" + mNum + ": onCreate."); } /** * The Fragment's UI is just a list fragment */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_pager_list, container, false); mIsViewCreated = true; mPath = mPrefs.getString("home_folder", "/sdcard"); return v; } /** * @return true if the content view is created and not destroyed yet. (i.e. * between {@link #onCreateView} and {@link #onDestroyView}. */ private boolean isViewCreated() { // Note that we don't use "getView() != null". This method is used in // updateSelectionMode() // to determine if CAB shold be shown. But because it's called from // onDestroyView(), at // this point the fragment still has views but we want to hide CAB, we // can't use getView() here. return mIsViewCreated; } @Override public void onActivityCreated(Bundle savedInstanceState) { // ^ Called when the activity's onCreate() method has returned. super.onActivityCreated(savedInstanceState); // getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); final ListView lv = getListView(); lv.setOnItemLongClickListener(this); lv.setItemsCanFocus(false); lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE); Log.i("FragmentList", "Fragment #" + mNum + ": onActivityCreated. mPath set to " + mPath); fill(mPath); if (mActivity.firstRun) updateNav(); mActivity.setFirstRun(false); // Restore path // if (savedInstanceState != null) { // // Restore last state for checked position. // mPath = savedInstanceState.getString("path"); // mNavItems = savedInstanceState.getStringArrayList("navitems"); // mNavSelected = savedInstanceState.getInt("navselected"); // } else { // mPath = root; // } if (savedInstanceState != null) { // Fragment doesn't have this method. Call it manually. restoreInstanceState(savedInstanceState); } } public void onResume() { super.onResume(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("path", mPath); outState.putInt("navselected", mNavSelected); outState.putStringArrayList("navitems", mNavItems); Log.i("FragmentList", "Fragment #" + mNum + " instance state path = " + mPath); } void restoreInstanceState(Bundle savedInstanceState) { mPath = savedInstanceState.getString("path"); mNavSelected = savedInstanceState.getInt("navselected"); mNavItems = savedInstanceState.getStringArrayList("navitems"); } @Override public void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); FileMeta f = mListAdapter.getItem(position); if (f.isDirectory) { // Cancel action mode unless user has cut or copied a selection and // is navigating up to paste it. if (!FileActions.canPaste()) { onDeselectAll(); } pushScrollHistory(); // Default current sort to alphabetical for newly opened folders currentSortType = SORT_ALPHABETICAL; fill(f.mPath); // Reset scroll position to the top for newly opened folders this.getListView().setSelectionFromTop(0, 0); // Make sure that the navigation drop-down doesn't fire on its first // new item after it gets repopulated mPauseNavSelect = true; updateNav(); } else { onFileClick(f); } } private void pushScrollHistory() { // Remember current location in file structure mHistory.push(mPath); // Remember top listview item position mHistoryScroll.push(getListView().getFirstVisiblePosition()); // Grab precise scroll position on top-most listview item View topView = getListView().getChildAt(0); int top = (topView == null) ? 0 : topView.getTop(); mHistoryScrollTop.push(top); } private void onFileClick(FileMeta fm) { File file = new File(fm.mPath); FileActions.openFile(mActivity, file); // Intent fileIntent = new Intent(mActivity, FileView.class); // fileIntent.putExtra("absPath", fm.getPath()); // startActivity(fileIntent); // Toast.makeText(mActivity, "File Clicked: " + fm.getName(), // Toast.LENGTH_SHORT).show(); } private void changeDir(String destination) { fill(destination); updateNav(); } private void fill(String f) { mPath = f; // for (ExtendedMount mount : mounts) { // // } if (mActivity.rootEnabled && !FileActionSupport.isReadable(mPath)) { if (mPath != "/") mPath += "/"; List<String> output = null; String[] commands = new String[] {"ls -la " + mPath}; try { output = RootTools.sendShell(commands, 0, 4000); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (RootToolsException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace(); } // Process string output and populate FileMeta objects dir = new ArrayList<FileMeta>(); fls = new ArrayList<FileMeta>(); Iterator<String> iterator = output.iterator(); while (iterator.hasNext()) { String[] fields = iterator.next().split("\\s+"); // Don't try to handle last line of output. Will be something // like "sh-4.1$" if (fields.length == 1) break; // test to see if file size is present, indicats file/folder // status if (!FileActionSupport.isNumeric(fields[3])) { dir.add(new FileMeta(mActivity, fields[0], // permissions fields[1], // ?? fields[2], // ?? null, // number of children (not implemented) fields[3], // date modifed fields[4], // time modified fields[5], // filename mPath // parent path )); } else { fls.add(new FileMeta(mActivity, fields[0], // permissions fields[1], // ?? fields[2], // ?? fields[3], // file size in bytes fields[4], // date modifed fields[5], // time modified fields[6], // filename mPath // parent path )); } } } else { File currentDir = new File(f); File[] dirs = currentDir.listFiles(); dir = new ArrayList<FileMeta>(); fls = new ArrayList<FileMeta>(); try { for (File ff : dirs) { if (ff.isDirectory()) dir.add(new FileMeta(mActivity, ff)); else { fls.add(new FileMeta(mActivity, ff)); } } } catch (Exception e) { } } sort(); } public void sort() { sort(currentSortType); } public void sort(int sortCode) { switch (sortCode) { case SORT_ALPHABETICAL: Collections.sort(dir, ALPHABETICAL_COMPARATOR); Collections.sort(fls, ALPHABETICAL_COMPARATOR); currentSortType = sortCode; break; case SORT_LAST_MODIFIED: Collections.sort(dir, LAST_MODIFIED_COMPARATOR); Collections.sort(fls, LAST_MODIFIED_COMPARATOR); currentSortType = sortCode; break; default: // Default to the generic alphabetical Collections.sort(dir, ALPHABETICAL_COMPARATOR); Collections.sort(fls, ALPHABETICAL_COMPARATOR); currentSortType = sortCode; break; } // Collections.sort(dir, currentComparator); // Collections.sort(fls, currentComparator); // if (!currentDir.getPath().equalsIgnoreCase("/")) // dir.add(0, new FileMeta(mActivity, "..", "Parent Directory", // currentDir.getParent())); mFileItems.clear(); mFileItems.addAll(dir); mFileItems.addAll(fls); if (mListAdapter == null) { mListAdapter = new FilesAdapter(mActivity, this, R.layout.file_list_item_normal, mFileItems); this.setListAdapter(mListAdapter); } else { mListAdapter.notifyDataSetChanged(); } } public void sortDirection(boolean newState) { sortReversed = newState; sort(); } public File getCurrentDir() { return new File(mPath); } public void refresh() { // TODO preserve scroll state Set<String> selectedConversations = mListAdapter.getSelectedSet(); fill(mPath); mListAdapter.setSelectedSet(selectedConversations); } // In addition to triggering the nav dropdown update from takeOver(), this // method updates the submenus for the sort action to reflect the currently // selected sort mode for this fragment public void takeOver(Menu mMainOptionsMenu) { int id = 0; switch (currentSortType) { case SORT_ALPHABETICAL: id = R.id.sort_alphabetical; break; case SORT_LAST_MODIFIED: id = R.id.sort_last_modified; break; default: id = R.id.sort_alphabetical; } // Take control of sort menu item MenuItem item = (MenuItem) mMainOptionsMenu.findItem(id); item.setChecked(true); MenuItem sortReverseMenuItem = (MenuItem) mMainOptionsMenu .findItem(R.id.sort_reverse); sortReverseMenuItem.setChecked(sortReversed); takeOver(); } // Used when called from the ViewPagerAdapter on initialization, and by the // overloaded takeOver method here public void takeOver() { mPauseNavSelect = true; Log.i("FragmentList", "Fragment #" + mNum + ":takeOver"); updateNav(); } private void updateNav() { Log.i("FragmentList", "Fragment #" + mNum + ":updateNav"); ActionBar bar = mActivity.getSupportActionBar(); bar.setDisplayShowTitleEnabled(false); bar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); String middle = ""; mNavTitles = new ArrayList<String>(); mNavPaths = new ArrayList<String>(); File f = new File(mPath); String split = f.getPath(); String[] tokens = split.split("/"); if (tokens.length > 0) { for (int x = 0; x < tokens.length; x++) { mNavTitles.add(tokens[x] + "/"); mNavPaths.add(middle + tokens[x] + "/"); middle = middle + tokens[x] + "/"; } } else { mNavTitles.add("/ "); mNavPaths.add("/"); } mNavSelected = mNavTitles.size() - 1; // set last item as selected // (bears // improvement) // Context testContext = bar.getThemedContext(); SpinnerAdapter mSpinnerAdapter = new ArrayAdapter<String>(mActivity, R.layout.nav_spinner_item, mNavTitles); bar.setListNavigationCallbacks(mSpinnerAdapter, this); bar.setSelectedNavigationItem(mNavSelected); Log.i("FragmentList", "Fragment #" + mNum + ":Nav item just clicked? ^"); } @Override public boolean onNavigationItemSelected(int itemPosition, long itemId) { if (mPauseNavSelect) { Log.i("FragmentList", "Fragment #" + mNum + ":Nav Item click intercepted"); mPauseNavSelect = false; return false; } else { Log.i("FragmentList", "Fragment #" + mNum + ":Nav Item clicked: " + itemId); pushScrollHistory(); fill(mNavPaths.get(itemPosition)); return true; } } public void setCallback(Callback callback) { mCallback = (callback != null) ? callback : EmptyCallback.INSTANCE; } /** * Show/hide the "selection" action mode, according to the number of * selected messages and the visibility of the fragment. Also update the * content (title and menus) if necessary. */ public void updateSelectionMode() { final int numSelected = getSelectedCount(); if ((numSelected == 0) || mDisableCab || !isViewCreated()) { finishSelectionMode(); return; } if (isInSelectionMode()) { updateSelectionModeView(); } else { mLastSelectionModeCallback = new SelectionModeCallback(); mActivity.startActionMode(mLastSelectionModeCallback); } } /** * Finish the "selection" action mode. * * Note this method finishes the contextual mode, but does *not* clear the * selection. If you want to do so use {@link #onDeselectAll()} instead. */ private void finishSelectionMode() { if (isInSelectionMode()) { mLastSelectionModeCallback.mClosedByUser = false; mSelectionMode.finish(); } } /** Update the "selection" action mode bar */ private void updateSelectionModeView() { mSelectionMode.invalidate(); } /** * @return the number of messages that are currently selected. */ private int getSelectedCount() { return mListAdapter.getSelectedSet().size(); } /** * @return true if the list is in the "selection" mode. */ public boolean isInSelectionMode() { return mSelectionMode != null; } public void onDeselectAll() { mListAdapter.clearSelection(); if (isInSelectionMode()) { finishSelectionMode(); } } /** Implements {@link MessagesAdapter.Callback} */ @Override public void onAdapterFavoriteChanged(FileListItem itemView, boolean newFavorite) { // mController.setMessageFavorite(itemView.mMessageId, newFavorite); } /** Implements {@link MessagesAdapter.Callback} */ @Override public void onAdapterSelectedChanged(FileListItem itemView, boolean newSelected, int mSelectedCount) { updateSelectionMode(); } private class SelectionModeCallback implements ActionMode.Callback { private MenuItem mCut; private MenuItem mCopy; private MenuItem mPaste; private MenuItem mArchive; private MenuItem mDelete; private MenuItem mShare; private MenuItem mRename; private MenuItem mDetails; private int viewPageNum; private ShareActionProvider mShareActionProvider; private boolean pasteReady = false; /* package */boolean mClosedByUser = true; @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { mSelectionMode = mode; MenuInflater inflater = mActivity.getSupportMenuInflater(); inflater.inflate(R.menu.message_list_cab, menu); mCut = menu.findItem(R.id.action_cut); mCopy = menu.findItem(R.id.action_copy); mPaste = menu.findItem(R.id.action_paste); mArchive = menu.findItem(R.id.action_zip); mDelete = menu.findItem(R.id.action_confirm_delete); mShare = menu.findItem(R.id.action_share); mRename = menu.findItem(R.id.action_rename); mDetails = menu.findItem(R.id.action_details); // Set file with share history to the provider and set the share // intent. mShareActionProvider = (ShareActionProvider) mShare .getActionProvider(); mShareActionProvider .setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME); return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { int num = getSelectedCount(); // Set title -- "# selected" mSelectionMode.setTitle(mActivity.getResources().getQuantityString( R.plurals.message_view_selected_message_count, num, num)); // Show appropriate menu items. if (!FileActions.canPaste()) { mCut.setVisible(true); mCopy.setVisible(true); mPaste.setVisible(false); mArchive.setVisible(true); mShare.setVisible(true); } else { mCut.setVisible(true); mCopy.setVisible(true); mPaste.setVisible(true); mArchive.setVisible(true); mShare.setVisible(true); } if (num == 1) { mRename.setVisible(true); mDetails.setVisible(true); } else { mRename.setVisible(false); mDetails.setVisible(false); } viewPageNum = mNum; return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { Set<String> selectedConversations = mListAdapter.getSelectedSet(); Object[] selectedFilePaths = mListAdapter.getSelectedSet() .toArray(); /* * Paste gets special handling. All other Contextual Action Mode * actions go in a switch statement below. */ // Paste clicked and files are available for pasting if (FileActions.canPaste() && item.getItemId() == R.id.action_paste) { new Mover(mActivity, FileActions.getPasteMode()) .execute(mActivity.getCurrentDir()); /* * !!! TODO: Mover.java refreshes the directory after finishing, * which keeps the following from running * * Test to see if there is any current selection: If there * aren't any selections, then the user's selection of paste can * be presumed to be the final action for the Action Mode. * Otherwise the action mode should be left open and * invalidated/reloaded to remove the paste action. */ if (selectedFilePaths.length < 1) { onDeselectAll(); // Close Action Mode } else { mSelectionMode.invalidate(); // Update Action Mode } // Paste clicked but no files available. Should never happen. } else if (!FileActions.canPaste() && item.getItemId() == R.id.action_paste) { Toast.makeText(mActivity, "No files stored in clipboard", Toast.LENGTH_SHORT).show(); // No items selected and we've already determined that there is // no pending paste to keep the action mode open. Should never // happen. } else if (selectedFilePaths.length < 1) { Toast.makeText( mActivity, "Why is this action mode still open? Because it shouldn't be.", Toast.LENGTH_SHORT).show(); // By process of elimination, we have established that paste has // not been clicked and that there is a selection to be operated // on. Get to it! } else { // Convert selected items into a list of files ArrayList<File> selectedFiles = new ArrayList<File>(); for (Object s : selectedFilePaths) { selectedFiles.add(new File((String) s)); } // Trigger appropriate CAB item code switch (item.getItemId()) { case R.id.action_share: mActivity.setPage(viewPageNum); if (selectedFiles.size() > 0) { Intent shareIntent = FileActions.createShareIntent( selectedFiles, mActivity); startActivity(Intent.createChooser(shareIntent, "Share via")); // Commented out pending solution for // shareactionprovider/splitactionbar bug // mShareActionProvider.setShareIntent(FileActions // .createShareIntent(selectedFiles, mActivity)); // mSelectionMode.invalidate(); } else { Toast.makeText(mActivity, "Error. No items selected.", Toast.LENGTH_SHORT).show(); } onDeselectAll(); break; case R.id.action_rename: mActivity.setPage(viewPageNum); Toast.makeText(mActivity, "Rename isn't implemented yet", Toast.LENGTH_SHORT).show(); break; case R.id.action_details: mActivity.setPage(viewPageNum); Intent fileIntent = new Intent(mActivity, FileView.class); fileIntent.putExtra("absPath", selectedFiles.get(0) .getPath()); startActivity(fileIntent); break; case R.id.action_zip: mActivity.setPage(viewPageNum); mActivity.showDialog(mActivity.DIALOG_ZIP); // Problem here. Zipfiles needs to run after dialog returns. // FileActions.zipFiles(selectedFiles, getCurrentDir(), // mActivity.getNewZipName(), mActivity); break; case R.id.action_cut: mActivity.setPage(viewPageNum); if (selectedFiles.size() > 0) { FileActions.cutFiles(selectedFiles, mActivity); mSelectionMode.invalidate(); // After cutting, the menu // should be rebuilt to // include paste } else { Toast.makeText(mActivity, "Error. No items selected.", Toast.LENGTH_SHORT).show(); } break; case R.id.action_copy: mActivity.setPage(viewPageNum); if (selectedFiles.size() > 0) { FileActions.copyFiles(selectedFiles, mActivity); mSelectionMode.invalidate(); // After copying, the menu // should be rebuilt to // include paste } else { Toast.makeText(mActivity, "Error. No items selected.", Toast.LENGTH_SHORT).show(); } break; case R.id.action_confirm_delete: // new // AlertDialog.Builder(mActivity).setIcon(android.R.drawable.ic_dialog_alert).setMessage("Quit the application?") // .setPositiveButton("Yes", new // DialogInterface.OnClickListener() { // // @Override // public void onClick(DialogInterface dialog, int which) { // mActivity.setPage(viewPageNum); // new Trasher(mActivity).execute(selectedFiles); // // Toast.makeText(mActivity, // // "Deleted " + getSelectedCount() + " items", // // Toast.LENGTH_SHORT).show(); // onDeselectAll(); // } // // }).setNegativeButton("No", null).show(); mActivity.setPage(viewPageNum); new Trasher(mActivity).execute(selectedFiles); // Toast.makeText(mActivity, // "Deleted " + getSelectedCount() + " items", // Toast.LENGTH_SHORT).show(); onDeselectAll(); break; default: break; } } return true; } @Override public void onDestroyActionMode(ActionMode mode) { // Clear this before onDeselectAll() to prevent onDeselectAll() from // trying to close the // contextual mode again. mSelectionMode = null; if (mClosedByUser) { // Clear selection, only when the contextual mode is explicitly // closed by the user. // // We close the contextual mode when the fragment becomes // temporary invisible // (i.e. mIsVisible == false) too, in which case we want to keep // the selection. onDeselectAll(); } } } @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { // FileListItem f = mListAdapter.getItem(position); boolean toggled = false; if (!mListAdapter.isSelected((FileListItem) view)) { toggleSelection((FileListItem) view); toggled = true; updateSelectionMode(); } return true; } private void toggleSelection(FileListItem itemView) { itemView.invalidate(); mListAdapter.toggleSelected(itemView); } public boolean atRoot() { return mPath.equals(root); } public void upOneLevel() { if (mPath != root) changeDir(new File(mPath).getParent()); } public boolean backOneLevel() { if (mHistory.empty()) { return false; } else { mPauseNavSelect = true; changeDir(mHistory.pop()); this.getListView().setSelectionFromTop(mHistoryScroll.pop(), mHistoryScrollTop.pop()); return true; } } public void createNewFolder(String foldername) { if (!TextUtils.isEmpty(foldername)) { File file = FileUtils.getFile(mPath, foldername); if (file.mkdirs()) { // Change into new directory: changeDir(file.getAbsolutePath()); } else { Toast.makeText(mActivity, R.string.error_creating_new_folder, Toast.LENGTH_SHORT).show(); } } } public void zipInit(String newZipName) { // Convert selected items into a list of files Object[] selectedFilePaths = mListAdapter.getSelectedSet().toArray(); ArrayList<File> selectedFiles = new ArrayList<File>(); for (Object s : selectedFilePaths) { selectedFiles.add(new File((String) s)); } FileActions.zipFiles(selectedFiles, getCurrentDir(), newZipName, mActivity); onDeselectAll(); refresh(); } }