2 * Copyright (C) 2008 OpenIntents.org
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.
18 * Based on AndDev.org's file browser V 2.0.
21 package org.openintents.filemanager;
24 import java.io.FileInputStream;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.LinkedList;
29 import java.util.List;
31 import org.openintents.filemanager.DistributionLibraryListActivity;
32 import org.openintents.filemanager.util.CompressManager;
33 import org.openintents.filemanager.util.ExtractManager;
34 import org.openintents.filemanager.util.FileUtils;
35 import org.openintents.filemanager.util.MimeTypeParser;
36 import org.openintents.filemanager.util.MimeTypes;
37 import org.openintents.intents.FileManagerIntents;
38 import org.openintents.util.MenuIntentOptionsWithIcons;
39 import org.xmlpull.v1.XmlPullParserException;
41 import android.app.AlertDialog;
42 import android.app.Dialog;
43 import android.content.ActivityNotFoundException;
44 import android.content.ComponentName;
45 import android.content.ContentValues;
46 import android.content.Context;
47 import android.content.DialogInterface;
48 import android.content.DialogInterface.OnCancelListener;
49 import android.content.DialogInterface.OnClickListener;
50 import android.content.Intent;
51 import android.content.SharedPreferences;
52 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
53 import android.content.pm.ActivityInfo;
54 import android.content.pm.PackageManager;
55 import android.content.pm.PackageManager.NameNotFoundException;
56 import android.content.pm.ResolveInfo;
57 import android.content.res.XmlResourceParser;
58 import android.database.Cursor;
59 import android.graphics.drawable.Drawable;
60 import android.net.Uri;
61 import android.os.AsyncTask;
62 import android.os.Bundle;
63 import android.os.Handler;
64 import android.os.IBinder;
65 import android.os.Message;
66 import android.os.Parcelable;
67 import android.preference.PreferenceManager;
68 import android.support.v2.os.Build;
69 import android.support.v2.view.MenuCompat;
70 import android.text.TextUtils;
71 import android.util.Log;
72 import android.view.ContextMenu;
73 import android.view.ContextMenu.ContextMenuInfo;
74 import android.view.inputmethod.EditorInfo;
75 import android.view.KeyEvent;
76 import android.view.LayoutInflater;
77 import android.view.Menu;
78 import android.view.MenuItem;
79 import android.view.View;
80 import android.view.View.OnKeyListener;
81 import android.view.Window;
82 import android.widget.AbsListView;
83 import android.widget.AbsListView.OnScrollListener;
84 import android.widget.AdapterView;
85 import android.widget.AdapterView.AdapterContextMenuInfo;
86 import android.widget.BaseAdapter;
87 import android.widget.Button;
88 import android.widget.CheckBox;
89 import android.widget.EditText;
90 import android.widget.ImageButton;
91 import android.widget.ImageView;
92 import android.widget.LinearLayout;
93 import android.widget.ListView;
94 import android.widget.ProgressBar;
95 import android.widget.TextView;
96 import android.widget.Toast;
98 public class FileManagerActivity extends DistributionLibraryListActivity implements OnSharedPreferenceChangeListener {
99 private static final String TAG = "FileManagerActivity";
101 private static final String NOMEDIA_FILE = ".nomedia";
103 private static final String DIALOG_EXISTS_ACTION_RENAME = "action_rename";
104 private static final String DIALOG_EXISTS_ACTION_MULTI_COMPRESS_ZIP = "action_multi_compress_zip";
109 private static final Character FILE_EXTENSION_SEPARATOR = '.';
113 private static final int STATE_BROWSE = 1;
114 private static final int STATE_PICK_FILE = 2;
115 private static final int STATE_PICK_DIRECTORY = 3;
116 private static final int STATE_MULTI_SELECT = 4;
118 protected static final int REQUEST_CODE_MOVE = 1;
119 protected static final int REQUEST_CODE_COPY = 2;
120 protected static final int REQUEST_CODE_EXTRACT = 4;
125 private static final int REQUEST_CODE_MULTI_SELECT = 3;
127 private static final int MENU_PREFERENCES = Menu.FIRST + 3;
128 private static final int MENU_NEW_FOLDER = Menu.FIRST + 4;
129 private static final int MENU_DELETE = Menu.FIRST + 5;
130 private static final int MENU_RENAME = Menu.FIRST + 6;
131 private static final int MENU_SEND = Menu.FIRST + 7;
132 private static final int MENU_OPEN = Menu.FIRST + 8;
133 private static final int MENU_MOVE = Menu.FIRST + 9;
134 private static final int MENU_COPY = Menu.FIRST + 10;
138 private static final int MENU_MORE = Menu.FIRST + 11;
139 private static final int MENU_INCLUDE_IN_MEDIA_SCAN = Menu.FIRST + 12;
140 private static final int MENU_EXCLUDE_FROM_MEDIA_SCAN = Menu.FIRST + 13;
141 private static final int MENU_SETTINGS = Menu.FIRST + 14;
142 private static final int MENU_MULTI_SELECT = Menu.FIRST + 15;
143 private static final int MENU_FILTER = Menu.FIRST + 16;
144 private static final int MENU_DETAILS = Menu.FIRST + 17;
145 private static final int MENU_BOOKMARKS = Menu.FIRST + 18;
146 private static final int MENU_BOOKMARK = Menu.FIRST + 19;
147 private static final int MENU_COMPRESS = Menu.FIRST + 20;
148 private static final int MENU_EXTRACT = Menu.FIRST + 21;
149 private static final int MENU_REFRESH = Menu.FIRST + 22;
150 private static final int MENU_DISTRIBUTION_START = Menu.FIRST + 100; // MUST BE LAST
152 private static final int DIALOG_NEW_FOLDER = 1;
153 private static final int DIALOG_DELETE = 2;
154 private static final int DIALOG_RENAME = 3;
159 private static final int DIALOG_MULTI_DELETE = 4;
160 private static final int DIALOG_FILTER = 5;
161 private static final int DIALOG_DETAILS = 6;
163 private static final int DIALOG_BOOKMARKS = 7;
164 private static final int DIALOG_COMPRESSING = 8;
165 private static final int DIALOG_WARNING_EXISTS = 9;
166 private static final int DIALOG_CHANGE_FILE_EXTENSION = 10;
167 private static final int DIALOG_MULTI_COMPRESS_ZIP = 11;
169 private static final int DIALOG_DISTRIBUTION_START = 100; // MUST BE LAST
171 private static final int COPY_BUFFER_SIZE = 32 * 1024;
173 private static final String BUNDLE_CURRENT_DIRECTORY = "current_directory";
174 private static final String BUNDLE_CONTEXT_FILE = "context_file";
175 private static final String BUNDLE_CONTEXT_TEXT = "context_text";
176 private static final String BUNDLE_SHOW_DIRECTORY_INPUT = "show_directory_input";
177 private static final String BUNDLE_STEPS_BACK = "steps_back";
178 private static final String BUNDLE_DIRECTORY_ENTRIES = "directory_entries";
180 private static boolean mSoftKeyboardAvailable;
181 /** Shows whether activity state has been restored (e.g. from a rotation). */
182 private static boolean mRestored = false;
186 org.openintents.filemanager.compatibility.SoftKeyboard.checkAvailable();
187 mSoftKeyboardAvailable = true;
188 } catch (Throwable t) {
189 mSoftKeyboardAvailable = false;
194 /** Contains directories and files together */
195 private ArrayList<IconifiedText> directoryEntries = new ArrayList<IconifiedText>();
197 /** Dir separate for sorting */
198 List<IconifiedText> mListDir = new ArrayList<IconifiedText>();
200 /** Files separate for sorting */
201 List<IconifiedText> mListFile = new ArrayList<IconifiedText>();
203 /** SD card separate for sorting */
204 List<IconifiedText> mListSdCard = new ArrayList<IconifiedText>();
206 // There's a ".nomedia" file here
207 private boolean mNoMedia;
209 private File currentDirectory = new File("");
211 private String mSdCardPath = "";
213 private MimeTypes mMimeTypes;
214 /** Files shown are filtered using this extension */
215 private String mFilterFiletype = "";
216 /** Files shown are filtered using this mimetype */
217 private String mFilterMimetype = null;
219 private String mContextText;
220 private File mContextFile = new File("");
221 private Drawable mContextIcon;
223 /** How many steps one can make back using the back key. */
224 private int mStepsBack;
226 private EditText mEditFilename;
227 private Button mButtonPick;
228 private LinearLayout mDirectoryButtons;
233 private Button mButtonMove;
238 private Button mButtonCopy;
243 private Button mButtonDelete;
245 private Button mButtonCompress;
247 private boolean fileDeleted = false;
248 private int positionAtDelete;
249 private boolean deletedFileIsDirectory = false;
251 private LinearLayout mDirectoryInput;
252 private EditText mEditDirectory;
253 private ImageButton mButtonDirectoryPick;
258 private LinearLayout mActionNormal;
263 private LinearLayout mActionMultiselect;
265 private TextView mEmptyText;
266 private ProgressBar mProgressBar;
268 private DirectoryScanner mDirectoryScanner;
269 private File mPreviousDirectory;
271 private MenuItem mExcludeMediaScanMenuItem;
272 private MenuItem mIncludeMediaScanMenuItem;
274 private Handler currentHandler;
276 private boolean mWritableOnly;
278 private IconifiedText[] mDirectoryEntries;
280 static final public int MESSAGE_SHOW_DIRECTORY_CONTENTS = 500; // List of contents is ready, obj = DirectoryContents
281 static final public int MESSAGE_SET_PROGRESS = 501; // Set progress bar, arg1 = current value, arg2 = max value
282 static final public int MESSAGE_ICON_CHANGED = 502; // View needs to be redrawn, obj = IconifiedText
284 private ImageView mCheckIconSelect;
285 private boolean mSelected = false;
288 * use it field to pass params to onCreateDialog method
290 private String mDialogArgument;
293 * to show warning dialog to user if he want to change file extension
295 private String mOldFileName;
296 private String mNewFileName;
299 * use this filed to set behaviour in DIALOG_WARNING_EXISTS
301 private String mDialogExistsAction = "";
303 private Drawable mIconChecked;
304 private Drawable mIconUnchecked;
306 private ThumbnailLoader mThumbnailLoader;
308 /** Called when the activity is first created. */
310 public void onCreate(Bundle icicle) {
311 super.onCreate(icicle);
313 mDistribution.setFirst(MENU_DISTRIBUTION_START, DIALOG_DISTRIBUTION_START);
315 // Check whether EULA has been accepted
316 // or information about new version can be presented.
317 if (mDistribution.showEulaOrNewVersion()) {
321 currentHandler = new Handler() {
322 public void handleMessage(Message msg) {
323 FileManagerActivity.this.handleMessage(msg);
327 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
328 setContentView(R.layout.filelist);
331 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
332 prefs.registerOnSharedPreferenceChangeListener(this);
335 mEmptyText = (TextView) findViewById(R.id.empty_text);
336 mProgressBar = (ProgressBar) findViewById(R.id.scan_progress);
338 getListView().setOnCreateContextMenuListener(this);
339 getListView().setEmptyView(findViewById(R.id.empty));
340 getListView().setTextFilterEnabled(true);
341 getListView().requestFocus();
342 getListView().requestFocusFromTouch();
344 mDirectoryButtons = (LinearLayout) findViewById(R.id.directory_buttons);
345 mActionNormal = (LinearLayout) findViewById(R.id.action_normal);
346 mActionMultiselect = (LinearLayout) findViewById(R.id.action_multiselect);
347 mEditFilename = (EditText) findViewById(R.id.filename);
350 mButtonPick = (Button) findViewById(R.id.button_pick);
352 mButtonPick.setOnClickListener(new View.OnClickListener() {
354 public void onClick(View arg0) {
355 pickFileOrDirectory();
359 // Initialize only when necessary:
360 mDirectoryInput = null;
362 // Create map of extensions:
367 mState = STATE_BROWSE;
369 Intent intent = getIntent();
370 String action = intent.getAction();
372 File browseto = new File("/");
374 if (!TextUtils.isEmpty(mSdCardPath)) {
375 browseto = new File(mSdCardPath);
379 mState = STATE_BROWSE;
380 mWritableOnly = false;
382 if (action != null) {
384 if (action.equals(FileManagerIntents.ACTION_PICK_FILE)) {
385 mState = STATE_PICK_FILE;
386 mFilterFiletype = intent.getStringExtra("FILE_EXTENSION");
387 if(mFilterFiletype == null)
388 mFilterFiletype = "";
389 mFilterMimetype = intent.getType();
390 if(mFilterMimetype == null)
391 mFilterMimetype = "";
392 } else if (action.equals(FileManagerIntents.ACTION_PICK_DIRECTORY)) {
393 mState = STATE_PICK_DIRECTORY;
394 mWritableOnly = intent.getBooleanExtra(FileManagerIntents.EXTRA_WRITEABLE_ONLY, false);
396 // Remove edit text and make button fill whole line
397 mEditFilename.setVisibility(View.GONE);
398 mButtonPick.setLayoutParams(new LinearLayout.LayoutParams(
399 LinearLayout.LayoutParams.FILL_PARENT,
400 LinearLayout.LayoutParams.WRAP_CONTENT));
401 } else if (action.equals(FileManagerIntents.ACTION_MULTI_SELECT)) {
402 mState = STATE_MULTI_SELECT;
405 mDirectoryButtons.setVisibility(View.GONE);
406 mActionNormal.setVisibility(View.GONE);
408 // Multi select action: move
409 mButtonMove = (Button) findViewById(R.id.button_move);
410 mButtonMove.setOnClickListener(new View.OnClickListener() {
412 public void onClick(View arg0) {
413 if (checkSelection()) {
414 promptDestinationAndMoveFile();
419 // Multi select action: copy
420 mButtonCopy = (Button) findViewById(R.id.button_copy);
421 mButtonCopy.setOnClickListener(new View.OnClickListener() {
423 public void onClick(View arg0) {
424 if (checkSelection()) {
425 promptDestinationAndCopyFile();
430 // Multi select action: delete
431 mButtonDelete = (Button) findViewById(R.id.button_delete);
432 mButtonDelete.setOnClickListener(new View.OnClickListener() {
434 public void onClick(View arg0) {
435 if (checkSelection()) {
436 showDialog(DIALOG_MULTI_DELETE);
441 // Multi select action: delete
442 mButtonCompress = (Button) findViewById(R.id.button_compress_zip);
443 mButtonCompress.setOnClickListener(new View.OnClickListener() {
445 public void onClick(View arg0) {
446 if (checkSelection()) {
447 showDialog(DIALOG_MULTI_COMPRESS_ZIP);
452 // Cache the checked and unchecked icons
453 mIconChecked = getResources().getDrawable(R.drawable.ic_button_checked);
454 mIconUnchecked = getResources().getDrawable(R.drawable.ic_button_unchecked);
456 mCheckIconSelect = (ImageView) findViewById(R.id.check_icon_select);
457 mCheckIconSelect.setOnClickListener(new View.OnClickListener() {
460 public void onClick(View v) {
461 mSelected = !mSelected;
464 mCheckIconSelect.setImageDrawable(mIconChecked);
466 mCheckIconSelect.setImageDrawable(mIconUnchecked);
469 toggleSelection(mSelected);
477 if (mState == STATE_BROWSE) {
478 // Remove edit text and button.
479 mEditFilename.setVisibility(View.GONE);
480 mButtonPick.setVisibility(View.GONE);
483 if (mState != STATE_MULTI_SELECT) {
484 // Remove multiselect action buttons
485 mActionMultiselect.setVisibility(View.GONE);
488 // Set current directory and file based on intent data.
489 File file = FileUtils.getFile(intent.getData());
491 File dir = FileUtils.getPathWithoutFilename(file);
492 if (dir.isDirectory()) {
495 if (!file.isDirectory()) {
496 mEditFilename.setText(file.getName());
499 if(mState == STATE_PICK_FILE || mState == STATE_PICK_DIRECTORY
500 || action.equals(Intent.ACTION_GET_CONTENT)){
501 String path = PreferenceActivity.getDefaultPickFilePath(this);
503 File dir = new File(path);
504 if(dir.exists() && dir.isDirectory()){
511 String title = intent.getStringExtra(FileManagerIntents.EXTRA_TITLE);
516 String buttontext = intent.getStringExtra(FileManagerIntents.EXTRA_BUTTON_TEXT);
517 if (buttontext != null) {
518 mButtonPick.setText(buttontext);
523 // Reset mRestored flag.
525 if (icicle != null) {
526 browseto = new File(icicle.getString(BUNDLE_CURRENT_DIRECTORY));
527 mContextFile = new File(icicle.getString(BUNDLE_CONTEXT_FILE));
528 mContextText = icicle.getString(BUNDLE_CONTEXT_TEXT);
530 boolean show = icicle.getBoolean(BUNDLE_SHOW_DIRECTORY_INPUT);
531 showDirectoryInput(show);
533 mStepsBack = icicle.getInt(BUNDLE_STEPS_BACK);
534 // had to bypass direct casting as it was causing a rather unexplainable crash
535 Parcelable tmpDirectoryEntries[] = icicle.getParcelableArray(BUNDLE_DIRECTORY_ENTRIES);
536 mDirectoryEntries = new IconifiedText[tmpDirectoryEntries.length];
537 for(int i=0; i<tmpDirectoryEntries.length; i++){
538 mDirectoryEntries[i] = (IconifiedText) tmpDirectoryEntries[i];
543 getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
546 public void onScrollStateChanged(AbsListView view, int scrollState) {
547 IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
549 switch (scrollState) {
550 case OnScrollListener.SCROLL_STATE_IDLE:
551 adapter.toggleScrolling(false);
552 adapter.notifyDataSetChanged();
554 case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
555 adapter.toggleScrolling(true);
557 case OnScrollListener.SCROLL_STATE_FLING:
558 adapter.toggleScrolling(true);
565 public void onScroll(AbsListView view, int firstVisibleItem,
566 int visibleItemCount, int totalItemCount) {
574 public void onDestroy() {
578 DirectoryScanner scanner = mDirectoryScanner;
580 if (scanner != null) {
581 scanner.cancel = true;
584 mDirectoryScanner = null;
586 ThumbnailLoader loader = mThumbnailLoader;
588 if (loader != null) {
590 mThumbnailLoader = null;
594 if((lv = getListView()) != null){
599 private void handleMessage(Message message) {
600 // Log.v(TAG, "Received message " + message.what);
602 switch (message.what) {
603 case MESSAGE_SHOW_DIRECTORY_CONTENTS:
604 showDirectoryContents((DirectoryContents) message.obj);
607 case MESSAGE_SET_PROGRESS:
608 setProgress(message.arg1, message.arg2);
613 private void setProgress(int progress, int maxProgress) {
614 mProgressBar.setMax(maxProgress);
615 mProgressBar.setProgress(progress);
616 mProgressBar.setVisibility(View.VISIBLE);
619 private void showDirectoryContents(DirectoryContents contents) {
620 mDirectoryScanner = null;
622 mListSdCard = contents.listSdCard;
623 mListDir = contents.listDir;
624 mListFile = contents.listFile;
625 mNoMedia = contents.noMedia;
628 directoryEntries.ensureCapacity(mListSdCard.size() + mListDir.size() + mListFile.size());
630 addAllElements(directoryEntries, mListSdCard);
631 addAllElements(directoryEntries, mListDir);
632 addAllElements(directoryEntries, mListFile);
634 mDirectoryEntries = directoryEntries.toArray(new IconifiedText[0]);
637 directoryEntries.clear();
638 directoryEntries.ensureCapacity(mDirectoryEntries.length);
639 for(int i = 0; i < mDirectoryEntries.length; i++){
640 directoryEntries.add(mDirectoryEntries[i]);
643 // Once mRestore flag has been used, we should toggle it so that further refreshes don't take it into account
647 IconifiedTextListAdapter itla = new IconifiedTextListAdapter(this);
648 itla.setListItems(directoryEntries, getListView().hasTextFilter(), currentDirectory, mMimeTypes);
649 setListAdapter(itla);
650 getListView().setTextFilterEnabled(true);
652 ThumbnailLoader mThumbnailLoader = ((IconifiedTextListAdapter) getListAdapter()).getThumbnailLoader();
655 getListView().setSelection(positionAtDelete);
658 selectInList(mPreviousDirectory);
659 refreshDirectoryPanel();
660 setProgressBarIndeterminateVisibility(false);
662 mProgressBar.setVisibility(View.GONE);
663 mEmptyText.setVisibility(View.VISIBLE);
665 toggleCheckBoxVisibility(mState == STATE_MULTI_SELECT);
668 private void onCreateDirectoryInput() {
669 mDirectoryInput = (LinearLayout) findViewById(R.id.directory_input);
670 mEditDirectory = (EditText) findViewById(R.id.directory_text);
673 mEditDirectory.setOnKeyListener(new OnKeyListener() {
674 public boolean onKey(View v, int keyCode, KeyEvent event) {
675 // If the event is a key-down event on the "enter" button
676 if ((event.getAction() == KeyEvent.ACTION_DOWN)
677 && (keyCode == KeyEvent.KEYCODE_ENTER)){
679 goToDirectoryInEditText();
687 mButtonDirectoryPick = (ImageButton) findViewById(R.id.button_directory_pick);
689 mButtonDirectoryPick.setOnClickListener(new View.OnClickListener() {
691 public void onClick(View arg0) {
692 goToDirectoryInEditText();
697 //private boolean mHaveShownErrorMessage;
698 private File mHaveShownErrorMessageForFile = null;
700 private void hideKeyboard(IBinder windowToken, int flags){
701 if(mSoftKeyboardAvailable){
702 (new org.openintents.filemanager.compatibility.SoftKeyboard(this))
703 .hideSoftInputFromWindow(windowToken, flags);
707 private void goToDirectoryInEditText() {
708 File browseto = new File(mEditDirectory.getText().toString());
711 * After calling showDirectoryInput(false); the keyboard stays displayed.
712 * Hide it by calling hideKeyboard(windowToken, 0);
713 * Might be a bit problematic - it hides the keyboard even if id didn't
714 * appear after focusing the editText (user had it displayed before).
715 * But I think letting it displayed when user doesn't want to
716 * is much worse (and much more common) than hiding it although
717 * the user wants it displayed
720 IBinder windowToken = mEditDirectory.getWindowToken();
722 if (browseto.equals(currentDirectory)) {
723 showDirectoryInput(false);
724 hideKeyboard(windowToken, 0);
726 if (mHaveShownErrorMessageForFile != null
727 && mHaveShownErrorMessageForFile.equals(browseto)) {
728 // Don't let user get stuck in wrong directory.
729 mHaveShownErrorMessageForFile = null;
730 showDirectoryInput(false);
731 hideKeyboard(windowToken, 0);
733 if (!browseto.exists()) {
734 // browseTo() below will show an error message,
735 // because file does not exist.
736 // It is ok to show this the first time.
737 mHaveShownErrorMessageForFile = browseto;
739 showDirectoryInput(false);
740 hideKeyboard(windowToken, 0);
748 * Show the directory line as input box instead of button row.
749 * If Directory input does not exist yet, it is created.
750 * Since the default is show == false, nothing is created if
751 * it is not necessary (like after icicle).
754 private void showDirectoryInput(boolean show) {
756 if (mDirectoryInput == null) {
757 onCreateDirectoryInput();
760 if (mDirectoryInput != null) {
761 mDirectoryInput.setVisibility(show ? View.VISIBLE : View.GONE);
762 mDirectoryButtons.setVisibility(show ? View.GONE : View.VISIBLE);
765 refreshDirectoryPanel();
771 private void refreshDirectoryPanel() {
772 if (isDirectoryInputVisible()) {
773 // Set directory path
774 String path = currentDirectory.getAbsolutePath();
775 mEditDirectory.setText(path);
777 // Set selection to last position so user can continue to type:
778 mEditDirectory.setSelection(path.length());
780 setDirectoryButtons();
785 protected void onResume() {
786 // TODO Auto-generated method stub
793 protected void onSaveInstanceState(Bundle outState) {
794 // TODO Auto-generated method stub
795 super.onSaveInstanceState(outState);
797 // remember file name
798 outState.putString(BUNDLE_CURRENT_DIRECTORY, currentDirectory.getAbsolutePath());
799 outState.putString(BUNDLE_CONTEXT_FILE, mContextFile.getAbsolutePath());
800 outState.putString(BUNDLE_CONTEXT_TEXT, mContextText);
801 boolean show = isDirectoryInputVisible();
802 outState.putBoolean(BUNDLE_SHOW_DIRECTORY_INPUT, show);
803 outState.putInt(BUNDLE_STEPS_BACK, mStepsBack);
804 outState.putParcelableArray(BUNDLE_DIRECTORY_ENTRIES, mDirectoryEntries);
810 private boolean isDirectoryInputVisible() {
811 return ((mDirectoryInput != null) && (mDirectoryInput.getVisibility() == View.VISIBLE));
814 private void pickFileOrDirectory() {
816 if (mState == STATE_PICK_FILE) {
817 String filename = mEditFilename.getText().toString();
818 file = FileUtils.getFile(currentDirectory.getAbsolutePath(), filename);
819 } else if (mState == STATE_PICK_DIRECTORY) {
820 file = currentDirectory;
823 PreferenceActivity.setDefaultPickFilePath(this, currentDirectory.getAbsolutePath());
825 Intent intent = getIntent();
826 intent.setData(FileUtils.getUri(file));
827 setResult(RESULT_OK, intent);
834 private void getMimeTypes() {
835 MimeTypeParser mtp = null;
837 mtp = new MimeTypeParser(this, this.getPackageName());
838 } catch (NameNotFoundException e) {
839 //Should never happen
842 XmlResourceParser in = getResources().getXml(R.xml.mimetypes);
845 mMimeTypes = mtp.fromXmlResource(in);
846 } catch (XmlPullParserException e) {
850 "PreselectedChannelsActivity: XmlPullParserException",
852 throw new RuntimeException(
853 "PreselectedChannelsActivity: XmlPullParserException");
854 } catch (IOException e) {
855 Log.e(TAG, "PreselectedChannelsActivity: IOException", e);
856 throw new RuntimeException(
857 "PreselectedChannelsActivity: IOException");
862 * This function browses up one level
863 * according to the field: currentDirectory
865 private void upOneLevel(){
866 if (mStepsBack > 0) {
869 if(currentDirectory.getParent() != null)
870 browseTo(currentDirectory.getParentFile());
874 * Jump to some location by clicking on a
877 * This resets the counter for "back" actions.
881 private void jumpTo(final File aDirectory) {
883 browseTo(aDirectory);
887 * Browse to some location by clicking on a list item.
890 private void browseTo(final File aDirectory){
891 // setTitle(aDirectory.getAbsolutePath());
893 if (aDirectory.isDirectory()){
894 if (aDirectory.equals(currentDirectory)) {
895 // Switch from button to directory input
896 showDirectoryInput(true);
898 mPreviousDirectory = currentDirectory;
899 currentDirectory = aDirectory;
901 // selectInList(previousDirectory);
902 // refreshDirectoryPanel();
905 if (mState == STATE_BROWSE || mState == STATE_PICK_DIRECTORY) {
906 // Lets start an intent to View the file, that was clicked...
907 openFile(aDirectory);
908 } else if (mState == STATE_PICK_FILE) {
910 mEditFilename.setText(aDirectory.getName());
916 private void openFile(File aFile) {
917 if (!aFile.exists()) {
918 Toast.makeText(this, R.string.error_file_does_not_exists, Toast.LENGTH_SHORT).show();
922 Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
924 Uri data = FileUtils.getUri(aFile);
925 String type = mMimeTypes.getMimeType(aFile.getName());
926 intent.setDataAndType(data, type);
928 // Were we in GET_CONTENT mode?
929 Intent originalIntent = getIntent();
931 if (originalIntent != null && originalIntent.getAction() != null && originalIntent.getAction().equals(Intent.ACTION_GET_CONTENT)) {
932 // In that case, we should probably just return the requested data.
933 PreferenceActivity.setDefaultPickFilePath(this,
934 FileUtils.getPathWithoutFilename(aFile).getAbsolutePath());
935 intent.setData(Uri.parse(FileManagerProvider.FILE_PROVIDER_PREFIX + aFile));
936 setResult(RESULT_OK, intent);
944 startActivity(intent);
945 } catch (ActivityNotFoundException e) {
946 Toast.makeText(this, R.string.application_not_available, Toast.LENGTH_SHORT).show();
950 public void refreshList() {
952 boolean directoriesOnly = mState == STATE_PICK_DIRECTORY;
954 // Cancel an existing scanner, if applicable.
955 DirectoryScanner scanner = mDirectoryScanner;
957 if (scanner != null) {
958 scanner.cancel = true;
961 ThumbnailLoader loader = mThumbnailLoader;
963 if (loader != null) {
965 mThumbnailLoader = null;
968 directoryEntries.clear();
973 setProgressBarIndeterminateVisibility(true);
975 // Don't show the "folder empty" text since we're scanning.
976 mEmptyText.setVisibility(View.GONE);
978 // Also DON'T show the progress bar - it's kind of lame to show that
979 // for less than a second.
980 mProgressBar.setVisibility(View.GONE);
981 setListAdapter(null);
983 mDirectoryScanner = new DirectoryScanner(currentDirectory, this, currentHandler, mMimeTypes, mFilterFiletype, mFilterMimetype, mSdCardPath, mWritableOnly, directoriesOnly);
984 mDirectoryScanner.start();
988 // Add the "." == "current directory"
989 /*directoryEntries.add(new IconifiedText(
990 getString(R.string.current_dir),
991 getResources().getDrawable(R.drawable.ic_launcher_folder))); */
992 // and the ".." == 'Up one level'
994 if(currentDirectory.getParent() != null)
995 directoryEntries.add(new IconifiedText(
996 getString(R.string.up_one_level),
997 getResources().getDrawable(R.drawable.ic_launcher_folder_open)));
1001 private void selectInList(File selectFile) {
1002 String filename = selectFile.getName();
1003 IconifiedTextListAdapter la = (IconifiedTextListAdapter) getListAdapter();
1004 int count = la.getCount();
1005 for (int i = 0; i < count; i++) {
1006 IconifiedText it = (IconifiedText) la.getItem(i);
1007 if (it.getText().equals(filename)) {
1008 getListView().setSelection(i);
1014 private void addAllElements(List<IconifiedText> addTo, List<IconifiedText> addFrom) {
1015 int size = addFrom.size();
1016 for (int i = 0; i < size; i++) {
1017 addTo.add(addFrom.get(i));
1021 private void setDirectoryButtons() {
1022 String[] parts = currentDirectory.getAbsolutePath().split("/");
1024 mDirectoryButtons.removeAllViews();
1026 int WRAP_CONTENT = LinearLayout.LayoutParams.WRAP_CONTENT;
1028 // Add home button separately
1029 ImageButton ib = new ImageButton(this);
1030 ib.setImageResource(R.drawable.ic_launcher_home_small);
1031 ib.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1032 ib.setOnClickListener(new View.OnClickListener() {
1033 public void onClick(View view) {
1034 jumpTo(new File("/"));
1037 mDirectoryButtons.addView(ib);
1039 // Add other buttons
1043 for (int i = 1; i < parts.length; i++) {
1044 dir += "/" + parts[i];
1045 if (dir.equals(mSdCardPath)) {
1046 // Add SD card button
1047 ib = new ImageButton(this);
1048 ib.setImageResource(R.drawable.ic_launcher_sdcard_small);
1049 ib.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1050 ib.setOnClickListener(new View.OnClickListener() {
1051 public void onClick(View view) {
1052 jumpTo(new File(mSdCardPath));
1055 mDirectoryButtons.addView(ib);
1057 Button b = new Button(this);
1058 b.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1059 b.setText(parts[i]);
1061 b.setOnClickListener(new View.OnClickListener() {
1062 public void onClick(View view) {
1063 String dir = (String) view.getTag();
1064 jumpTo(new File(dir));
1067 mDirectoryButtons.addView(b);
1071 checkButtonLayout();
1074 private void checkButtonLayout() {
1076 // Let's measure how much space we need:
1077 int spec = View.MeasureSpec.UNSPECIFIED;
1078 mDirectoryButtons.measure(spec, spec);
1079 int count = mDirectoryButtons.getChildCount();
1081 int requiredwidth = mDirectoryButtons.getMeasuredWidth();
1082 int width = getWindowManager().getDefaultDisplay().getWidth();
1084 if (requiredwidth > width) {
1085 int WRAP_CONTENT = LinearLayout.LayoutParams.WRAP_CONTENT;
1087 // Create a new button that shows that there is more to the left:
1088 ImageButton ib = new ImageButton(this);
1089 ib.setImageResource(R.drawable.ic_menu_back_small);
1090 ib.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1092 ib.setOnClickListener(new View.OnClickListener() {
1093 public void onClick(View view) {
1094 // Up one directory.
1098 mDirectoryButtons.addView(ib, 0);
1100 // New button needs even more space
1101 ib.measure(spec, spec);
1102 requiredwidth += ib.getMeasuredWidth();
1104 // Need to take away some buttons
1105 // but leave at least "back" button and one directory button.
1106 while (requiredwidth > width && mDirectoryButtons.getChildCount() > 2) {
1107 View view = mDirectoryButtons.getChildAt(1);
1108 requiredwidth -= view.getMeasuredWidth();
1110 mDirectoryButtons.removeViewAt(1);
1116 protected void onListItemClick(ListView l, View v, int position, long id) {
1117 super.onListItemClick(l, v, position, id);
1119 IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
1121 if (adapter == null) {
1125 IconifiedText text = (IconifiedText) adapter.getItem(position);
1127 if (mState == STATE_MULTI_SELECT) {
1128 text.setSelected(!text.isSelected());
1129 adapter.notifyDataSetChanged();
1133 String file = text.getText();
1135 if (selectedFileString.equals(getString(R.string.up_one_level))) {
1139 String curdir = currentDirectory
1140 .getAbsolutePath() ;
1141 File clickedFile = FileUtils.getFile(curdir, file);
1142 if (clickedFile != null) {
1143 if (clickedFile.isDirectory()) {
1144 // If we click on folders, we can return later by the "back" key.
1147 browseTo(clickedFile);
1154 private void getSdCardPath() {
1155 mSdCardPath = android.os.Environment
1156 .getExternalStorageDirectory().getAbsolutePath();
1161 public boolean onCreateOptionsMenu(Menu menu) {
1162 super.onCreateOptionsMenu(menu);
1164 int icon = android.R.drawable.ic_menu_add;
1165 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
1166 icon = R.drawable.ic_menu_add_folder;
1168 MenuItem item = menu.add(0, MENU_NEW_FOLDER, 0, R.string.menu_new_folder).setIcon(
1169 icon).setShortcut('0', 'f');
1170 MenuCompat.setShowAsAction(item, 0/*MenuItem.SHOW_AS_ACTION_IF_ROOM*/);
1172 if (mState == STATE_BROWSE) {
1173 // Multi select option menu.
1174 menu.add(0, MENU_MULTI_SELECT, 0, R.string.menu_multi_select).setIcon(
1175 R.drawable.ic_menu_multiselect).setShortcut('1', 'm');
1178 mIncludeMediaScanMenuItem = menu.add(0, MENU_INCLUDE_IN_MEDIA_SCAN, 0, R.string.menu_include_in_media_scan).setShortcut('2', 's')
1179 .setIcon(android.R.drawable.ic_menu_gallery);
1180 mExcludeMediaScanMenuItem = menu.add(0, MENU_EXCLUDE_FROM_MEDIA_SCAN, 0, R.string.menu_exclude_from_media_scan).setShortcut('2', 's')
1181 .setIcon(android.R.drawable.ic_menu_gallery);
1183 menu.add(0, MENU_BOOKMARKS, 0, R.string.bookmarks).setIcon(
1184 R.drawable.ic_menu_star);
1187 menu.add(0, MENU_SETTINGS, 0, R.string.settings).setIcon(
1188 android.R.drawable.ic_menu_preferences).setShortcut('9', 'p');
1190 /* We don't want to allow the user to override a filter set
1191 * by an application.
1193 if(mState != STATE_PICK_FILE) {
1194 menu.add(0, MENU_FILTER, 0, R.string.menu_filter).setIcon(
1195 android.R.drawable.ic_menu_search);
1198 menu.add(0, MENU_REFRESH, 0, R.string.menu_refresh).setIcon(
1199 android.R.drawable.ic_menu_rotate);
1201 mDistribution.onCreateOptionsMenu(menu);
1207 public boolean onPrepareOptionsMenu(Menu menu) {
1208 super.onPrepareOptionsMenu(menu);
1210 mIncludeMediaScanMenuItem.setVisible(false);
1211 mExcludeMediaScanMenuItem.setVisible(false);
1213 boolean showMediaScanMenuItem = PreferenceActivity.getMediaScanFromPreference(this);
1215 // We only know about ".nomedia" once we have the results list back.
1216 if (showMediaScanMenuItem && mListDir != null) {
1218 mIncludeMediaScanMenuItem.setVisible(true);
1220 mExcludeMediaScanMenuItem.setVisible(true);
1224 // Generate any additional actions that can be performed on the
1225 // overall list. This allows other applications to extend
1226 // our menu with their own actions.
1227 Intent intent = new Intent(null, getIntent().getData());
1228 intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
1229 // menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
1230 // new ComponentName(this, NoteEditor.class), null, intent, 0, null);
1232 // Workaround to add icons:
1233 MenuIntentOptionsWithIcons menu2 = new MenuIntentOptionsWithIcons(this,
1235 menu2.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
1236 new ComponentName(this, FileManagerActivity.class), null, intent,
1243 public boolean onOptionsItemSelected(MenuItem item) {
1244 switch (item.getItemId()) {
1245 case MENU_NEW_FOLDER:
1246 showDialog(DIALOG_NEW_FOLDER);
1249 case MENU_MULTI_SELECT:
1250 promptMultiSelect();
1253 case MENU_INCLUDE_IN_MEDIA_SCAN:
1254 includeInMediaScan();
1257 case MENU_EXCLUDE_FROM_MEDIA_SCAN:
1258 excludeFromMediaScan();
1266 showDialog(DIALOG_FILTER);
1269 case MENU_BOOKMARKS:
1270 showDialog(DIALOG_BOOKMARKS);
1277 return super.onOptionsItemSelected(item);
1281 private void showSettings() {
1282 Intent intent = new Intent(this, PreferenceActivity.class);
1283 startActivity(intent);
1287 public void onCreateContextMenu(ContextMenu menu, View view,
1288 ContextMenuInfo menuInfo) {
1289 AdapterView.AdapterContextMenuInfo info;
1291 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
1292 } catch (ClassCastException e) {
1293 Log.e(TAG, "bad menuInfo", e);
1297 Cursor cursor = (Cursor) getListAdapter().getItem(info.position);
1298 if (cursor == null) {
1299 // For some reason the requested item isn't available, do nothing
1303 IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
1305 if (adapter == null) {
1309 IconifiedText it = (IconifiedText) adapter.getItem(info.position);
1310 menu.setHeaderTitle(it.getText());
1311 menu.setHeaderIcon(it.getIcon());
1312 File file = FileUtils.getFile(currentDirectory, it.getText());
1315 if (!file.isDirectory()) {
1316 if (mState == STATE_PICK_FILE) {
1318 menu.add(0, MENU_OPEN, 0, R.string.menu_open);
1320 menu.add(0, MENU_SEND, 0, R.string.menu_send);
1322 menu.add(0, MENU_MOVE, 0, R.string.menu_move);
1324 if (!file.isDirectory()) {
1325 menu.add(0, MENU_COPY, 0, R.string.menu_copy);
1328 menu.add(0, MENU_RENAME, 0, R.string.menu_rename);
1329 menu.add(0, MENU_DELETE, 0, R.string.menu_delete);
1331 //if (!file.isDirectory()) {
1332 Uri data = Uri.fromFile(file);
1333 Intent intent = new Intent(null, data);
1334 String type = mMimeTypes.getMimeType(file.getName());
1336 intent.setDataAndType(data, type);
1337 intent.addCategory(Intent.CATEGORY_SELECTED_ALTERNATIVE);
1338 //intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
1340 Log.v(TAG, "Data=" + data);
1341 Log.v(TAG, "Type=" + type);
1344 // Add additional options for the MIME type of the selected file.
1345 menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
1346 new ComponentName(this, FileManagerActivity.class), null, intent, 0, null);
1350 if (FileUtils.checkIfZipArchive(file)){
1351 menu.add(0, MENU_EXTRACT, 0, R.string.menu_extract);
1353 menu.add(0, MENU_COMPRESS, 0, R.string.menu_compress);
1355 menu.add(0, MENU_DETAILS, 0, R.string.menu_details);
1356 menu.add(0, MENU_BOOKMARK, 0, R.string.menu_bookmark);
1357 menu.add(0, MENU_MORE, 0, R.string.menu_more);
1361 public boolean onContextItemSelected(MenuItem item) {
1362 super.onContextItemSelected(item);
1363 AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item
1366 // Remember current selection
1367 IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
1369 if (adapter == null) {
1373 IconifiedText ic = (IconifiedText) adapter.getItem(menuInfo.position);
1374 mContextText = ic.getText();
1375 mContextIcon = ic.getIcon();
1376 mContextFile = FileUtils.getFile(currentDirectory, ic.getText());
1378 switch (item.getItemId()) {
1380 openFile(mContextFile);
1384 promptDestinationAndMoveFile();
1388 promptDestinationAndCopyFile();
1392 showDialog(DIALOG_DELETE);
1396 showDialog(DIALOG_RENAME);
1400 sendFile(mContextFile);
1404 showDialog(DIALOG_DETAILS);
1408 showDialog(DIALOG_COMPRESSING);
1412 promptDestinationAndExtract();
1416 String path = mContextFile.getAbsolutePath();
1417 Cursor query = managedQuery(BookmarksProvider.CONTENT_URI,
1418 new String[]{BookmarksProvider._ID},
1419 BookmarksProvider.PATH + "=?",
1422 if(!query.moveToFirst()){
1423 ContentValues values = new ContentValues();
1424 values.put(BookmarksProvider.NAME, mContextFile.getName());
1425 values.put(BookmarksProvider.PATH, path);
1426 getContentResolver().insert(BookmarksProvider.CONTENT_URI, values);
1427 Toast.makeText(this, R.string.bookmark_added, Toast.LENGTH_SHORT).show();
1430 Toast.makeText(this, R.string.bookmark_already_exists, Toast.LENGTH_SHORT).show();
1435 if (!PreferenceActivity.getShowAllWarning(FileManagerActivity.this)) {
1436 showMoreCommandsDialog();
1440 showWarningDialog();
1449 protected Dialog onCreateDialog(int id) {
1452 case DIALOG_NEW_FOLDER:
1453 LayoutInflater inflater = LayoutInflater.from(this);
1454 View view = inflater.inflate(R.layout.dialog_new_folder, null);
1455 final EditText et = (EditText) view
1456 .findViewById(R.id.foldername);
1458 //accept "return" key
1459 TextView.OnEditorActionListener returnListener = new TextView.OnEditorActionListener(){
1460 public boolean onEditorAction(TextView exampleView, int actionId, KeyEvent event) {
1461 if (actionId == EditorInfo.IME_NULL
1462 && event.getAction() == KeyEvent.ACTION_DOWN) {
1463 createNewFolder(et.getText().toString()); //match this behavior to your OK button
1464 dismissDialog(DIALOG_NEW_FOLDER);
1470 et.setOnEditorActionListener(returnListener);
1471 //end of code regarding "return key"
1473 return new AlertDialog.Builder(this)
1474 .setIcon(android.R.drawable.ic_dialog_alert)
1475 .setTitle(R.string.create_new_folder).setView(view).setPositiveButton(
1476 android.R.string.ok, new OnClickListener() {
1478 public void onClick(DialogInterface dialog, int which) {
1479 createNewFolder(et.getText().toString());
1482 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1484 public void onClick(DialogInterface dialog, int which) {
1485 // Cancel should not do anything.
1492 return new AlertDialog.Builder(this).setTitle(getString(R.string.really_delete, mContextText))
1493 .setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(
1494 android.R.string.ok, new OnClickListener() {
1496 public void onClick(DialogInterface dialog, int which) {
1497 deleteFileOrFolder(mContextFile);
1500 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1502 public void onClick(DialogInterface dialog, int which) {
1503 // Cancel should not do anything.
1509 inflater = LayoutInflater.from(this);
1510 view = inflater.inflate(R.layout.dialog_new_folder, null);
1511 final EditText et2 = (EditText) view
1512 .findViewById(R.id.foldername);
1513 //accept "return" key
1514 TextView.OnEditorActionListener returnListener2 = new TextView.OnEditorActionListener(){
1515 public boolean onEditorAction(TextView exampleView, int actionId, KeyEvent event) {
1516 if (actionId == EditorInfo.IME_NULL
1517 && event.getAction() == KeyEvent.ACTION_DOWN) {
1518 renameFileOrFolder(mContextFile, et2.getText().toString()); //match this behavior to your OK button
1519 dismissDialog(DIALOG_RENAME);
1525 et2.setOnEditorActionListener(returnListener2);
1526 //end of code regarding "return key"
1527 return new AlertDialog.Builder(this)
1528 .setTitle(R.string.menu_rename).setView(view).setPositiveButton(
1529 android.R.string.ok, new OnClickListener() {
1531 public void onClick(DialogInterface dialog, int which) {
1533 renameFileOrFolder(mContextFile, et2.getText().toString());
1536 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1538 public void onClick(DialogInterface dialog, int which) {
1539 // Cancel should not do anything.
1544 case DIALOG_MULTI_DELETE:
1545 String contentText = null;
1547 for (IconifiedText it : mDirectoryEntries) {
1548 if (!it.isSelected()) {
1552 contentText = it.getText();
1557 string = getString(R.string.really_delete, contentText);
1559 string = getString(R.string.really_delete_multiselect, count);
1561 return new AlertDialog.Builder(this).setTitle(string)
1562 .setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(
1563 android.R.string.ok, new OnClickListener() {
1565 public void onClick(DialogInterface dialog, int which) {
1568 Intent intent = getIntent();
1569 setResult(RESULT_OK, intent);
1573 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1575 public void onClick(DialogInterface dialog, int which) {
1576 // Cancel should not do anything.
1582 inflater = LayoutInflater.from(this);
1583 view = inflater.inflate(R.layout.dialog_new_folder, null);
1584 ((TextView)view.findViewById(R.id.foldernametext)).setText(R.string.extension);
1585 final EditText et3 = (EditText) view
1586 .findViewById(R.id.foldername);
1588 return new AlertDialog.Builder(this)
1589 .setIcon(android.R.drawable.ic_dialog_alert)
1590 .setTitle(R.string.menu_filter).setView(view).setPositiveButton(
1591 android.R.string.ok, new OnClickListener() {
1593 public void onClick(DialogInterface dialog, int which) {
1594 mFilterFiletype = et3.getText().toString().trim();
1598 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1600 public void onClick(DialogInterface dialog, int which) {
1601 // Cancel should not do anything.
1607 case DIALOG_DETAILS:
1608 inflater = LayoutInflater.from(this);
1609 view = inflater.inflate(R.layout.dialog_details, null);
1611 return new AlertDialog.Builder(this).setTitle(mContextText).
1612 setIcon(mContextIcon).setView(view).create();
1614 case DIALOG_BOOKMARKS:
1615 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1617 final Cursor bookmarksCursor = getBookmarks();
1619 builder.setTitle(R.string.bookmarks);
1621 builder.setCursor(bookmarksCursor, new DialogInterface.OnClickListener() {
1622 public void onClick(DialogInterface dialog, int item) {
1623 if (bookmarksCursor.moveToPosition(item)) {
1624 String path = bookmarksCursor.getString(
1625 bookmarksCursor.getColumnIndex(BookmarksProvider.PATH));
1626 File file = new File(path);
1628 if (file.isDirectory()) {
1634 Toast.makeText(FileManagerActivity.this, R.string.bookmark_not_found,
1635 Toast.LENGTH_SHORT).show();
1638 }, BookmarksProvider.NAME);
1640 return builder.create();
1642 case DIALOG_COMPRESSING:
1643 inflater = LayoutInflater.from(this);
1644 view = inflater.inflate(R.layout.dialog_new_folder, null);
1645 final EditText editText = (EditText) view.findViewById(R.id.foldername);
1646 //accept "return" key
1647 TextView.OnEditorActionListener returnListener3 = new TextView.OnEditorActionListener(){
1648 public boolean onEditorAction(TextView exampleView, int actionId, KeyEvent event) {
1649 if (actionId == EditorInfo.IME_NULL
1650 && event.getAction() == KeyEvent.ACTION_DOWN) {
1651 if (new File(mContextFile.getParent()+File.separator+editText.getText().toString()).exists()){
1652 mDialogArgument = editText.getText().toString();
1653 showDialog(DIALOG_WARNING_EXISTS);
1655 new CompressManager(FileManagerActivity.this).compress(mContextFile, editText.getText().toString());
1656 } //match this behavior to your OK button
1657 dismissDialog(DIALOG_COMPRESSING);
1663 editText.setOnEditorActionListener(returnListener3);
1664 //end of code regarding "return key"
1665 return new AlertDialog.Builder(this)
1666 .setTitle(R.string.menu_compress).setView(view).setPositiveButton(
1667 android.R.string.ok, new OnClickListener() {
1668 public void onClick(DialogInterface dialog, int which) {
1669 if (new File(mContextFile.getParent()+File.separator+editText.getText().toString()).exists()){
1670 mDialogArgument = editText.getText().toString();
1671 showDialog(DIALOG_WARNING_EXISTS);
1673 new CompressManager(FileManagerActivity.this).compress(mContextFile, editText.getText().toString());
1676 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1677 public void onClick(DialogInterface dialog, int which) {
1678 // Cancel should not do anything.
1682 case DIALOG_MULTI_COMPRESS_ZIP:
1683 inflater = LayoutInflater.from(this);
1684 view = inflater.inflate(R.layout.dialog_new_folder, null);
1685 final EditText editText1 = (EditText) view.findViewById(R.id.foldername);
1686 //accept "return" key
1687 TextView.OnEditorActionListener returnListener4 = new TextView.OnEditorActionListener(){
1688 public boolean onEditorAction(TextView exampleView, int actionId, KeyEvent event) {
1689 if (actionId == EditorInfo.IME_NULL
1690 && event.getAction() == KeyEvent.ACTION_DOWN) {
1691 if (new File(currentDirectory+File.separator+editText1.getText().toString()).exists()){
1692 mDialogArgument = editText1.getText().toString();
1693 mDialogExistsAction = DIALOG_EXISTS_ACTION_MULTI_COMPRESS_ZIP;
1694 showDialog(DIALOG_WARNING_EXISTS);
1696 compressMultiFile(editText1.getText().toString());
1697 } //match this behavior to your OK button
1698 dismissDialog(DIALOG_MULTI_COMPRESS_ZIP);
1704 editText1.setOnEditorActionListener(returnListener4);
1705 //end of code regarding "return key"
1706 return new AlertDialog.Builder(this)
1707 .setTitle(R.string.menu_compress).setView(view).setPositiveButton(
1708 android.R.string.ok, new OnClickListener() {
1709 public void onClick(DialogInterface dialog, int which) {
1710 if (new File(currentDirectory+File.separator+editText1.getText().toString()).exists()){
1711 mDialogArgument = editText1.getText().toString();
1712 mDialogExistsAction = DIALOG_EXISTS_ACTION_MULTI_COMPRESS_ZIP;
1713 showDialog(DIALOG_WARNING_EXISTS);
1715 compressMultiFile(editText1.getText().toString());
1718 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1719 public void onClick(DialogInterface dialog, int which) {
1720 // Cancel should not do anything.
1724 case DIALOG_WARNING_EXISTS:
1725 return new AlertDialog.Builder(this).setTitle(getString(R.string.warning_overwrite, mDialogArgument))
1726 .setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(
1727 android.R.string.ok, new OnClickListener() {
1728 public void onClick(DialogInterface dialog, int which) {
1729 if (mDialogExistsAction.equals(DIALOG_EXISTS_ACTION_MULTI_COMPRESS_ZIP)){
1730 compressMultiFile(mDialogArgument);
1731 } else if (mDialogExistsAction.equals(DIALOG_EXISTS_ACTION_RENAME)){
1732 File newFile = FileUtils.getFile(currentDirectory, mNewFileName);
1733 rename(FileUtils.getFile(currentDirectory, mOldFileName), newFile);
1735 new File(mContextFile.getParent()+File.separator+mDialogArgument).delete();
1736 new CompressManager(FileManagerActivity.this).compress(mContextFile, mDialogArgument);
1738 mDialogExistsAction = "";
1740 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1741 public void onClick(DialogInterface dialog, int which) {
1742 if (mDialogExistsAction.equals(DIALOG_EXISTS_ACTION_RENAME)){
1743 mContextText = mOldFileName;
1744 showDialog(DIALOG_RENAME);
1745 } else if (mDialogExistsAction.equals(DIALOG_EXISTS_ACTION_MULTI_COMPRESS_ZIP)){
1746 showDialog(DIALOG_MULTI_COMPRESS_ZIP);
1748 showDialog(DIALOG_COMPRESSING);
1750 mDialogExistsAction = "";
1754 case DIALOG_CHANGE_FILE_EXTENSION:
1755 return new AlertDialog.Builder(this).setTitle(getString(R.string.change_file_extension))
1756 .setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(
1757 android.R.string.ok, new OnClickListener() {
1758 public void onClick(DialogInterface dialog, int which) {
1759 File newFile = FileUtils.getFile(currentDirectory, mNewFileName);
1760 if (newFile.exists()){
1761 mDialogExistsAction = DIALOG_EXISTS_ACTION_RENAME;
1762 showDialog(DIALOG_WARNING_EXISTS);
1764 rename(FileUtils.getFile(currentDirectory, mOldFileName), newFile);
1767 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1768 public void onClick(DialogInterface dialog, int which) {
1769 mContextText = mOldFileName;
1770 showDialog(DIALOG_RENAME);
1774 return super.onCreateDialog(id);
1778 private Cursor getBookmarks(){
1779 return managedQuery(BookmarksProvider.CONTENT_URI,
1781 BookmarksProvider._ID,
1782 BookmarksProvider.NAME,
1783 BookmarksProvider.PATH,
1784 }, null, null, null);
1789 protected void onPrepareDialog(int id, Dialog dialog) {
1790 super.onPrepareDialog(id, dialog);
1793 case DIALOG_NEW_FOLDER:
1794 EditText et = (EditText) dialog.findViewById(R.id.foldername);
1799 ((AlertDialog) dialog).setTitle(getString(R.string.really_delete, mContextText));
1803 et = (EditText) dialog.findViewById(R.id.foldername);
1804 et.setText(mContextText);
1805 TextView tv = (TextView) dialog.findViewById(R.id.foldernametext);
1806 if (mContextFile.isDirectory()) {
1807 tv.setText(R.string.file_name);
1809 tv.setText(R.string.file_name);
1811 et.setSelection(0, mContextText.lastIndexOf(".") == -1 ? mContextText.length() : mContextText.lastIndexOf("."));
1812 ((AlertDialog) dialog).setIcon(mContextIcon);
1815 case DIALOG_MULTI_DELETE:
1818 case DIALOG_DETAILS:
1819 final TextView type = ((TextView)dialog.findViewById(R.id.details_type_value));
1820 type.setText((mContextFile.isDirectory() ? R.string.details_type_folder :
1821 (mContextFile.isFile() ? R.string.details_type_file :
1822 R.string.details_type_other)));
1824 final TextView size = ((TextView)dialog.findViewById(R.id.details_size_value));
1825 size.setText(FileUtils.formatSize(this, mContextFile.length()));
1827 // Creates a background thread that obtains the size of a directory and updates
1828 // the TextView accordingly.
1829 if(mContextFile.isDirectory()){
1830 final AsyncTask folderSizeTask = new AsyncTask<File, Long, Long>(){
1832 protected long totalSize = 0L;
1835 protected Long doInBackground(File... file) {
1841 protected void onProgressUpdate(Long... updatedSize){
1842 size.setText(FileUtils.formatSize(size.getContext(), updatedSize[0]));
1846 protected void onPostExecute(Long result){
1847 size.setText(FileUtils.formatSize(size.getContext(), result));
1850 private void sizeOf(File file){
1852 totalSize += file.length();
1853 publishProgress(totalSize);
1855 File[] files = file.listFiles();
1857 if(files != null && files.length != 0){
1858 for(File subFile : files){
1864 }.execute(mContextFile);
1866 ((AlertDialog) dialog).setOnCancelListener(new OnCancelListener(){
1868 public void onCancel(DialogInterface dialog) {
1869 folderSizeTask.cancel(true);
1874 String perms = (mContextFile.canRead() ? "R" : "-") +
1875 (mContextFile.canWrite() ? "W" : "-") +
1876 (FileUtils.canExecute(mContextFile) ? "X" : "-");
1878 final TextView permissions = ((TextView)dialog.findViewById(R.id.details_permissions_value));
1879 permissions.setText(perms);
1881 final TextView hidden = ((TextView)dialog.findViewById(R.id.details_hidden_value));
1882 hidden.setText(mContextFile.isHidden() ? R.string.details_yes : R.string.details_no);
1884 final TextView lastmodified = ((TextView)dialog.findViewById(R.id.details_lastmodified_value));
1885 lastmodified.setText(FileUtils.formatDate(this, mContextFile.lastModified()));
1886 ((AlertDialog) dialog).setIcon(mContextIcon);
1887 ((AlertDialog) dialog).setTitle(mContextText);
1890 case DIALOG_COMPRESSING:
1891 TextView textView = (TextView) dialog.findViewById(R.id.foldernametext);
1892 textView.setText(R.string.compress_into_archive);
1893 final EditText editText = (EditText) dialog.findViewById(R.id.foldername);
1894 String archiveName = "";
1895 if (mContextFile.isDirectory()){
1896 archiveName = mContextFile.getName()+".zip";
1898 String extension = FileUtils.getExtension(mContextFile.getName());
1899 archiveName = mContextFile.getName().replaceAll(extension, "")+".zip";
1901 editText.setText(archiveName);
1902 editText.setSelection(0, archiveName.length()-4);
1905 case DIALOG_MULTI_COMPRESS_ZIP:
1906 textView = (TextView) dialog.findViewById(R.id.foldernametext);
1907 textView.setText(R.string.compress_into_archive);
1908 final EditText editText1 = (EditText) dialog.findViewById(R.id.foldername);
1909 archiveName = currentDirectory.getName()+".zip";
1910 editText1.setText(archiveName);
1911 editText1.setSelection(0, archiveName.length()-4);
1914 case DIALOG_WARNING_EXISTS:
1915 dialog.setTitle(getString(R.string.warning_overwrite, mDialogArgument));
1922 private void showWarningDialog() {
1923 LayoutInflater li = LayoutInflater.from(this);
1924 View warningView = li.inflate(R.layout.dialog_warning, null);
1925 final CheckBox showWarningAgain = (CheckBox)warningView.findViewById(R.id.showagaincheckbox);
1927 showWarningAgain.setChecked(PreferenceActivity.getShowAllWarning(FileManagerActivity.this));
1929 new AlertDialog.Builder(this).setView(warningView).setTitle(getString(R.string.title_warning_some_may_not_work))
1930 .setMessage(getString(R.string.warning_some_may_not_work, mContextText))
1931 .setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(
1932 android.R.string.ok, new OnClickListener() {
1934 public void onClick(DialogInterface dialog, int which) {
1935 PreferenceActivity.setShowAllWarning(FileManagerActivity.this, showWarningAgain.isChecked());
1937 showMoreCommandsDialog();
1947 private void showMoreCommandsDialog() {
1948 final Uri data = Uri.fromFile(mContextFile);
1949 final Intent intent = new Intent(null, data);
1950 String type = mMimeTypes.getMimeType(mContextFile.getName());
1952 intent.setDataAndType(data, type);
1954 Log.v(TAG, "Data=" + data);
1955 Log.v(TAG, "Type=" + type);
1958 // Add additional options for the MIME type of the selected file.
1959 PackageManager pm = getPackageManager();
1960 final List<ResolveInfo> lri = pm.queryIntentActivityOptions(
1961 new ComponentName(this, FileManagerActivity.class),
1963 final int N = lri != null ? lri.size() : 0;
1965 // Create name list for menu item.
1966 final List<CharSequence> items = new ArrayList<CharSequence>();
1967 /* Some of the options don't go to the list hence we have to remove them
1968 * to keep the lri correspond with the menu items. In the addition, we have
1969 * to remove them after the first iteration, otherwise the iteration breaks.
1971 List<ResolveInfo> toRemove = new ArrayList<ResolveInfo>();
1972 for (int i = 0; i < N; i++) {
1973 final ResolveInfo ri = lri.get(i);
1974 Intent rintent = new Intent(intent);
1975 rintent.setComponent(
1977 ri.activityInfo.applicationInfo.packageName,
1978 ri.activityInfo.name));
1979 ActivityInfo info = rintent.resolveActivityInfo(pm, 0);
1980 String permission = info.permission;
1981 if(info.exported && (permission == null
1982 || checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED))
1983 items.add(ri.loadLabel(pm));
1988 for(ResolveInfo ri : toRemove){
1992 new AlertDialog.Builder(this)
1993 .setTitle(mContextText)
1994 .setIcon(mContextIcon)
1995 .setItems(items.toArray(new CharSequence[0]),
1996 new DialogInterface.OnClickListener() {
1997 public void onClick(DialogInterface dialog, int item) {
1998 final ResolveInfo ri = lri.get(item);
1999 Intent rintent = new Intent(intent)
2000 .setComponent(new ComponentName(
2001 ri.activityInfo.applicationInfo.packageName,
2002 ri.activityInfo.name));
2003 startActivity(rintent);
2010 private void includeInMediaScan() {
2011 // Delete the .nomedia file.
2012 File file = FileUtils.getFile(currentDirectory, NOMEDIA_FILE);
2013 if (file.delete()) {
2014 Toast.makeText(this, getString(R.string.media_scan_included), Toast.LENGTH_LONG).show();
2017 // That didn't work.
2018 Toast.makeText(this, getString(R.string.error_generic), Toast.LENGTH_LONG).show();
2022 private void excludeFromMediaScan() {
2023 // Create the .nomedia file.
2024 File file = FileUtils.getFile(currentDirectory, NOMEDIA_FILE);
2026 if (file.createNewFile()) {
2028 Toast.makeText(this, getString(R.string.media_scan_excluded), Toast.LENGTH_LONG).show();
2030 Toast.makeText(this, getString(R.string.error_media_scan), Toast.LENGTH_LONG).show();
2032 } catch (IOException e) {
2033 // That didn't work.
2034 Toast.makeText(this, getString(R.string.error_generic) + e.getMessage(), Toast.LENGTH_LONG).show();
2038 private boolean checkSelection() {
2039 for (IconifiedText it : mDirectoryEntries) {
2040 if (!it.isSelected()) {
2047 Toast.makeText(this, R.string.error_selection, Toast.LENGTH_SHORT).show();
2052 private void toggleSelection(boolean selected) {
2053 for(IconifiedText it : mDirectoryEntries){
2054 it.setSelected(selected);
2057 ((BaseAdapter) getListAdapter()).notifyDataSetChanged();
2060 private void toggleCheckBoxVisibility(boolean visible) {
2061 for(IconifiedText it : mDirectoryEntries){
2062 it.setCheckIconVisible(visible);
2065 ((BaseAdapter) getListAdapter()).notifyDataSetChanged();
2068 private void promptDestinationAndMoveFile() {
2070 Intent intent = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY);
2072 intent.setData(FileUtils.getUri(currentDirectory));
2074 intent.putExtra(FileManagerIntents.EXTRA_TITLE, getString(R.string.move_title));
2075 intent.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, getString(R.string.move_button));
2076 intent.putExtra(FileManagerIntents.EXTRA_WRITEABLE_ONLY, true);
2078 startActivityForResult(intent, REQUEST_CODE_MOVE);
2081 private void promptDestinationAndExtract() {
2082 Intent intent = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY);
2083 intent.setData(FileUtils.getUri(currentDirectory));
2084 intent.putExtra(FileManagerIntents.EXTRA_TITLE, getString(R.string.extract_title));
2085 intent.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, getString(R.string.extract_button));
2086 intent.putExtra(FileManagerIntents.EXTRA_WRITEABLE_ONLY, true);
2087 startActivityForResult(intent, REQUEST_CODE_EXTRACT);
2090 private void promptDestinationAndCopyFile() {
2092 Intent intent = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY);
2094 intent.setData(FileUtils.getUri(currentDirectory));
2096 intent.putExtra(FileManagerIntents.EXTRA_TITLE, getString(R.string.copy_title));
2097 intent.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, getString(R.string.copy_button));
2098 intent.putExtra(FileManagerIntents.EXTRA_WRITEABLE_ONLY, true);
2100 startActivityForResult(intent, REQUEST_CODE_COPY);
2104 * Starts activity for multi select.
2106 private void promptMultiSelect() {
2107 Intent intent = new Intent(FileManagerIntents.ACTION_MULTI_SELECT);
2109 intent.setData(FileUtils.getUri(currentDirectory));
2111 intent.putExtra(FileManagerIntents.EXTRA_TITLE, getString(R.string.multiselect_title));
2112 //intent.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, getString(R.string.move_button));
2114 startActivityForResult(intent, REQUEST_CODE_MULTI_SELECT);
2117 private void createNewFolder(String foldername) {
2118 if (!TextUtils.isEmpty(foldername)) {
2119 File file = FileUtils.getFile(currentDirectory, foldername);
2120 if (file.mkdirs()) {
2122 // Change into new directory:
2125 Toast.makeText(this, R.string.error_creating_new_folder, Toast.LENGTH_SHORT).show();
2130 private void compressMultiFile(String out) {
2131 List<File> files = new ArrayList<File>();
2132 for (IconifiedText it : mDirectoryEntries) {
2133 if (!it.isSelected()) {
2137 File file = FileUtils.getFile(currentDirectory, it.getText());
2140 new CompressManager(FileManagerActivity.this).compress(files, out);
2143 /*! Recursively delete a directory and all of its children.
2144 * @params toastOnError If set to true, this function will toast if an error occurs.
2145 * @returns true if successful, false otherwise.
2147 private boolean recursiveDelete(File file, boolean toastOnError) {
2148 // Recursively delete all contents.
2149 File[] files = file.listFiles();
2151 if (files == null) {
2152 Toast.makeText(this, getString(R.string.error_deleting_folder, file.getAbsolutePath()), Toast.LENGTH_LONG);
2156 for (int x=0; x<files.length; x++) {
2157 File childFile = files[x];
2158 if (childFile.isDirectory()) {
2159 if (!recursiveDelete(childFile, toastOnError)) {
2163 if (!childFile.delete()) {
2164 Toast.makeText(this, getString(R.string.error_deleting_child_file, childFile.getAbsolutePath()), Toast.LENGTH_LONG);
2170 if (!file.delete()) {
2171 Toast.makeText(this, getString(R.string.error_deleting_folder, file.getAbsolutePath()), Toast.LENGTH_LONG);
2178 private class RecursiveDeleteTask extends AsyncTask<Object, Void, Integer> {
2180 private FileManagerActivity activity = FileManagerActivity.this;
2181 private static final int success = 0;
2182 private static final int err_deleting_folder = 1;
2183 private static final int err_deleting_child_file = 2;
2184 private static final int err_deleting_file = 3;
2186 private File errorFile;
2189 * Recursively delete a file or directory and all of its children.
2191 * @returns 0 if successful, error value otherwise.
2193 private int recursiveDelete(File file) {
2194 if (file.isDirectory() && file.listFiles() != null)
2195 for (File childFile : file.listFiles()) {
2196 if (childFile.isDirectory()) {
2197 int result = recursiveDelete(childFile);
2202 if (!childFile.delete()) {
2203 errorFile = childFile;
2204 return err_deleting_child_file;
2209 if (!file.delete()) {
2211 return file.isFile() ? err_deleting_file : err_deleting_folder;
2218 protected void onPreExecute() {
2219 Toast.makeText(activity, R.string.deleting_files, Toast.LENGTH_SHORT).show();
2222 @SuppressWarnings("unchecked")
2224 protected Integer doInBackground(Object... params) {
2225 Object files = params[0];
2227 if (files instanceof List<?>) {
2228 for (File file: (List<File>)files) {
2229 int result = recursiveDelete(file);
2230 if (result != success) return result;
2234 return recursiveDelete((File)files);
2239 protected void onPostExecute(Integer result) {
2242 activity.refreshList();
2243 if(deletedFileIsDirectory){
2244 Toast.makeText(activity, R.string.folder_deleted,Toast.LENGTH_SHORT).show();
2246 Toast.makeText(activity, R.string.file_deleted,Toast.LENGTH_SHORT).show();
2249 case err_deleting_folder:
2250 Toast.makeText(activity,getString(R.string.error_deleting_folder,
2251 errorFile.getAbsolutePath()), Toast.LENGTH_LONG).show();
2253 case err_deleting_child_file:
2254 Toast.makeText(activity,getString(R.string.error_deleting_child_file,
2255 errorFile.getAbsolutePath()),Toast.LENGTH_SHORT).show();
2257 case err_deleting_file:
2258 Toast.makeText(activity,getString(R.string.error_deleting_file,
2259 errorFile.getAbsolutePath()), Toast.LENGTH_LONG).show();
2266 private void deleteFileOrFolder(File file) {
2268 positionAtDelete = getListView().getFirstVisiblePosition();
2269 deletedFileIsDirectory = file.isDirectory();
2270 new RecursiveDeleteTask().execute(file);
2271 // if (file.isDirectory()) {
2272 // if (recursiveDelete(file, true)) {
2274 // Toast.makeText(this, R.string.folder_deleted, Toast.LENGTH_SHORT).show();
2277 // if (file.delete()) {
2278 // // Delete was successful.
2280 // Toast.makeText(this, R.string.file_deleted, Toast.LENGTH_SHORT).show();
2282 // Toast.makeText(this, R.string.error_deleting_file, Toast.LENGTH_SHORT).show();
2287 private void deleteMultiFile() {
2289 LinkedList<File> files = new LinkedList<File>();
2290 for (IconifiedText it : mDirectoryEntries) {
2291 if (!it.isSelected()) {
2295 File file = FileUtils.getFile(currentDirectory, it.getText());
2297 // if (file.isDirectory()) {
2298 // if (!recursiveDelete(file, true)) {
2302 // if (!file.delete()) {
2303 // toast = R.string.error_deleting_file;
2309 new RecursiveDeleteTask().execute(files);
2311 // if (toast == 0) {
2312 // // Delete was successful.
2314 // toast = R.string.file_deleted;
2317 // Toast.makeText(FileManagerActivity.this, toast, Toast.LENGTH_SHORT).show();
2320 private void renameFileOrFolder(File file, String newFileName) {
2321 mOldFileName = file.getName();
2322 mNewFileName = newFileName;
2323 mDialogArgument = mNewFileName;
2324 if (newFileName != null && newFileName.length() > 0){
2325 if (!file.isDirectory() && !FileUtils.getExtension(newFileName).equals(FileUtils.getExtension(file.getName()))){
2326 showDialog(DIALOG_CHANGE_FILE_EXTENSION);
2330 File newFile = FileUtils.getFile(currentDirectory, newFileName);
2331 if (newFile.exists()){
2332 mDialogExistsAction = DIALOG_EXISTS_ACTION_RENAME;
2333 showDialog(DIALOG_WARNING_EXISTS);
2335 rename(file, newFile);
2343 private void rename(File oldFile, File newFile) {
2345 if (oldFile.renameTo(newFile)) {
2346 // Rename was successful.
2348 if (newFile.isDirectory()) {
2349 toast = R.string.folder_renamed;
2351 toast = R.string.file_renamed;
2354 if (newFile.isDirectory()) {
2355 toast = R.string.error_renaming_folder;
2357 toast = R.string.error_renaming_file;
2360 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2363 /*@ RETURNS: A file name that is guaranteed to not exist yet.
2366 * context - Application context.
2367 * path - The path that the file is supposed to be in.
2368 * fileName - Desired file name. This name will be modified to
2369 * create a unique file if necessary.
2372 private File createUniqueCopyName(Context context, File path, String fileName) {
2373 // Does that file exist?
2374 File file = FileUtils.getFile(path, fileName);
2376 if (!file.exists()) {
2377 // Nope - we can take that.
2381 // Split file's name and extension to fix internationalization issue #307
2382 int fromIndex = fileName.lastIndexOf(FILE_EXTENSION_SEPARATOR);
2383 String extension = "";
2384 if (fromIndex > 0) {
2385 extension = fileName.substring(fromIndex);
2386 fileName = fileName.substring(0, fromIndex);
2389 // Try a simple "copy of".
2390 file = FileUtils.getFile(path, context.getString(R.string.copied_file_name, fileName).concat(extension));
2392 if (!file.exists()) {
2393 // Nope - we can take that.
2399 // Well, we gotta find a unique name at some point.
2400 while (copyIndex < 500) {
2401 file = FileUtils.getFile(path, context.getString(R.string.copied_file_name_2, copyIndex, fileName).concat(extension));
2403 if (!file.exists()) {
2404 // Nope - we can take that.
2415 private boolean copy(File oldFile, File newFile) {
2417 FileInputStream input = new FileInputStream(oldFile);
2418 FileOutputStream output = new FileOutputStream(newFile);
2420 byte[] buffer = new byte[COPY_BUFFER_SIZE];
2423 int bytes = input.read(buffer);
2429 output.write(buffer, 0, bytes);
2435 } catch (Exception e) {
2441 private void sendFile(File file) {
2443 String filename = file.getName();
2444 String content = "hh";
2446 Log.i(TAG, "Title to send: " + filename);
2447 Log.i(TAG, "Content to send: " + content);
2449 Intent i = new Intent();
2450 i.setAction(Intent.ACTION_SEND);
2451 i.setType(mMimeTypes.getMimeType(file.getName()));
2452 i.putExtra(Intent.EXTRA_SUBJECT, filename);
2453 //i.putExtra(Intent.EXTRA_STREAM, FileUtils.getUri(file));
2454 i.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + FileManagerProvider.AUTHORITY + file.getAbsolutePath()));
2456 i = Intent.createChooser(i, getString(R.string.menu_send));
2460 } catch (ActivityNotFoundException e) {
2461 Toast.makeText(this, R.string.send_not_available,
2462 Toast.LENGTH_SHORT).show();
2463 Log.e(TAG, "Email client not installed");
2467 // This code seems to work for SDK 2.3 (target="9")
2469 public boolean onKeyDown(int keyCode, KeyEvent event) {
2471 if (keyCode == KeyEvent.KEYCODE_BACK) {
2472 if (mStepsBack > 0) {
2478 return super.onKeyDown(keyCode, event);
2481 // For targetSdkVersion="5" or higher, one needs to use the following code instead of the one above:
2482 // (See http://android-developers.blogspot.com/2009/12/back-and-other-hard-keys-three-stories.html )
2486 public boolean onKeyDown(int keyCode, KeyEvent event) {
2487 if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR
2488 && keyCode == KeyEvent.KEYCODE_BACK
2489 && event.getRepeatCount() == 0) {
2490 // Take care of calling this method on earlier versions of
2491 // the platform where it doesn't exist.
2495 return super.onKeyDown(keyCode, event);
2499 public void onBackPressed() {
2500 // This will be called either automatically for you on 2.0
2501 // or later, or by the code above on earlier versions of the
2503 if (mStepsBack > 0) {
2512 * This is called after the file manager finished.
2515 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
2516 super.onActivityResult(requestCode, resultCode, data);
2518 switch (requestCode) {
2519 case REQUEST_CODE_MOVE:
2520 if (resultCode == RESULT_OK && data != null) {
2521 // obtain the filename
2522 File movefrom = mContextFile;
2523 File moveto = FileUtils.getFile(data.getData());
2524 if (moveto != null) {
2525 if (mState != STATE_MULTI_SELECT) {
2526 // Move single file.
2527 moveto = FileUtils.getFile(moveto, movefrom.getName());
2529 if (movefrom.renameTo(moveto)) {
2530 // Move was successful.
2532 if (moveto.isDirectory()) {
2533 toast = R.string.folder_moved;
2535 toast = R.string.file_moved;
2538 if (moveto.isDirectory()) {
2539 toast = R.string.error_moving_folder;
2541 toast = R.string.error_moving_file;
2544 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2548 for (IconifiedText it : mDirectoryEntries) {
2549 if (!it.isSelected()) {
2553 movefrom = FileUtils.getFile(currentDirectory, it.getText());
2554 File newPath = FileUtils.getFile(moveto, movefrom.getName());
2555 if (!movefrom.renameTo(newPath)) {
2557 if (moveto.isDirectory()) {
2558 toast = R.string.error_moving_folder;
2560 toast = R.string.error_moving_file;
2567 // Move was successful.
2569 toast = R.string.file_moved;
2572 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2574 Intent intent = getIntent();
2575 setResult(RESULT_OK, intent);
2584 case REQUEST_CODE_EXTRACT:
2585 if (resultCode == RESULT_OK && data != null) {
2586 new ExtractManager(this).extract(mContextFile, data.getData().getPath());
2590 case REQUEST_CODE_COPY:
2591 if (resultCode == RESULT_OK && data != null) {
2592 // obtain the filename
2593 File copyfrom = mContextFile;
2594 File copyto = FileUtils.getFile(data.getData());
2595 if (copyto != null) {
2596 if (mState != STATE_MULTI_SELECT) {
2597 // Copy single file.
2598 copyto = createUniqueCopyName(this, copyto, copyfrom.getName());
2600 if (copyto != null) {
2602 if (copy(copyfrom, copyto)) {
2603 toast = R.string.file_copied;
2606 toast = R.string.error_copying_file;
2608 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2613 for (IconifiedText it : mDirectoryEntries) {
2614 if (!it.isSelected()) {
2618 copyfrom = FileUtils.getFile(currentDirectory, it.getText());
2619 File newPath = createUniqueCopyName(this, copyto, copyfrom.getName());
2620 if (copyto != null) {
2621 if (!copy(copyfrom, newPath)) {
2622 toast = R.string.error_copying_file;
2629 // Copy was successful.
2630 toast = R.string.file_copied;
2634 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2636 Intent intent = getIntent();
2637 setResult(RESULT_OK, intent);
2644 case REQUEST_CODE_MULTI_SELECT:
2645 if (resultCode == RESULT_OK && data != null) {
2653 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
2654 if (//When the user chooses to show/hide hidden files, update the list
2655 //to correspond with the user's choice
2656 PreferenceActivity.PREFS_DISPLAYHIDDENFILES.equals(key)
2657 //When the user changes the sortBy settings, update the list
2658 || PreferenceActivity.PREFS_SORTBY.equals(key)
2659 || PreferenceActivity.PREFS_ASCENDING.equals(key)){