merge in OI distribution
[android_pandora.git] / apps / oi-filemanager / FileManager / src / org / openintents / filemanager / FileManagerActivity.java
CommitLineData
811a5a4a 1/*
2 * Copyright (C) 2008 OpenIntents.org
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17/*
18 * Based on AndDev.org's file browser V 2.0.
19 */
20
21package org.openintents.filemanager;
22
23import java.io.File;
24import java.io.FileInputStream;
25import java.io.FileOutputStream;
26import java.io.IOException;
27import java.util.ArrayList;
28import java.util.LinkedList;
29import java.util.List;
30
27a4fda1 31import org.openintents.filemanager.DistributionLibraryListActivity;
811a5a4a 32import org.openintents.filemanager.util.CompressManager;
33import org.openintents.filemanager.util.ExtractManager;
34import org.openintents.filemanager.util.FileUtils;
35import org.openintents.filemanager.util.MimeTypeParser;
36import org.openintents.filemanager.util.MimeTypes;
37import org.openintents.intents.FileManagerIntents;
38import org.openintents.util.MenuIntentOptionsWithIcons;
39import org.xmlpull.v1.XmlPullParserException;
40
41import android.app.AlertDialog;
42import android.app.Dialog;
43import android.content.ActivityNotFoundException;
44import android.content.ComponentName;
45import android.content.ContentValues;
46import android.content.Context;
47import android.content.DialogInterface;
48import android.content.DialogInterface.OnCancelListener;
49import android.content.DialogInterface.OnClickListener;
50import android.content.Intent;
51import android.content.SharedPreferences;
52import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
53import android.content.pm.ActivityInfo;
54import android.content.pm.PackageManager;
55import android.content.pm.PackageManager.NameNotFoundException;
56import android.content.pm.ResolveInfo;
57import android.content.res.XmlResourceParser;
58import android.database.Cursor;
59import android.graphics.drawable.Drawable;
60import android.net.Uri;
61import android.os.AsyncTask;
62import android.os.Bundle;
63import android.os.Handler;
64import android.os.IBinder;
65import android.os.Message;
66import android.os.Parcelable;
67import android.preference.PreferenceManager;
68import android.support.v2.os.Build;
69import android.support.v2.view.MenuCompat;
70import android.text.TextUtils;
71import android.util.Log;
72import android.view.ContextMenu;
73import android.view.ContextMenu.ContextMenuInfo;
74import android.view.inputmethod.EditorInfo;
75import android.view.KeyEvent;
76import android.view.LayoutInflater;
77import android.view.Menu;
78import android.view.MenuItem;
79import android.view.View;
80import android.view.View.OnKeyListener;
81import android.view.Window;
82import android.widget.AbsListView;
83import android.widget.AbsListView.OnScrollListener;
84import android.widget.AdapterView;
85import android.widget.AdapterView.AdapterContextMenuInfo;
86import android.widget.BaseAdapter;
87import android.widget.Button;
88import android.widget.CheckBox;
89import android.widget.EditText;
90import android.widget.ImageButton;
91import android.widget.ImageView;
92import android.widget.LinearLayout;
93import android.widget.ListView;
94import android.widget.ProgressBar;
95import android.widget.TextView;
96import android.widget.Toast;
97
98public class FileManagerActivity extends DistributionLibraryListActivity implements OnSharedPreferenceChangeListener {
99 private static final String TAG = "FileManagerActivity";
100
101 private static final String NOMEDIA_FILE = ".nomedia";
102
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";
105
106 /**
107 * @since 2011-03-23
108 */
109 private static final Character FILE_EXTENSION_SEPARATOR = '.';
110
111 private int mState;
112
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;
117
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;
121
122 /**
123 * @since 2011-02-11
124 */
125 private static final int REQUEST_CODE_MULTI_SELECT = 3;
126
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;
135 /**
136 * @since 2011-09-29
137 */
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
151
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;
155
156 /**
157 * @since 2011-02-12
158 */
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;
162
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;
168
169 private static final int DIALOG_DISTRIBUTION_START = 100; // MUST BE LAST
170
171 private static final int COPY_BUFFER_SIZE = 32 * 1024;
172
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";
179
180 private static boolean mSoftKeyboardAvailable;
181 /** Shows whether activity state has been restored (e.g. from a rotation). */
182 private static boolean mRestored = false;
183
184 static {
185 try {
186 org.openintents.filemanager.compatibility.SoftKeyboard.checkAvailable();
187 mSoftKeyboardAvailable = true;
188 } catch (Throwable t) {
189 mSoftKeyboardAvailable = false;
190 }
191 }
192
193
194 /** Contains directories and files together */
195 private ArrayList<IconifiedText> directoryEntries = new ArrayList<IconifiedText>();
196
197 /** Dir separate for sorting */
198 List<IconifiedText> mListDir = new ArrayList<IconifiedText>();
199
200 /** Files separate for sorting */
201 List<IconifiedText> mListFile = new ArrayList<IconifiedText>();
202
203 /** SD card separate for sorting */
204 List<IconifiedText> mListSdCard = new ArrayList<IconifiedText>();
205
206 // There's a ".nomedia" file here
207 private boolean mNoMedia;
208
209 private File currentDirectory = new File("");
210
211 private String mSdCardPath = "";
212
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;
218
219 private String mContextText;
220 private File mContextFile = new File("");
221 private Drawable mContextIcon;
222
223 /** How many steps one can make back using the back key. */
224 private int mStepsBack;
225
226 private EditText mEditFilename;
227 private Button mButtonPick;
228 private LinearLayout mDirectoryButtons;
229
230 /**
231 * @since 2011-02-11
232 */
233 private Button mButtonMove;
234
235 /**
236 * @since 2011-02-11
237 */
238 private Button mButtonCopy;
239
240 /**
241 * @since 2011-02-11
242 */
243 private Button mButtonDelete;
244
245 private Button mButtonCompress;
246
247 private boolean fileDeleted = false;
248 private int positionAtDelete;
249 private boolean deletedFileIsDirectory = false;
250
251 private LinearLayout mDirectoryInput;
252 private EditText mEditDirectory;
253 private ImageButton mButtonDirectoryPick;
254
255 /**
256 * @since 2011-02-11
257 */
258 private LinearLayout mActionNormal;
259
260 /**
261 * @since 2011-02-11
262 */
263 private LinearLayout mActionMultiselect;
264
265 private TextView mEmptyText;
266 private ProgressBar mProgressBar;
267
268 private DirectoryScanner mDirectoryScanner;
269 private File mPreviousDirectory;
270
271 private MenuItem mExcludeMediaScanMenuItem;
272 private MenuItem mIncludeMediaScanMenuItem;
273
274 private Handler currentHandler;
275
276 private boolean mWritableOnly;
277
278 private IconifiedText[] mDirectoryEntries;
279
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
283
284 private ImageView mCheckIconSelect;
285 private boolean mSelected = false;
286
287 /**
288 * use it field to pass params to onCreateDialog method
289 */
290 private String mDialogArgument;
291
292 /**
293 * to show warning dialog to user if he want to change file extension
294 */
295 private String mOldFileName;
296 private String mNewFileName;
297
298 /**
299 * use this filed to set behaviour in DIALOG_WARNING_EXISTS
300 */
301 private String mDialogExistsAction = "";
302
303 private Drawable mIconChecked;
304 private Drawable mIconUnchecked;
305
306 private ThumbnailLoader mThumbnailLoader;
307
308 /** Called when the activity is first created. */
309 @Override
310 public void onCreate(Bundle icicle) {
311 super.onCreate(icicle);
312
313 mDistribution.setFirst(MENU_DISTRIBUTION_START, DIALOG_DISTRIBUTION_START);
314
315 // Check whether EULA has been accepted
316 // or information about new version can be presented.
317 if (mDistribution.showEulaOrNewVersion()) {
318 return;
319 }
320
321 currentHandler = new Handler() {
322 public void handleMessage(Message msg) {
323 FileManagerActivity.this.handleMessage(msg);
324 }
325 };
326
327 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
328 setContentView(R.layout.filelist);
329
330
331 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
332 prefs.registerOnSharedPreferenceChangeListener(this);
333
334
335 mEmptyText = (TextView) findViewById(R.id.empty_text);
336 mProgressBar = (ProgressBar) findViewById(R.id.scan_progress);
337
338 getListView().setOnCreateContextMenuListener(this);
339 getListView().setEmptyView(findViewById(R.id.empty));
340 getListView().setTextFilterEnabled(true);
341 getListView().requestFocus();
342 getListView().requestFocusFromTouch();
343
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);
348
349
350 mButtonPick = (Button) findViewById(R.id.button_pick);
351
352 mButtonPick.setOnClickListener(new View.OnClickListener() {
353
354 public void onClick(View arg0) {
355 pickFileOrDirectory();
356 }
357 });
358
359 // Initialize only when necessary:
360 mDirectoryInput = null;
361
362 // Create map of extensions:
363 getMimeTypes();
364
365 getSdCardPath();
366
367 mState = STATE_BROWSE;
368
369 Intent intent = getIntent();
370 String action = intent.getAction();
371
372 File browseto = new File("/");
373
374 if (!TextUtils.isEmpty(mSdCardPath)) {
375 browseto = new File(mSdCardPath);
376 }
377
378 // Default state
379 mState = STATE_BROWSE;
380 mWritableOnly = false;
381
382 if (action != null) {
383
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);
395
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;
403
404 // Remove buttons
405 mDirectoryButtons.setVisibility(View.GONE);
406 mActionNormal.setVisibility(View.GONE);
407
408 // Multi select action: move
409 mButtonMove = (Button) findViewById(R.id.button_move);
410 mButtonMove.setOnClickListener(new View.OnClickListener() {
411
412 public void onClick(View arg0) {
413 if (checkSelection()) {
414 promptDestinationAndMoveFile();
415 }
416 }
417 });
418
419 // Multi select action: copy
420 mButtonCopy = (Button) findViewById(R.id.button_copy);
421 mButtonCopy.setOnClickListener(new View.OnClickListener() {
422
423 public void onClick(View arg0) {
424 if (checkSelection()) {
425 promptDestinationAndCopyFile();
426 }
427 }
428 });
429
430 // Multi select action: delete
431 mButtonDelete = (Button) findViewById(R.id.button_delete);
432 mButtonDelete.setOnClickListener(new View.OnClickListener() {
433
434 public void onClick(View arg0) {
435 if (checkSelection()) {
436 showDialog(DIALOG_MULTI_DELETE);
437 }
438 }
439 });
440
441 // Multi select action: delete
442 mButtonCompress = (Button) findViewById(R.id.button_compress_zip);
443 mButtonCompress.setOnClickListener(new View.OnClickListener() {
444
445 public void onClick(View arg0) {
446 if (checkSelection()) {
447 showDialog(DIALOG_MULTI_COMPRESS_ZIP);
448 }
449 }
450 });
451
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);
455
456 mCheckIconSelect = (ImageView) findViewById(R.id.check_icon_select);
457 mCheckIconSelect.setOnClickListener(new View.OnClickListener() {
458
459 @Override
460 public void onClick(View v) {
461 mSelected = !mSelected;
462
463 if(mSelected){
464 mCheckIconSelect.setImageDrawable(mIconChecked);
465 } else {
466 mCheckIconSelect.setImageDrawable(mIconUnchecked);
467 }
468
469 toggleSelection(mSelected);
470 }
471 });
472
473 }
474
475 }
476
477 if (mState == STATE_BROWSE) {
478 // Remove edit text and button.
479 mEditFilename.setVisibility(View.GONE);
480 mButtonPick.setVisibility(View.GONE);
481 }
482
483 if (mState != STATE_MULTI_SELECT) {
484 // Remove multiselect action buttons
485 mActionMultiselect.setVisibility(View.GONE);
486 }
487
488 // Set current directory and file based on intent data.
489 File file = FileUtils.getFile(intent.getData());
490 if (file != null) {
491 File dir = FileUtils.getPathWithoutFilename(file);
492 if (dir.isDirectory()) {
493 browseto = dir;
494 }
495 if (!file.isDirectory()) {
496 mEditFilename.setText(file.getName());
497 }
498 } else{
499 if(mState == STATE_PICK_FILE || mState == STATE_PICK_DIRECTORY
500 || action.equals(Intent.ACTION_GET_CONTENT)){
501 String path = PreferenceActivity.getDefaultPickFilePath(this);
502 if(path != null){
503 File dir = new File(path);
504 if(dir.exists() && dir.isDirectory()){
505 browseto = dir;
506 }
507 }
508 }
509 }
510
511 String title = intent.getStringExtra(FileManagerIntents.EXTRA_TITLE);
512 if (title != null) {
513 setTitle(title);
514 }
515
516 String buttontext = intent.getStringExtra(FileManagerIntents.EXTRA_BUTTON_TEXT);
517 if (buttontext != null) {
518 mButtonPick.setText(buttontext);
519 }
520
521 mStepsBack = 0;
522
523 // Reset mRestored flag.
524 mRestored = false;
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);
529
530 boolean show = icicle.getBoolean(BUNDLE_SHOW_DIRECTORY_INPUT);
531 showDirectoryInput(show);
532
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];
539 }
540 mRestored = true;
541 }
542
543 getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
544
545 @Override
546 public void onScrollStateChanged(AbsListView view, int scrollState) {
547 IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
548 if(adapter != null){
549 switch (scrollState) {
550 case OnScrollListener.SCROLL_STATE_IDLE:
551 adapter.toggleScrolling(false);
552 adapter.notifyDataSetChanged();
553 break;
554 case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
555 adapter.toggleScrolling(true);
556 break;
557 case OnScrollListener.SCROLL_STATE_FLING:
558 adapter.toggleScrolling(true);
559 break;
560 }
561 }
562 }
563
564 @Override
565 public void onScroll(AbsListView view, int firstVisibleItem,
566 int visibleItemCount, int totalItemCount) {
567 // Not used
568 }
569 });
570
571 browseTo(browseto);
572 }
573
574 public void onDestroy() {
575 super.onDestroy();
576
577 // Stop the scanner.
578 DirectoryScanner scanner = mDirectoryScanner;
579
580 if (scanner != null) {
581 scanner.cancel = true;
582 }
583
584 mDirectoryScanner = null;
585
586 ThumbnailLoader loader = mThumbnailLoader;
587
588 if (loader != null) {
589 loader.cancel();
590 mThumbnailLoader = null;
591 }
592
593 ListView lv;
594 if((lv = getListView()) != null){
595 lv.setAdapter(null);
596 }
597 }
598
599 private void handleMessage(Message message) {
600// Log.v(TAG, "Received message " + message.what);
601
602 switch (message.what) {
603 case MESSAGE_SHOW_DIRECTORY_CONTENTS:
604 showDirectoryContents((DirectoryContents) message.obj);
605 break;
606
607 case MESSAGE_SET_PROGRESS:
608 setProgress(message.arg1, message.arg2);
609 break;
610 }
611 }
612
613 private void setProgress(int progress, int maxProgress) {
614 mProgressBar.setMax(maxProgress);
615 mProgressBar.setProgress(progress);
616 mProgressBar.setVisibility(View.VISIBLE);
617 }
618
619 private void showDirectoryContents(DirectoryContents contents) {
620 mDirectoryScanner = null;
621
622 mListSdCard = contents.listSdCard;
623 mListDir = contents.listDir;
624 mListFile = contents.listFile;
625 mNoMedia = contents.noMedia;
626
627 if(!mRestored){
628 directoryEntries.ensureCapacity(mListSdCard.size() + mListDir.size() + mListFile.size());
629
630 addAllElements(directoryEntries, mListSdCard);
631 addAllElements(directoryEntries, mListDir);
632 addAllElements(directoryEntries, mListFile);
633
634 mDirectoryEntries = directoryEntries.toArray(new IconifiedText[0]);
635 }
636 else {
637 directoryEntries.clear();
638 directoryEntries.ensureCapacity(mDirectoryEntries.length);
639 for(int i = 0; i < mDirectoryEntries.length; i++){
640 directoryEntries.add(mDirectoryEntries[i]);
641 }
642
643 // Once mRestore flag has been used, we should toggle it so that further refreshes don't take it into account
644 mRestored = false;
645 }
646
647 IconifiedTextListAdapter itla = new IconifiedTextListAdapter(this);
648 itla.setListItems(directoryEntries, getListView().hasTextFilter(), currentDirectory, mMimeTypes);
649 setListAdapter(itla);
650 getListView().setTextFilterEnabled(true);
651
652 ThumbnailLoader mThumbnailLoader = ((IconifiedTextListAdapter) getListAdapter()).getThumbnailLoader();
653
654 if(fileDeleted){
655 getListView().setSelection(positionAtDelete);
656 }
657
658 selectInList(mPreviousDirectory);
659 refreshDirectoryPanel();
660 setProgressBarIndeterminateVisibility(false);
661
662 mProgressBar.setVisibility(View.GONE);
663 mEmptyText.setVisibility(View.VISIBLE);
664
665 toggleCheckBoxVisibility(mState == STATE_MULTI_SELECT);
666 }
667
668 private void onCreateDirectoryInput() {
669 mDirectoryInput = (LinearLayout) findViewById(R.id.directory_input);
670 mEditDirectory = (EditText) findViewById(R.id.directory_text);
671
672
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)){
678
679 goToDirectoryInEditText();
680
681 return true;
682 }
683 return false;
684 }
685 });
686
687 mButtonDirectoryPick = (ImageButton) findViewById(R.id.button_directory_pick);
688
689 mButtonDirectoryPick.setOnClickListener(new View.OnClickListener() {
690
691 public void onClick(View arg0) {
692 goToDirectoryInEditText();
693 }
694 });
695 }
696
697 //private boolean mHaveShownErrorMessage;
698 private File mHaveShownErrorMessageForFile = null;
699
700 private void hideKeyboard(IBinder windowToken, int flags){
701 if(mSoftKeyboardAvailable){
702 (new org.openintents.filemanager.compatibility.SoftKeyboard(this))
703 .hideSoftInputFromWindow(windowToken, flags);
704 }
705 }
706
707 private void goToDirectoryInEditText() {
708 File browseto = new File(mEditDirectory.getText().toString());
709
710 /*
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
718 */
719
720 IBinder windowToken = mEditDirectory.getWindowToken();
721
722 if (browseto.equals(currentDirectory)) {
723 showDirectoryInput(false);
724 hideKeyboard(windowToken, 0);
725 } else {
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);
732 } else {
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;
738 }else{
739 showDirectoryInput(false);
740 hideKeyboard(windowToken, 0);
741 }
742 browseTo(browseto);
743 }
744 }
745 }
746
747 /**
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).
752 * @param show
753 */
754 private void showDirectoryInput(boolean show) {
755 if (show) {
756 if (mDirectoryInput == null) {
757 onCreateDirectoryInput();
758 }
759 }
760 if (mDirectoryInput != null) {
761 mDirectoryInput.setVisibility(show ? View.VISIBLE : View.GONE);
762 mDirectoryButtons.setVisibility(show ? View.GONE : View.VISIBLE);
763 }
764
765 refreshDirectoryPanel();
766 }
767
768 /**
769 *
770 */
771 private void refreshDirectoryPanel() {
772 if (isDirectoryInputVisible()) {
773 // Set directory path
774 String path = currentDirectory.getAbsolutePath();
775 mEditDirectory.setText(path);
776
777 // Set selection to last position so user can continue to type:
778 mEditDirectory.setSelection(path.length());
779 } else {
780 setDirectoryButtons();
781 }
782 }
783 /*
784 @Override
785 protected void onResume() {
786 // TODO Auto-generated method stub
787 super.onResume();
788 }
789*/
790
791
792 @Override
793 protected void onSaveInstanceState(Bundle outState) {
794 // TODO Auto-generated method stub
795 super.onSaveInstanceState(outState);
796
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);
805 }
806
807 /**
808 * @return
809 */
810 private boolean isDirectoryInputVisible() {
811 return ((mDirectoryInput != null) && (mDirectoryInput.getVisibility() == View.VISIBLE));
812 }
813
814 private void pickFileOrDirectory() {
815 File file = null;
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;
821 }
822
823 PreferenceActivity.setDefaultPickFilePath(this, currentDirectory.getAbsolutePath());
824
825 Intent intent = getIntent();
826 intent.setData(FileUtils.getUri(file));
827 setResult(RESULT_OK, intent);
828 finish();
829 }
830
831 /**
832 *
833 */
834 private void getMimeTypes() {
835 MimeTypeParser mtp = null;
836 try {
837 mtp = new MimeTypeParser(this, this.getPackageName());
838 } catch (NameNotFoundException e) {
839 //Should never happen
840 }
841
842 XmlResourceParser in = getResources().getXml(R.xml.mimetypes);
843
844 try {
845 mMimeTypes = mtp.fromXmlResource(in);
846 } catch (XmlPullParserException e) {
847 Log
848 .e(
849 TAG,
850 "PreselectedChannelsActivity: XmlPullParserException",
851 e);
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");
858 }
859 }
860
861 /**
862 * This function browses up one level
863 * according to the field: currentDirectory
864 */
865 private void upOneLevel(){
866 if (mStepsBack > 0) {
867 mStepsBack--;
868 }
869 if(currentDirectory.getParent() != null)
870 browseTo(currentDirectory.getParentFile());
871 }
872
873 /**
874 * Jump to some location by clicking on a
875 * directory button.
876 *
877 * This resets the counter for "back" actions.
878 *
879 * @param aDirectory
880 */
881 private void jumpTo(final File aDirectory) {
882 mStepsBack = 0;
883 browseTo(aDirectory);
884 }
885
886 /**
887 * Browse to some location by clicking on a list item.
888 * @param aDirectory
889 */
890 private void browseTo(final File aDirectory){
891 // setTitle(aDirectory.getAbsolutePath());
892
893 if (aDirectory.isDirectory()){
894 if (aDirectory.equals(currentDirectory)) {
895 // Switch from button to directory input
896 showDirectoryInput(true);
897 } else {
898 mPreviousDirectory = currentDirectory;
899 currentDirectory = aDirectory;
900 refreshList();
901// selectInList(previousDirectory);
902 // refreshDirectoryPanel();
903 }
904 }else{
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) {
909 // Pick the file
910 mEditFilename.setText(aDirectory.getName());
911 }
912 }
913 }
914
915
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();
919 return;
920 }
921
922 Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
923
924 Uri data = FileUtils.getUri(aFile);
925 String type = mMimeTypes.getMimeType(aFile.getName());
926 intent.setDataAndType(data, type);
927
928 // Were we in GET_CONTENT mode?
929 Intent originalIntent = getIntent();
930
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);
937 finish();
938 return;
939 }
940
941
942
943 try {
944 startActivity(intent);
945 } catch (ActivityNotFoundException e) {
946 Toast.makeText(this, R.string.application_not_available, Toast.LENGTH_SHORT).show();
947 };
948 }
949
950 public void refreshList() {
951
952 boolean directoriesOnly = mState == STATE_PICK_DIRECTORY;
953
954 // Cancel an existing scanner, if applicable.
955 DirectoryScanner scanner = mDirectoryScanner;
956
957 if (scanner != null) {
958 scanner.cancel = true;
959 }
960
961 ThumbnailLoader loader = mThumbnailLoader;
962
963 if (loader != null) {
964 loader.cancel();
965 mThumbnailLoader = null;
966 }
967
968 directoryEntries.clear();
969 mListDir.clear();
970 mListFile.clear();
971 mListSdCard.clear();
972
973 setProgressBarIndeterminateVisibility(true);
974
975 // Don't show the "folder empty" text since we're scanning.
976 mEmptyText.setVisibility(View.GONE);
977
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);
982
983 mDirectoryScanner = new DirectoryScanner(currentDirectory, this, currentHandler, mMimeTypes, mFilterFiletype, mFilterMimetype, mSdCardPath, mWritableOnly, directoriesOnly);
984 mDirectoryScanner.start();
985
986
987
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'
993 /*
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)));
998 */
999 }
1000
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);
1009 break;
1010 }
1011 }
1012 }
1013
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));
1018 }
1019 }
1020
1021 private void setDirectoryButtons() {
1022 String[] parts = currentDirectory.getAbsolutePath().split("/");
1023
1024 mDirectoryButtons.removeAllViews();
1025
1026 int WRAP_CONTENT = LinearLayout.LayoutParams.WRAP_CONTENT;
1027
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("/"));
1035 }
1036 });
1037 mDirectoryButtons.addView(ib);
1038
1039 // Add other buttons
1040
1041 String dir = "";
1042
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));
1053 }
1054 });
1055 mDirectoryButtons.addView(ib);
1056 } else {
1057 Button b = new Button(this);
1058 b.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
1059 b.setText(parts[i]);
1060 b.setTag(dir);
1061 b.setOnClickListener(new View.OnClickListener() {
1062 public void onClick(View view) {
1063 String dir = (String) view.getTag();
1064 jumpTo(new File(dir));
1065 }
1066 });
1067 mDirectoryButtons.addView(b);
1068 }
1069 }
1070
1071 checkButtonLayout();
1072 }
1073
1074 private void checkButtonLayout() {
1075
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();
1080
1081 int requiredwidth = mDirectoryButtons.getMeasuredWidth();
1082 int width = getWindowManager().getDefaultDisplay().getWidth();
1083
1084 if (requiredwidth > width) {
1085 int WRAP_CONTENT = LinearLayout.LayoutParams.WRAP_CONTENT;
1086
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));
1091 //
1092 ib.setOnClickListener(new View.OnClickListener() {
1093 public void onClick(View view) {
1094 // Up one directory.
1095 upOneLevel();
1096 }
1097 });
1098 mDirectoryButtons.addView(ib, 0);
1099
1100 // New button needs even more space
1101 ib.measure(spec, spec);
1102 requiredwidth += ib.getMeasuredWidth();
1103
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();
1109
1110 mDirectoryButtons.removeViewAt(1);
1111 }
1112 }
1113 }
1114
1115 @Override
1116 protected void onListItemClick(ListView l, View v, int position, long id) {
1117 super.onListItemClick(l, v, position, id);
1118
1119 IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
1120
1121 if (adapter == null) {
1122 return;
1123 }
1124
1125 IconifiedText text = (IconifiedText) adapter.getItem(position);
1126
1127 if (mState == STATE_MULTI_SELECT) {
1128 text.setSelected(!text.isSelected());
1129 adapter.notifyDataSetChanged();
1130 return;
1131 }
1132
1133 String file = text.getText();
1134 /*
1135 if (selectedFileString.equals(getString(R.string.up_one_level))) {
1136 upOneLevel();
1137 } else {
1138 */
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.
1145 mStepsBack++;
1146 }
1147 browseTo(clickedFile);
1148 }
1149 /*
1150 }
1151 */
1152 }
1153
1154 private void getSdCardPath() {
1155 mSdCardPath = android.os.Environment
1156 .getExternalStorageDirectory().getAbsolutePath();
1157 }
1158
1159
1160 @Override
1161 public boolean onCreateOptionsMenu(Menu menu) {
1162 super.onCreateOptionsMenu(menu);
1163
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;
1167 }
1168 MenuItem item = menu.add(0, MENU_NEW_FOLDER, 0, R.string.menu_new_folder).setIcon(
1169 icon).setShortcut('0', 'f');
27a4fda1 1170 MenuCompat.setShowAsAction(item, 0/*MenuItem.SHOW_AS_ACTION_IF_ROOM*/);
811a5a4a 1171
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');
1176 }
1177
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);
1182
1183 menu.add(0, MENU_BOOKMARKS, 0, R.string.bookmarks).setIcon(
1184 R.drawable.ic_menu_star);
1185
1186
1187 menu.add(0, MENU_SETTINGS, 0, R.string.settings).setIcon(
1188 android.R.drawable.ic_menu_preferences).setShortcut('9', 'p');
1189
1190 /* We don't want to allow the user to override a filter set
1191 * by an application.
1192 */
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);
1196 }
1197
1198 menu.add(0, MENU_REFRESH, 0, R.string.menu_refresh).setIcon(
1199 android.R.drawable.ic_menu_rotate);
1200
1201 mDistribution.onCreateOptionsMenu(menu);
1202 return true;
1203 }
1204
1205
1206 @Override
1207 public boolean onPrepareOptionsMenu(Menu menu) {
1208 super.onPrepareOptionsMenu(menu);
1209
1210 mIncludeMediaScanMenuItem.setVisible(false);
1211 mExcludeMediaScanMenuItem.setVisible(false);
1212
1213 boolean showMediaScanMenuItem = PreferenceActivity.getMediaScanFromPreference(this);
1214
1215 // We only know about ".nomedia" once we have the results list back.
1216 if (showMediaScanMenuItem && mListDir != null) {
1217 if (mNoMedia) {
1218 mIncludeMediaScanMenuItem.setVisible(true);
1219 } else {
1220 mExcludeMediaScanMenuItem.setVisible(true);
1221 }
1222 }
1223
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);
1231
1232 // Workaround to add icons:
1233 MenuIntentOptionsWithIcons menu2 = new MenuIntentOptionsWithIcons(this,
1234 menu);
1235 menu2.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
1236 new ComponentName(this, FileManagerActivity.class), null, intent,
1237 0, null);
1238
1239 return true;
1240 }
1241
1242 @Override
1243 public boolean onOptionsItemSelected(MenuItem item) {
1244 switch (item.getItemId()) {
1245 case MENU_NEW_FOLDER:
1246 showDialog(DIALOG_NEW_FOLDER);
1247 return true;
1248
1249 case MENU_MULTI_SELECT:
1250 promptMultiSelect();
1251 return true;
1252
1253 case MENU_INCLUDE_IN_MEDIA_SCAN:
1254 includeInMediaScan();
1255 return true;
1256
1257 case MENU_EXCLUDE_FROM_MEDIA_SCAN:
1258 excludeFromMediaScan();
1259 return true;
1260
1261 case MENU_SETTINGS:
1262 showSettings();
1263 return true;
1264
1265 case MENU_FILTER:
1266 showDialog(DIALOG_FILTER);
1267 return true;
1268
1269 case MENU_BOOKMARKS:
1270 showDialog(DIALOG_BOOKMARKS);
1271 return true;
1272
1273 case MENU_REFRESH:
1274 refreshList();
1275 return true;
1276 }
1277 return super.onOptionsItemSelected(item);
1278
1279 }
1280
1281 private void showSettings() {
1282 Intent intent = new Intent(this, PreferenceActivity.class);
1283 startActivity(intent);
1284 }
1285
1286 @Override
1287 public void onCreateContextMenu(ContextMenu menu, View view,
1288 ContextMenuInfo menuInfo) {
1289 AdapterView.AdapterContextMenuInfo info;
1290 try {
1291 info = (AdapterView.AdapterContextMenuInfo) menuInfo;
1292 } catch (ClassCastException e) {
1293 Log.e(TAG, "bad menuInfo", e);
1294 return;
1295 }
1296/*
1297 Cursor cursor = (Cursor) getListAdapter().getItem(info.position);
1298 if (cursor == null) {
1299 // For some reason the requested item isn't available, do nothing
1300 return;
1301 }
1302*/
1303 IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
1304
1305 if (adapter == null) {
1306 return;
1307 }
1308
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());
1313
1314
1315 if (!file.isDirectory()) {
1316 if (mState == STATE_PICK_FILE) {
1317 // Show "open" menu
1318 menu.add(0, MENU_OPEN, 0, R.string.menu_open);
1319 }
1320 menu.add(0, MENU_SEND, 0, R.string.menu_send);
1321 }
1322 menu.add(0, MENU_MOVE, 0, R.string.menu_move);
1323
1324 if (!file.isDirectory()) {
1325 menu.add(0, MENU_COPY, 0, R.string.menu_copy);
1326 }
1327
1328 menu.add(0, MENU_RENAME, 0, R.string.menu_rename);
1329 menu.add(0, MENU_DELETE, 0, R.string.menu_delete);
1330
1331 //if (!file.isDirectory()) {
1332 Uri data = Uri.fromFile(file);
1333 Intent intent = new Intent(null, data);
1334 String type = mMimeTypes.getMimeType(file.getName());
1335
1336 intent.setDataAndType(data, type);
1337 intent.addCategory(Intent.CATEGORY_SELECTED_ALTERNATIVE);
1338 //intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
1339
1340 Log.v(TAG, "Data=" + data);
1341 Log.v(TAG, "Type=" + type);
1342
1343 if (type != null) {
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);
1347 }
1348 //}
1349
1350 if (FileUtils.checkIfZipArchive(file)){
1351 menu.add(0, MENU_EXTRACT, 0, R.string.menu_extract);
1352 } else {
1353 menu.add(0, MENU_COMPRESS, 0, R.string.menu_compress);
1354 }
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);
1358 }
1359
1360 @Override
1361 public boolean onContextItemSelected(MenuItem item) {
1362 super.onContextItemSelected(item);
1363 AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item
1364 .getMenuInfo();
1365
1366 // Remember current selection
1367 IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
1368
1369 if (adapter == null) {
1370 return false;
1371 }
1372
1373 IconifiedText ic = (IconifiedText) adapter.getItem(menuInfo.position);
1374 mContextText = ic.getText();
1375 mContextIcon = ic.getIcon();
1376 mContextFile = FileUtils.getFile(currentDirectory, ic.getText());
1377
1378 switch (item.getItemId()) {
1379 case MENU_OPEN:
1380 openFile(mContextFile);
1381 return true;
1382
1383 case MENU_MOVE:
1384 promptDestinationAndMoveFile();
1385 return true;
1386
1387 case MENU_COPY:
1388 promptDestinationAndCopyFile();
1389 return true;
1390
1391 case MENU_DELETE:
1392 showDialog(DIALOG_DELETE);
1393 return true;
1394
1395 case MENU_RENAME:
1396 showDialog(DIALOG_RENAME);
1397 return true;
1398
1399 case MENU_SEND:
1400 sendFile(mContextFile);
1401 return true;
1402
1403 case MENU_DETAILS:
1404 showDialog(DIALOG_DETAILS);
1405 return true;
1406
1407 case MENU_COMPRESS:
1408 showDialog(DIALOG_COMPRESSING);
1409 return true;
1410
1411 case MENU_EXTRACT:
1412 promptDestinationAndExtract();
1413 return true;
1414
1415 case MENU_BOOKMARK:
1416 String path = mContextFile.getAbsolutePath();
1417 Cursor query = managedQuery(BookmarksProvider.CONTENT_URI,
1418 new String[]{BookmarksProvider._ID},
1419 BookmarksProvider.PATH + "=?",
1420 new String[]{path},
1421 null);
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();
1428 }
1429 else{
1430 Toast.makeText(this, R.string.bookmark_already_exists, Toast.LENGTH_SHORT).show();
1431 }
1432 return true;
1433
1434 case MENU_MORE:
1435 if (!PreferenceActivity.getShowAllWarning(FileManagerActivity.this)) {
1436 showMoreCommandsDialog();
1437 return true;
1438 }
1439
1440 showWarningDialog();
1441
1442 return true;
1443 }
1444
1445 return false;
1446 }
1447
1448 @Override
1449 protected Dialog onCreateDialog(int id) {
1450
1451 switch (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);
1457 et.setText("");
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);
1465 }
1466 return true;
1467 }
1468
1469 };
1470 et.setOnEditorActionListener(returnListener);
1471 //end of code regarding "return key"
1472
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() {
1477
1478 public void onClick(DialogInterface dialog, int which) {
1479 createNewFolder(et.getText().toString());
1480 }
1481
1482 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1483
1484 public void onClick(DialogInterface dialog, int which) {
1485 // Cancel should not do anything.
1486 }
1487
1488 }).create();
1489
1490
1491 case DIALOG_DELETE:
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() {
1495
1496 public void onClick(DialogInterface dialog, int which) {
1497 deleteFileOrFolder(mContextFile);
1498 }
1499
1500 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1501
1502 public void onClick(DialogInterface dialog, int which) {
1503 // Cancel should not do anything.
1504 }
1505
1506 }).create();
1507
1508 case DIALOG_RENAME:
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);
1520 }
1521 return true;
1522 }
1523
1524 };
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() {
1530
1531 public void onClick(DialogInterface dialog, int which) {
1532
1533 renameFileOrFolder(mContextFile, et2.getText().toString());
1534 }
1535
1536 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1537
1538 public void onClick(DialogInterface dialog, int which) {
1539 // Cancel should not do anything.
1540 }
1541
1542 }).create();
1543
1544 case DIALOG_MULTI_DELETE:
1545 String contentText = null;
1546 int count = 0;
1547 for (IconifiedText it : mDirectoryEntries) {
1548 if (!it.isSelected()) {
1549 continue;
1550 }
1551
1552 contentText = it.getText();
1553 count++;
1554 }
1555 String string;
1556 if (count == 1) {
1557 string = getString(R.string.really_delete, contentText);
1558 } else {
1559 string = getString(R.string.really_delete_multiselect, count);
1560 }
1561 return new AlertDialog.Builder(this).setTitle(string)
1562 .setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(
1563 android.R.string.ok, new OnClickListener() {
1564
1565 public void onClick(DialogInterface dialog, int which) {
1566 deleteMultiFile();
1567
1568 Intent intent = getIntent();
1569 setResult(RESULT_OK, intent);
1570 finish();
1571 }
1572
1573 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1574
1575 public void onClick(DialogInterface dialog, int which) {
1576 // Cancel should not do anything.
1577 }
1578
1579 }).create();
1580
1581 case DIALOG_FILTER:
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);
1587 et3.setText("");
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() {
1592
1593 public void onClick(DialogInterface dialog, int which) {
1594 mFilterFiletype = et3.getText().toString().trim();
1595 refreshList();
1596 }
1597
1598 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1599
1600 public void onClick(DialogInterface dialog, int which) {
1601 // Cancel should not do anything.
1602 }
1603
1604 }).create();
1605
1606
1607 case DIALOG_DETAILS:
1608 inflater = LayoutInflater.from(this);
1609 view = inflater.inflate(R.layout.dialog_details, null);
1610
1611 return new AlertDialog.Builder(this).setTitle(mContextText).
1612 setIcon(mContextIcon).setView(view).create();
1613
1614 case DIALOG_BOOKMARKS:
1615 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1616
1617 final Cursor bookmarksCursor = getBookmarks();
1618
1619 builder.setTitle(R.string.bookmarks);
1620
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);
1627 if (file != null) {
1628 if (file.isDirectory()) {
1629 mStepsBack++;
1630 }
1631 browseTo(file);
1632 }
1633 } else{
1634 Toast.makeText(FileManagerActivity.this, R.string.bookmark_not_found,
1635 Toast.LENGTH_SHORT).show();
1636 }
1637 }
1638 }, BookmarksProvider.NAME);
1639
1640 return builder.create();
1641
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);
1654 } else {
1655 new CompressManager(FileManagerActivity.this).compress(mContextFile, editText.getText().toString());
1656 } //match this behavior to your OK button
1657 dismissDialog(DIALOG_COMPRESSING);
1658 }
1659 return true;
1660 }
1661
1662 };
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);
1672 } else {
1673 new CompressManager(FileManagerActivity.this).compress(mContextFile, editText.getText().toString());
1674 }
1675 }
1676 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1677 public void onClick(DialogInterface dialog, int which) {
1678 // Cancel should not do anything.
1679 }
1680 }).create();
1681
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);
1695 } else {
1696 compressMultiFile(editText1.getText().toString());
1697 } //match this behavior to your OK button
1698 dismissDialog(DIALOG_MULTI_COMPRESS_ZIP);
1699 }
1700 return true;
1701 }
1702
1703 };
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);
1714 } else {
1715 compressMultiFile(editText1.getText().toString());
1716 }
1717 }
1718 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1719 public void onClick(DialogInterface dialog, int which) {
1720 // Cancel should not do anything.
1721 }
1722 }).create();
1723
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);
1734 } else {
1735 new File(mContextFile.getParent()+File.separator+mDialogArgument).delete();
1736 new CompressManager(FileManagerActivity.this).compress(mContextFile, mDialogArgument);
1737 }
1738 mDialogExistsAction = "";
1739 }
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);
1747 } else {
1748 showDialog(DIALOG_COMPRESSING);
1749 }
1750 mDialogExistsAction = "";
1751 }
1752 }).create();
1753
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);
1763 } else {
1764 rename(FileUtils.getFile(currentDirectory, mOldFileName), newFile);
1765 }
1766 }
1767 }).setNegativeButton(android.R.string.cancel, new OnClickListener() {
1768 public void onClick(DialogInterface dialog, int which) {
1769 mContextText = mOldFileName;
1770 showDialog(DIALOG_RENAME);
1771 }
1772 }).create();
1773 }
1774 return super.onCreateDialog(id);
1775
1776 }
1777
1778 private Cursor getBookmarks(){
1779 return managedQuery(BookmarksProvider.CONTENT_URI,
1780 new String[] {
1781 BookmarksProvider._ID,
1782 BookmarksProvider.NAME,
1783 BookmarksProvider.PATH,
1784 }, null, null, null);
1785 }
1786
1787
1788 @Override
1789 protected void onPrepareDialog(int id, Dialog dialog) {
1790 super.onPrepareDialog(id, dialog);
1791
1792 switch (id) {
1793 case DIALOG_NEW_FOLDER:
1794 EditText et = (EditText) dialog.findViewById(R.id.foldername);
1795 et.setText("");
1796 break;
1797
1798 case DIALOG_DELETE:
1799 ((AlertDialog) dialog).setTitle(getString(R.string.really_delete, mContextText));
1800 break;
1801
1802 case DIALOG_RENAME:
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);
1808 } else {
1809 tv.setText(R.string.file_name);
1810 }
1811 et.setSelection(0, mContextText.lastIndexOf(".") == -1 ? mContextText.length() : mContextText.lastIndexOf("."));
1812 ((AlertDialog) dialog).setIcon(mContextIcon);
1813 break;
1814
1815 case DIALOG_MULTI_DELETE:
1816 break;
1817
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)));
1823
1824 final TextView size = ((TextView)dialog.findViewById(R.id.details_size_value));
1825 size.setText(FileUtils.formatSize(this, mContextFile.length()));
1826
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>(){
1831
1832 protected long totalSize = 0L;
1833
1834 @Override
1835 protected Long doInBackground(File... file) {
1836 sizeOf(file[0]);
1837 return totalSize;
1838 }
1839
1840 @Override
1841 protected void onProgressUpdate(Long... updatedSize){
1842 size.setText(FileUtils.formatSize(size.getContext(), updatedSize[0]));
1843 }
1844
1845 @Override
1846 protected void onPostExecute(Long result){
1847 size.setText(FileUtils.formatSize(size.getContext(), result));
1848 }
1849
1850 private void sizeOf(File file){
1851 if(file.isFile()){
1852 totalSize += file.length();
1853 publishProgress(totalSize);
1854 } else {
1855 File[] files = file.listFiles();
1856
1857 if(files != null && files.length != 0){
1858 for(File subFile : files){
1859 sizeOf(subFile);
1860 }
1861 }
1862 }
1863 }
1864 }.execute(mContextFile);
1865
1866 ((AlertDialog) dialog).setOnCancelListener(new OnCancelListener(){
1867 @Override
1868 public void onCancel(DialogInterface dialog) {
1869 folderSizeTask.cancel(true);
1870 }
1871 });
1872 }
1873
1874 String perms = (mContextFile.canRead() ? "R" : "-") +
1875 (mContextFile.canWrite() ? "W" : "-") +
1876 (FileUtils.canExecute(mContextFile) ? "X" : "-");
1877
1878 final TextView permissions = ((TextView)dialog.findViewById(R.id.details_permissions_value));
1879 permissions.setText(perms);
1880
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);
1883
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);
1888 break;
1889
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";
1897 } else {
1898 String extension = FileUtils.getExtension(mContextFile.getName());
1899 archiveName = mContextFile.getName().replaceAll(extension, "")+".zip";
1900 }
1901 editText.setText(archiveName);
1902 editText.setSelection(0, archiveName.length()-4);
1903 break;
1904
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);
1912 break;
1913
1914 case DIALOG_WARNING_EXISTS:
1915 dialog.setTitle(getString(R.string.warning_overwrite, mDialogArgument));
1916 }
1917 }
1918
1919 /**
1920 * @since 2011-09-30
1921 */
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);
1926
1927 showWarningAgain.setChecked(PreferenceActivity.getShowAllWarning(FileManagerActivity.this));
1928
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() {
1933
1934 public void onClick(DialogInterface dialog, int which) {
1935 PreferenceActivity.setShowAllWarning(FileManagerActivity.this, showWarningAgain.isChecked());
1936
1937 showMoreCommandsDialog();
1938 }
1939
1940 }).create()
1941 .show();
1942 }
1943
1944 /**
1945 * @since 2011-09-30
1946 */
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());
1951
1952 intent.setDataAndType(data, type);
1953
1954 Log.v(TAG, "Data=" + data);
1955 Log.v(TAG, "Type=" + type);
1956
1957 if (type != null) {
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),
1962 null, intent, 0);
1963 final int N = lri != null ? lri.size() : 0;
1964
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.
1970 */
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(
1976 new ComponentName(
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));
1984 else
1985 toRemove.add(ri);
1986 }
1987
1988 for(ResolveInfo ri : toRemove){
1989 lri.remove(ri);
1990 }
1991
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);
2004 }
2005 }).create()
2006 .show();
2007 }
2008 }
2009
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();
2015 mNoMedia = false;
2016 } else {
2017 // That didn't work.
2018 Toast.makeText(this, getString(R.string.error_generic), Toast.LENGTH_LONG).show();
2019 }
2020 }
2021
2022 private void excludeFromMediaScan() {
2023 // Create the .nomedia file.
2024 File file = FileUtils.getFile(currentDirectory, NOMEDIA_FILE);
2025 try {
2026 if (file.createNewFile()) {
2027 mNoMedia = true;
2028 Toast.makeText(this, getString(R.string.media_scan_excluded), Toast.LENGTH_LONG).show();
2029 } else {
2030 Toast.makeText(this, getString(R.string.error_media_scan), Toast.LENGTH_LONG).show();
2031 }
2032 } catch (IOException e) {
2033 // That didn't work.
2034 Toast.makeText(this, getString(R.string.error_generic) + e.getMessage(), Toast.LENGTH_LONG).show();
2035 }
2036 }
2037
2038 private boolean checkSelection() {
2039 for (IconifiedText it : mDirectoryEntries) {
2040 if (!it.isSelected()) {
2041 continue;
2042 }
2043
2044 return true;
2045 }
2046
2047 Toast.makeText(this, R.string.error_selection, Toast.LENGTH_SHORT).show();
2048
2049 return false;
2050 }
2051
2052 private void toggleSelection(boolean selected) {
2053 for(IconifiedText it : mDirectoryEntries){
2054 it.setSelected(selected);
2055 }
2056
2057 ((BaseAdapter) getListAdapter()).notifyDataSetChanged();
2058 }
2059
2060 private void toggleCheckBoxVisibility(boolean visible) {
2061 for(IconifiedText it : mDirectoryEntries){
2062 it.setCheckIconVisible(visible);
2063 }
2064
2065 ((BaseAdapter) getListAdapter()).notifyDataSetChanged();
2066 }
2067
2068 private void promptDestinationAndMoveFile() {
2069
2070 Intent intent = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY);
2071
2072 intent.setData(FileUtils.getUri(currentDirectory));
2073
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);
2077
2078 startActivityForResult(intent, REQUEST_CODE_MOVE);
2079 }
2080
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);
2088 }
2089
2090 private void promptDestinationAndCopyFile() {
2091
2092 Intent intent = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY);
2093
2094 intent.setData(FileUtils.getUri(currentDirectory));
2095
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);
2099
2100 startActivityForResult(intent, REQUEST_CODE_COPY);
2101 }
2102
2103 /**
2104 * Starts activity for multi select.
2105 */
2106 private void promptMultiSelect() {
2107 Intent intent = new Intent(FileManagerIntents.ACTION_MULTI_SELECT);
2108
2109 intent.setData(FileUtils.getUri(currentDirectory));
2110
2111 intent.putExtra(FileManagerIntents.EXTRA_TITLE, getString(R.string.multiselect_title));
2112 //intent.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, getString(R.string.move_button));
2113
2114 startActivityForResult(intent, REQUEST_CODE_MULTI_SELECT);
2115 }
2116
2117 private void createNewFolder(String foldername) {
2118 if (!TextUtils.isEmpty(foldername)) {
2119 File file = FileUtils.getFile(currentDirectory, foldername);
2120 if (file.mkdirs()) {
2121
2122 // Change into new directory:
2123 browseTo(file);
2124 } else {
2125 Toast.makeText(this, R.string.error_creating_new_folder, Toast.LENGTH_SHORT).show();
2126 }
2127 }
2128 }
2129
2130 private void compressMultiFile(String out) {
2131 List<File> files = new ArrayList<File>();
2132 for (IconifiedText it : mDirectoryEntries) {
2133 if (!it.isSelected()) {
2134 continue;
2135 }
2136
2137 File file = FileUtils.getFile(currentDirectory, it.getText());
2138 files.add(file);
2139 }
2140 new CompressManager(FileManagerActivity.this).compress(files, out);
2141 }
2142
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.
2146 */
2147 private boolean recursiveDelete(File file, boolean toastOnError) {
2148 // Recursively delete all contents.
2149 File[] files = file.listFiles();
2150
2151 if (files == null) {
2152 Toast.makeText(this, getString(R.string.error_deleting_folder, file.getAbsolutePath()), Toast.LENGTH_LONG);
2153 return false;
2154 }
2155
2156 for (int x=0; x<files.length; x++) {
2157 File childFile = files[x];
2158 if (childFile.isDirectory()) {
2159 if (!recursiveDelete(childFile, toastOnError)) {
2160 return false;
2161 }
2162 } else {
2163 if (!childFile.delete()) {
2164 Toast.makeText(this, getString(R.string.error_deleting_child_file, childFile.getAbsolutePath()), Toast.LENGTH_LONG);
2165 return false;
2166 }
2167 }
2168 }
2169
2170 if (!file.delete()) {
2171 Toast.makeText(this, getString(R.string.error_deleting_folder, file.getAbsolutePath()), Toast.LENGTH_LONG);
2172 return false;
2173 }
2174
2175 return true;
2176 }
2177
2178 private class RecursiveDeleteTask extends AsyncTask<Object, Void, Integer> {
2179
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;
2185
2186 private File errorFile;
2187
2188 /**
2189 * Recursively delete a file or directory and all of its children.
2190 *
2191 * @returns 0 if successful, error value otherwise.
2192 */
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);
2198 if (result > 0) {
2199 return result;
2200 }
2201 } else {
2202 if (!childFile.delete()) {
2203 errorFile = childFile;
2204 return err_deleting_child_file;
2205 }
2206 }
2207 }
2208
2209 if (!file.delete()) {
2210 errorFile = file;
2211 return file.isFile() ? err_deleting_file : err_deleting_folder;
2212 }
2213
2214 return success;
2215 }
2216
2217 @Override
2218 protected void onPreExecute() {
2219 Toast.makeText(activity, R.string.deleting_files, Toast.LENGTH_SHORT).show();
2220 }
2221
2222 @SuppressWarnings("unchecked")
2223 @Override
2224 protected Integer doInBackground(Object... params) {
2225 Object files = params[0];
2226
2227 if (files instanceof List<?>) {
2228 for (File file: (List<File>)files) {
2229 int result = recursiveDelete(file);
2230 if (result != success) return result;
2231 }
2232 return success;
2233 } else
2234 return recursiveDelete((File)files);
2235
2236 }
2237
2238 @Override
2239 protected void onPostExecute(Integer result) {
2240 switch (result) {
2241 case success:
2242 activity.refreshList();
2243 if(deletedFileIsDirectory){
2244 Toast.makeText(activity, R.string.folder_deleted,Toast.LENGTH_SHORT).show();
2245 } else {
2246 Toast.makeText(activity, R.string.file_deleted,Toast.LENGTH_SHORT).show();
2247 }
2248 break;
2249 case err_deleting_folder:
2250 Toast.makeText(activity,getString(R.string.error_deleting_folder,
2251 errorFile.getAbsolutePath()), Toast.LENGTH_LONG).show();
2252 break;
2253 case err_deleting_child_file:
2254 Toast.makeText(activity,getString(R.string.error_deleting_child_file,
2255 errorFile.getAbsolutePath()),Toast.LENGTH_SHORT).show();
2256 break;
2257 case err_deleting_file:
2258 Toast.makeText(activity,getString(R.string.error_deleting_file,
2259 errorFile.getAbsolutePath()), Toast.LENGTH_LONG).show();
2260 break;
2261 }
2262 }
2263
2264 }
2265
2266 private void deleteFileOrFolder(File file) {
2267 fileDeleted = true;
2268 positionAtDelete = getListView().getFirstVisiblePosition();
2269 deletedFileIsDirectory = file.isDirectory();
2270 new RecursiveDeleteTask().execute(file);
2271// if (file.isDirectory()) {
2272// if (recursiveDelete(file, true)) {
2273// refreshList();
2274// Toast.makeText(this, R.string.folder_deleted, Toast.LENGTH_SHORT).show();
2275// }
2276// } else {
2277// if (file.delete()) {
2278// // Delete was successful.
2279// refreshList();
2280// Toast.makeText(this, R.string.file_deleted, Toast.LENGTH_SHORT).show();
2281// } else {
2282// Toast.makeText(this, R.string.error_deleting_file, Toast.LENGTH_SHORT).show();
2283// }
2284// }
2285 }
2286
2287 private void deleteMultiFile() {
2288// int toast = 0;
2289 LinkedList<File> files = new LinkedList<File>();
2290 for (IconifiedText it : mDirectoryEntries) {
2291 if (!it.isSelected()) {
2292 continue;
2293 }
2294
2295 File file = FileUtils.getFile(currentDirectory, it.getText());
2296 files.add(file);
2297// if (file.isDirectory()) {
2298// if (!recursiveDelete(file, true)) {
2299// break;
2300// }
2301// } else {
2302// if (!file.delete()) {
2303// toast = R.string.error_deleting_file;
2304// break;
2305// }
2306// }
2307 }
2308
2309 new RecursiveDeleteTask().execute(files);
2310
2311// if (toast == 0) {
2312// // Delete was successful.
2313// refreshList();
2314// toast = R.string.file_deleted;
2315// }
2316//
2317// Toast.makeText(FileManagerActivity.this, toast, Toast.LENGTH_SHORT).show();
2318 }
2319
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);
2327 return;
2328 }
2329 }
2330 File newFile = FileUtils.getFile(currentDirectory, newFileName);
2331 if (newFile.exists()){
2332 mDialogExistsAction = DIALOG_EXISTS_ACTION_RENAME;
2333 showDialog(DIALOG_WARNING_EXISTS);
2334 } else {
2335 rename(file, newFile);
2336 }
2337 }
2338
2339 /**
2340 * @param oldFile
2341 * @param newFile
2342 */
2343 private void rename(File oldFile, File newFile) {
2344 int toast = 0;
2345 if (oldFile.renameTo(newFile)) {
2346 // Rename was successful.
2347 refreshList();
2348 if (newFile.isDirectory()) {
2349 toast = R.string.folder_renamed;
2350 } else {
2351 toast = R.string.file_renamed;
2352 }
2353 } else {
2354 if (newFile.isDirectory()) {
2355 toast = R.string.error_renaming_folder;
2356 } else {
2357 toast = R.string.error_renaming_file;
2358 }
2359 }
2360 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2361 }
2362
2363 /*@ RETURNS: A file name that is guaranteed to not exist yet.
2364 *
2365 * PARAMS:
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.
2370 *
2371 */
2372 private File createUniqueCopyName(Context context, File path, String fileName) {
2373 // Does that file exist?
2374 File file = FileUtils.getFile(path, fileName);
2375
2376 if (!file.exists()) {
2377 // Nope - we can take that.
2378 return file;
2379 }
2380
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);
2387 }
2388
2389 // Try a simple "copy of".
2390 file = FileUtils.getFile(path, context.getString(R.string.copied_file_name, fileName).concat(extension));
2391
2392 if (!file.exists()) {
2393 // Nope - we can take that.
2394 return file;
2395 }
2396
2397 int copyIndex = 2;
2398
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));
2402
2403 if (!file.exists()) {
2404 // Nope - we can take that.
2405 return file;
2406 }
2407
2408 copyIndex++;
2409 }
2410
2411 // I GIVE UP.
2412 return null;
2413 }
2414
2415 private boolean copy(File oldFile, File newFile) {
2416 try {
2417 FileInputStream input = new FileInputStream(oldFile);
2418 FileOutputStream output = new FileOutputStream(newFile);
2419
2420 byte[] buffer = new byte[COPY_BUFFER_SIZE];
2421
2422 while (true) {
2423 int bytes = input.read(buffer);
2424
2425 if (bytes <= 0) {
2426 break;
2427 }
2428
2429 output.write(buffer, 0, bytes);
2430 }
2431
2432 output.close();
2433 input.close();
2434
2435 } catch (Exception e) {
2436 return false;
2437 }
2438 return true;
2439 }
2440
2441 private void sendFile(File file) {
2442
2443 String filename = file.getName();
2444 String content = "hh";
2445
2446 Log.i(TAG, "Title to send: " + filename);
2447 Log.i(TAG, "Content to send: " + content);
2448
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()));
2455
2456 i = Intent.createChooser(i, getString(R.string.menu_send));
2457
2458 try {
2459 startActivity(i);
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");
2464 }
2465 }
2466
2467 // This code seems to work for SDK 2.3 (target="9")
2468 @Override
2469 public boolean onKeyDown(int keyCode, KeyEvent event) {
2470
2471 if (keyCode == KeyEvent.KEYCODE_BACK) {
2472 if (mStepsBack > 0) {
2473 upOneLevel();
2474 return true;
2475 }
2476 }
2477
2478 return super.onKeyDown(keyCode, event);
2479 }
2480
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 )
2483
2484 /*
2485 //@Override
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.
2492 onBackPressed();
2493 }
2494
2495 return super.onKeyDown(keyCode, event);
2496 }
2497
2498 //@Override
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
2502 // platform.
2503 if (mStepsBack > 0) {
2504 upOneLevel();
2505 } else {
2506 finish();
2507 }
2508 }
2509 */
2510
2511 /**
2512 * This is called after the file manager finished.
2513 */
2514 @Override
2515 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
2516 super.onActivityResult(requestCode, resultCode, data);
2517
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());
2528 int toast = 0;
2529 if (movefrom.renameTo(moveto)) {
2530 // Move was successful.
2531 refreshList();
2532 if (moveto.isDirectory()) {
2533 toast = R.string.folder_moved;
2534 } else {
2535 toast = R.string.file_moved;
2536 }
2537 } else {
2538 if (moveto.isDirectory()) {
2539 toast = R.string.error_moving_folder;
2540 } else {
2541 toast = R.string.error_moving_file;
2542 }
2543 }
2544 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2545 } else {
2546 // Move multi file.
2547 int toast = 0;
2548 for (IconifiedText it : mDirectoryEntries) {
2549 if (!it.isSelected()) {
2550 continue;
2551 }
2552
2553 movefrom = FileUtils.getFile(currentDirectory, it.getText());
2554 File newPath = FileUtils.getFile(moveto, movefrom.getName());
2555 if (!movefrom.renameTo(newPath)) {
2556 refreshList();
2557 if (moveto.isDirectory()) {
2558 toast = R.string.error_moving_folder;
2559 } else {
2560 toast = R.string.error_moving_file;
2561 }
2562 break;
2563 }
2564 }
2565
2566 if (toast == 0) {
2567 // Move was successful.
2568 refreshList();
2569 toast = R.string.file_moved;
2570 }
2571
2572 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2573
2574 Intent intent = getIntent();
2575 setResult(RESULT_OK, intent);
2576 finish();
2577 }
2578
2579 }
2580
2581 }
2582 break;
2583
2584 case REQUEST_CODE_EXTRACT:
2585 if (resultCode == RESULT_OK && data != null) {
2586 new ExtractManager(this).extract(mContextFile, data.getData().getPath());
2587 }
2588 break;
2589
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());
2599
2600 if (copyto != null) {
2601 int toast = 0;
2602 if (copy(copyfrom, copyto)) {
2603 toast = R.string.file_copied;
2604 refreshList();
2605 } else {
2606 toast = R.string.error_copying_file;
2607 }
2608 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2609 }
2610 } else {
2611 // Copy multi file.
2612 int toast = 0;
2613 for (IconifiedText it : mDirectoryEntries) {
2614 if (!it.isSelected()) {
2615 continue;
2616 }
2617
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;
2623 break;
2624 }
2625 }
2626 }
2627
2628 if (toast == 0) {
2629 // Copy was successful.
2630 toast = R.string.file_copied;
2631 refreshList();
2632 }
2633
2634 Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
2635
2636 Intent intent = getIntent();
2637 setResult(RESULT_OK, intent);
2638 finish();
2639 }
2640 }
2641 }
2642 break;
2643
2644 case REQUEST_CODE_MULTI_SELECT:
2645 if (resultCode == RESULT_OK && data != null) {
2646 refreshList();
2647 }
2648 break;
2649 }
2650
2651 }
2652
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)){
2660
2661 refreshList();
2662 }
2663 }
2664
2665
2666}