/*
* Copyright (C) 2008 OpenIntents.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Based on AndDev.org's file browser V 2.0.
*/
package org.openintents.filemanager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilePermission;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.linnaeus.activity.R;
import org.openintents.filemanager.util.FileUtils;
import org.openintents.filemanager.util.MimeTypeParser;
import org.openintents.filemanager.util.MimeTypes;
import org.openintents.intents.FileManagerIntents;
import org.openintents.util.MenuIntentOptionsWithIcons;
import org.xmlpull.v1.XmlPullParserException;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Contacts.Intents;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
public class FileManagerActivity extends ListActivity {
private static final String TAG = "FileManagerActivity";
private int mState;
private static final int STATE_BROWSE = 1;
private static final int STATE_PICK_FILE = 2;
private static final int STATE_PICK_DIRECTORY = 3;
protected static final int REQUEST_CODE_MOVE = 1;
protected static final int REQUEST_CODE_COPY = 2;
private static final int MENU_ABOUT = Menu.FIRST + 1;
private static final int MENU_UPDATE = Menu.FIRST + 2;
private static final int MENU_PREFERENCES = Menu.FIRST + 3;
private static final int MENU_NEW_FOLDER = Menu.FIRST + 4;
private static final int MENU_DELETE = Menu.FIRST + 5;
private static final int MENU_RENAME = Menu.FIRST + 6;
private static final int MENU_SEND = Menu.FIRST + 7;
private static final int MENU_OPEN = Menu.FIRST + 8;
private static final int MENU_MOVE = Menu.FIRST + 9;
private static final int MENU_COPY = Menu.FIRST + 10;
private static final int DIALOG_NEW_FOLDER = 1;
private static final int DIALOG_DELETE = 2;
private static final int DIALOG_RENAME = 3;
private static final int DIALOG_ABOUT = 4;
private static final int COPY_BUFFER_SIZE = 32 * 1024;
private static final String BUNDLE_CURRENT_DIRECTORY = "current_directory";
private static final String BUNDLE_CONTEXT_FILE = "context_file";
private static final String BUNDLE_CONTEXT_TEXT = "context_text";
private static final String BUNDLE_SHOW_DIRECTORY_INPUT = "show_directory_input";
private static final String BUNDLE_STEPS_BACK = "steps_back";
/** Contains directories and files together */
private ArrayList<IconifiedText> directoryEntries = new ArrayList<IconifiedText>();
/** Dir separate for sorting */
List<IconifiedText> mListDir = new ArrayList<IconifiedText>();
/** Files separate for sorting */
List<IconifiedText> mListFile = new ArrayList<IconifiedText>();
/** SD card separate for sorting */
List<IconifiedText> mListSdCard = new ArrayList<IconifiedText>();
private File currentDirectory = new File("");
private String mSdCardPath = "";
private MimeTypes mMimeTypes;
private String mContextText;
private File mContextFile = new File("");
private Drawable mContextIcon;
/** How many steps one can make back using the back key. */
private int mStepsBack;
private EditText mEditFilename;
private Button mButtonPick;
private LinearLayout mDirectoryButtons;
private LinearLayout mDirectoryInput;
private EditText mEditDirectory;
private ImageButton mButtonDirectoryPick;
private TextView mEmptyText;
private ProgressBar mProgressBar;
private DirectoryScanner mDirectoryScanner;
private File mPreviousDirectory;
private ThumbnailLoader mThumbnailLoader;
private Handler currentHandler;
static final public int MESSAGE_SHOW_DIRECTORY_CONTENTS = 500; // List of contents is ready, obj = DirectoryContents
static final public int MESSAGE_SET_PROGRESS = 501; // Set progress bar, arg1 = current value, arg2 = max value
static final public int MESSAGE_ICON_CHANGED = 502; // View needs to be redrawn, obj = IconifiedText
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
currentHandler = new Handler() {
public void handleMessage(Message msg) {
FileManagerActivity.this.handleMessage(msg);
}
};
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.filelist);
mEmptyText = (TextView) findViewById(R.id.empty_text);
mProgressBar = (ProgressBar) findViewById(R.id.scan_progress);
getListView().setOnCreateContextMenuListener(this);
getListView().setEmptyView(findViewById(R.id.empty));
getListView().setTextFilterEnabled(true);
getListView().requestFocus();
getListView().requestFocusFromTouch();
mDirectoryButtons = (LinearLayout) findViewById(R.id.directory_buttons);
mEditFilename = (EditText) findViewById(R.id.filename);
mButtonPick = (Button) findViewById(R.id.button_pick);
mButtonPick.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
pickFileOrDirectory();
}
});
// Initialize only when necessary:
mDirectoryInput = null;
// Create map of extensions:
getMimeTypes();
getSdCardPath();
mState = STATE_BROWSE;
Intent intent = getIntent();
String action = intent.getAction();
File browseto = new File("/");
if (!TextUtils.isEmpty(mSdCardPath)) {
browseto = new File(mSdCardPath);
}
// Default state
mState = STATE_BROWSE;
if (action != null) {
if (action.equals(FileManagerIntents.ACTION_PICK_FILE)) {
mState = STATE_PICK_FILE;
} else if (action.equals(FileManagerIntents.ACTION_PICK_DIRECTORY)) {
mState = STATE_PICK_DIRECTORY;
// Remove edit text and make button fill whole line
mEditFilename.setVisibility(View.GONE);
mButtonPick.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
}
}
if (mState == STATE_BROWSE) {
// Remove edit text and button.
mEditFilename.setVisibility(View.GONE);
mButtonPick.setVisibility(View.GONE);
}
// Set current directory and file based on intent data.
File file = FileUtils.getFile(intent.getData());
if (file != null) {
File dir = FileUtils.getPathWithoutFilename(file);
if (dir.isDirectory()) {
browseto = dir;
}
if (!file.isDirectory()) {
mEditFilename.setText(file.getName());
}
}
String title = intent.getStringExtra(FileManagerIntents.EXTRA_TITLE);
if (title != null) {
setTitle(title);
}
String buttontext = intent.getStringExtra(FileManagerIntents.EXTRA_BUTTON_TEXT);
if (buttontext != null) {
mButtonPick.setText(buttontext);
}
mStepsBack = 0;
if (icicle != null) {
browseto = new File(icicle.getString(BUNDLE_CURRENT_DIRECTORY));
mContextFile = new File(icicle.getString(BUNDLE_CONTEXT_FILE));
mContextText = icicle.getString(BUNDLE_CONTEXT_TEXT);
boolean show = icicle.getBoolean(BUNDLE_SHOW_DIRECTORY_INPUT);
showDirectoryInput(show);
mStepsBack = icicle.getInt(BUNDLE_STEPS_BACK);
}
browseTo(browseto);
}
public void onDestroy() {
super.onDestroy();
// Stop the scanner.
DirectoryScanner scanner = mDirectoryScanner;
if (scanner != null) {
scanner.cancel = true;
}
mDirectoryScanner = null;
ThumbnailLoader loader = mThumbnailLoader;
if (loader != null) {
loader.cancel = true;
mThumbnailLoader = null;
}
}
private void handleMessage(Message message) {
// Log.v(TAG, "Received message " + message.what);
switch (message.what) {
case MESSAGE_SHOW_DIRECTORY_CONTENTS:
showDirectoryContents((DirectoryContents) message.obj);
break;
case MESSAGE_SET_PROGRESS:
setProgress(message.arg1, message.arg2);
break;
case MESSAGE_ICON_CHANGED:
notifyIconChanged((IconifiedText) message.obj);
break;
}
}
private void notifyIconChanged(IconifiedText text) {
if (getListAdapter() != null) {
((BaseAdapter) getListAdapter()).notifyDataSetChanged();
}
}
private void setProgress(int progress, int maxProgress) {
mProgressBar.setMax(maxProgress);
mProgressBar.setProgress(progress);
mProgressBar.setVisibility(View.VISIBLE);
}
private void showDirectoryContents(DirectoryContents contents) {
mDirectoryScanner = null;
mListSdCard = contents.listSdCard;
mListDir = contents.listDir;
mListFile = contents.listFile;
directoryEntries.ensureCapacity(mListSdCard.size() + mListDir.size() + mListFile.size());
addAllElements(directoryEntries, mListSdCard);
addAllElements(directoryEntries, mListDir);
addAllElements(directoryEntries, mListFile);
IconifiedTextListAdapter itla = new IconifiedTextListAdapter(this);
itla.setListItems(directoryEntries, getListView().hasTextFilter());
setListAdapter(itla);
getListView().setTextFilterEnabled(true);
selectInList(mPreviousDirectory);
refreshDirectoryPanel();
setProgressBarIndeterminateVisibility(false);
mProgressBar.setVisibility(View.GONE);
mEmptyText.setVisibility(View.VISIBLE);
mThumbnailLoader = new ThumbnailLoader(currentDirectory, mListFile, currentHandler, this);
mThumbnailLoader.start();
}
private void onCreateDirectoryInput() {
mDirectoryInput = (LinearLayout) findViewById(R.id.directory_input);
mEditDirectory = (EditText) findViewById(R.id.directory_text);
mButtonDirectoryPick = (ImageButton) findViewById(R.id.button_directory_pick);
mButtonDirectoryPick.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
goToDirectoryInEditText();
}
});
}
//private boolean mHaveShownErrorMessage;
private File mHaveShownErrorMessageForFile = null;
private void goToDirectoryInEditText() {
File browseto = new File(mEditDirectory.getText().toString());
if (browseto.equals(currentDirectory)) {
showDirectoryInput(false);
} else {
if (mHaveShownErrorMessageForFile != null
&& mHaveShownErrorMessageForFile.equals(browseto)) {
// Don't let user get stuck in wrong directory.
mHaveShownErrorMessageForFile = null;
showDirectoryInput(false);
} else {
if (!browseto.exists()) {
// browseTo() below will show an error message,
// because file does not exist.
// It is ok to show this the first time.
mHaveShownErrorMessageForFile = browseto;
}
browseTo(browseto);
}
}
}
/**
* Show the directory line as input box instead of button row.
* If Directory input does not exist yet, it is created.
* Since the default is show == false, nothing is created if
* it is not necessary (like after icicle).
* @param show
*/
private void showDirectoryInput(boolean show) {
if (show) {
if (mDirectoryInput == null) {
onCreateDirectoryInput();
}
}
if (mDirectoryInput != null) {
mDirectoryInput.setVisibility(show ? View.VISIBLE : View.GONE);
mDirectoryButtons.setVisibility(show ? View.GONE : View.VISIBLE);
}
refreshDirectoryPanel();
}
/**
*
*/
private void refreshDirectoryPanel() {
if (isDirectoryInputVisible()) {
// Set directory path
String path = currentDirectory.getAbsolutePath();
mEditDirectory.setText(path);
// Set selection to last position so user can continue to type:
mEditDirectory.setSelection(path.length());
} else {
setDirectoryButtons();
}
}
/*
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
}
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
// remember file name
outState.putString(BUNDLE_CURRENT_DIRECTORY, currentDirectory.getAbsolutePath());
outState.putString(BUNDLE_CONTEXT_FILE, mContextFile.getAbsolutePath());
outState.putString(BUNDLE_CONTEXT_TEXT, mContextText);
boolean show = isDirectoryInputVisible();
outState.putBoolean(BUNDLE_SHOW_DIRECTORY_INPUT, show);
outState.putInt(BUNDLE_STEPS_BACK, mStepsBack);
}
/**
* @return
*/
private boolean isDirectoryInputVisible() {
return ((mDirectoryInput != null) && (mDirectoryInput.getVisibility() == View.VISIBLE));
}
private void pickFileOrDirectory() {
File file = null;
if (mState == STATE_PICK_FILE) {
String filename = mEditFilename.getText().toString();
file = FileUtils.getFile(currentDirectory.getAbsolutePath(), filename);
} else if (mState == STATE_PICK_DIRECTORY) {
file = currentDirectory;
}
Intent intent = getIntent();
intent.setData(FileUtils.getUri(file));
setResult(RESULT_OK, intent);
finish();
}
/**
*
*/
private void getMimeTypes() {
MimeTypeParser mtp = new MimeTypeParser();
XmlResourceParser in = getResources().getXml(R.xml.mimetypes);
try {
mMimeTypes = mtp.fromXmlResource(in);
} catch (XmlPullParserException e) {
Log
.e(
TAG,
"PreselectedChannelsActivity: XmlPullParserException",
e);
throw new RuntimeException(
"PreselectedChannelsActivity: XmlPullParserException");
} catch (IOException e) {
Log.e(TAG, "PreselectedChannelsActivity: IOException", e);
throw new RuntimeException(
"PreselectedChannelsActivity: IOException");
}
}
/**
* This function browses up one level
* according to the field: currentDirectory
*/
private void upOneLevel(){
if (mStepsBack > 0) {
mStepsBack--;
}
if(currentDirectory.getParent() != null)
browseTo(currentDirectory.getParentFile());
}
/**
* Jump to some location by clicking on a
* directory button.
*
* This resets the counter for "back" actions.
*
* @param aDirectory
*/
private void jumpTo(final File aDirectory) {
mStepsBack = 0;
browseTo(aDirectory);
}
/**
* Browse to some location by clicking on a list item.
* @param aDirectory
*/
private void browseTo(final File aDirectory){
// setTitle(aDirectory.getAbsolutePath());
if (aDirectory.isDirectory()){
if (aDirectory.equals(currentDirectory)) {
// Switch from button to directory input
showDirectoryInput(true);
} else {
mPreviousDirectory = currentDirectory;
currentDirectory = aDirectory;
refreshList();
// selectInList(previousDirectory);
// refreshDirectoryPanel();
}
}else{
if (mState == STATE_BROWSE || mState == STATE_PICK_DIRECTORY) {
// Lets start an intent to View the file, that was clicked...
openFile(aDirectory);
} else if (mState == STATE_PICK_FILE) {
// Pick the file
mEditFilename.setText(aDirectory.getName());
}
}
}
private void openFile(File aFile) {
if (!aFile.exists()) {
Toast.makeText(this, R.string.error_file_does_not_exists, Toast.LENGTH_SHORT).show();
return;
}
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
Uri data = FileUtils.getUri(aFile);
String type = mMimeTypes.getMimeType(aFile.getName());
intent.setDataAndType(data, type);
// Were we in GET_CONTENT mode?
Intent originalIntent = getIntent();
if (originalIntent != null && originalIntent.getAction() != null && originalIntent.getAction().equals(Intent.ACTION_GET_CONTENT)) {
// In that case, we should probably just return the requested data.
setResult(RESULT_OK, intent);
finish();
return;
}
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.application_not_available, Toast.LENGTH_SHORT).show();
};
}
private void refreshList() {
// Cancel an existing scanner, if applicable.
DirectoryScanner scanner = mDirectoryScanner;
if (scanner != null) {
scanner.cancel = true;
}
ThumbnailLoader loader = mThumbnailLoader;
if (loader != null) {
loader.cancel = true;
mThumbnailLoader = null;
}
directoryEntries.clear();
mListDir.clear();
mListFile.clear();
mListSdCard.clear();
setProgressBarIndeterminateVisibility(true);
// Don't show the "folder empty" text since we're scanning.
mEmptyText.setVisibility(View.GONE);
// Also DON'T show the progress bar - it's kind of lame to show that
// for less than a second.
mProgressBar.setVisibility(View.GONE);
setListAdapter(null);
mDirectoryScanner = new DirectoryScanner(currentDirectory, this, currentHandler, mMimeTypes, mSdCardPath);
mDirectoryScanner.start();
// Add the "." == "current directory"
/*directoryEntries.add(new IconifiedText(
getString(R.string.current_dir),
getResources().getDrawable(R.drawable.ic_launcher_folder))); */
// and the ".." == 'Up one level'
/*
if(currentDirectory.getParent() != null)
directoryEntries.add(new IconifiedText(
getString(R.string.up_one_level),
getResources().getDrawable(R.drawable.ic_launcher_folder_open)));
*/
}
private void selectInList(File selectFile) {
String filename = selectFile.getName();
IconifiedTextListAdapter la = (IconifiedTextListAdapter) getListAdapter();
int count = la.getCount();
for (int i = 0; i < count; i++) {
IconifiedText it = (IconifiedText) la.getItem(i);
if (it.getText().equals(filename)) {
getListView().setSelection(i);
break;
}
}
}
private void addAllElements(List<IconifiedText> addTo, List<IconifiedText> addFrom) {
int size = addFrom.size();
for (int i = 0; i < size; i++) {
addTo.add(addFrom.get(i));
}
}
private void setDirectoryButtons() {
String[] parts = currentDirectory.getAbsolutePath().split("/");
mDirectoryButtons.removeAllViews();
int WRAP_CONTENT = LinearLayout.LayoutParams.WRAP_CONTENT;
// Add home button separately
ImageButton ib = new ImageButton(this);
ib.setImageResource(R.drawable.ic_launcher_home_small);
ib.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ib.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
jumpTo(new File("/"));
}
});
mDirectoryButtons.addView(ib);
// Add other buttons
String dir = "";
for (int i = 1; i < parts.length; i++) {
dir += "/" + parts[i];
if (dir.equals(mSdCardPath)) {
// Add SD card button
ib = new ImageButton(this);
ib.setImageResource(R.drawable.icon_sdcard_small);
ib.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ib.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
jumpTo(new File(mSdCardPath));
}
});
mDirectoryButtons.addView(ib);
} else {
Button b = new Button(this);
b.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
b.setText(parts[i]);
b.setTag(dir);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String dir = (String) view.getTag();
jumpTo(new File(dir));
}
});
mDirectoryButtons.addView(b);
}
}
checkButtonLayout();
}
private void checkButtonLayout() {
// Let's measure how much space we need:
int spec = View.MeasureSpec.UNSPECIFIED;
mDirectoryButtons.measure(spec, spec);
int count = mDirectoryButtons.getChildCount();
int requiredwidth = mDirectoryButtons.getMeasuredWidth();
int width = getWindowManager().getDefaultDisplay().getWidth();
if (requiredwidth > width) {
int WRAP_CONTENT = LinearLayout.LayoutParams.WRAP_CONTENT;
// Create a new button that shows that there is more to the left:
ImageButton ib = new ImageButton(this);
ib.setImageResource(R.drawable.ic_menu_back_small);
ib.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
//
ib.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
// Up one directory.
upOneLevel();
}
});
mDirectoryButtons.addView(ib, 0);
// New button needs even more space
ib.measure(spec, spec);
requiredwidth += ib.getMeasuredWidth();
// Need to take away some buttons
// but leave at least "back" button and one directory button.
while (requiredwidth > width && mDirectoryButtons.getChildCount() > 2) {
View view = mDirectoryButtons.getChildAt(1);
requiredwidth -= view.getMeasuredWidth();
mDirectoryButtons.removeViewAt(1);
}
}
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
if (adapter == null) {
return;
}
IconifiedText text = (IconifiedText) adapter.getItem(position);
String file = text.getText();
/*
if (selectedFileString.equals(getString(R.string.up_one_level))) {
upOneLevel();
} else {
*/
String curdir = currentDirectory
.getAbsolutePath() ;
File clickedFile = FileUtils.getFile(curdir, file);
if (clickedFile != null) {
if (clickedFile.isDirectory()) {
// If we click on folders, we can return later by the "back" key.
mStepsBack++;
}
browseTo(clickedFile);
}
/*
}
*/
}
private void getSdCardPath() {
mSdCardPath = android.os.Environment
.getExternalStorageDirectory().getAbsolutePath();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_NEW_FOLDER, 0, R.string.menu_new_folder).setIcon(
android.R.drawable.ic_menu_add).setShortcut('0', 'f');
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
// Generate any additional actions that can be performed on the
// overall list. This allows other applications to extend
// our menu with their own actions.
Intent intent = new Intent(null, getIntent().getData());
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
// menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
// new ComponentName(this, NoteEditor.class), null, intent, 0, null);
// Workaround to add icons:
MenuIntentOptionsWithIcons menu2 = new MenuIntentOptionsWithIcons(this,
menu);
menu2.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
new ComponentName(this, FileManagerActivity.class), null, intent,
0, null);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//Intent intent;
switch (item.getItemId()) {
case MENU_NEW_FOLDER:
showDialog(DIALOG_NEW_FOLDER);
return true;
/*
case MENU_PREFERENCES:
intent = new Intent(this, PreferenceActivity.class);
startActivity(intent);
return true;
*/
}
return super.onOptionsItemSelected(item);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view,
ContextMenuInfo menuInfo) {
AdapterView.AdapterContextMenuInfo info;
try {
info = (AdapterView.AdapterContextMenuInfo) menuInfo;
} catch (ClassCastException e) {
Log.e(TAG, "bad menuInfo", e);
return;
}
/*
Cursor cursor = (Cursor) getListAdapter().getItem(info.position);
if (cursor == null) {
// For some reason the requested item isn't available, do nothing
return;
}
*/
IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
if (adapter == null) {
return;
}
IconifiedText it = (IconifiedText) adapter.getItem(info.position);
menu.setHeaderTitle(it.getText());
menu.setHeaderIcon(it.getIcon());
File file = FileUtils.getFile(currentDirectory, it.getText());
if (!file.isDirectory()) {
if (mState == STATE_PICK_FILE) {
// Show "open" menu
menu.add(0, MENU_OPEN, 0, R.string.menu_open);
}
menu.add(0, MENU_SEND, 0, R.string.menu_send);
}
menu.add(0, MENU_MOVE, 0, R.string.menu_move);
if (!file.isDirectory()) {
menu.add(0, MENU_COPY, 0, R.string.menu_copy);
}
menu.add(0, MENU_RENAME, 0, R.string.menu_rename);
menu.add(0, MENU_DELETE, 0, R.string.menu_delete);
//if (!file.isDirectory()) {
Uri data = Uri.fromFile(file);
Intent intent = new Intent(null, data);
String type = mMimeTypes.getMimeType(file.getName());
intent.setDataAndType(data, type);
//intent.addCategory(Intent.CATEGORY_SELECTED_ALTERNATIVE);
Log.v(TAG, "Data=" + data);
Log.v(TAG, "Type=" + type);
if (type != null) {
// Add additional options for the MIME type of the selected file.
menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
new ComponentName(this, FileManagerActivity.class), null, intent, 0, null);
}
//}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
super.onContextItemSelected(item);
AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item
.getMenuInfo();
// Remember current selection
IconifiedTextListAdapter adapter = (IconifiedTextListAdapter) getListAdapter();
if (adapter == null) {
return false;
}
IconifiedText ic = (IconifiedText) adapter.getItem(menuInfo.position);
mContextText = ic.getText();
mContextIcon = ic.getIcon();
mContextFile = FileUtils.getFile(currentDirectory, ic.getText());
switch (item.getItemId()) {
case MENU_OPEN:
openFile(mContextFile);
return true;
case MENU_MOVE:
promptDestinationAndMoveFile();
return true;
case MENU_COPY:
promptDestinationAndCopyFile();
return true;
case MENU_DELETE:
showDialog(DIALOG_DELETE);
return true;
case MENU_RENAME:
showDialog(DIALOG_RENAME);
return true;
case MENU_SEND:
sendFile(mContextFile);
return true;
}
return false;
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_NEW_FOLDER:
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_new_folder, null);
final EditText et = (EditText) view
.findViewById(R.id.foldername);
et.setText("");
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.create_new_folder).setView(view).setPositiveButton(
android.R.string.ok, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
createNewFolder(et.getText().toString());
}
}).setNegativeButton(android.R.string.cancel, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Cancel should not do anything.
}
}).create();
case DIALOG_DELETE:
return new AlertDialog.Builder(this).setTitle(getString(R.string.really_delete, mContextText))
.setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(
android.R.string.ok, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteFileOrFolder(mContextFile);
}
}).setNegativeButton(android.R.string.cancel, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Cancel should not do anything.
}
}).create();
case DIALOG_RENAME:
inflater = LayoutInflater.from(this);
view = inflater.inflate(R.layout.dialog_new_folder, null);
final EditText et2 = (EditText) view
.findViewById(R.id.foldername);
return new AlertDialog.Builder(this)
.setTitle(R.string.menu_rename).setView(view).setPositiveButton(
android.R.string.ok, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
renameFileOrFolder(mContextFile, et2.getText().toString());
}
}).setNegativeButton(android.R.string.cancel, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Cancel should not do anything.
}
}).create();
}
return null;
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
switch (id) {
case DIALOG_NEW_FOLDER:
EditText et = (EditText) dialog.findViewById(R.id.foldername);
et.setText("");
break;
case DIALOG_DELETE:
((AlertDialog) dialog).setTitle(getString(R.string.really_delete, mContextText));
break;
case DIALOG_RENAME:
et = (EditText) dialog.findViewById(R.id.foldername);
et.setText(mContextText);
TextView tv = (TextView) dialog.findViewById(R.id.foldernametext);
if (mContextFile.isDirectory()) {
tv.setText(R.string.file_name);
} else {
tv.setText(R.string.file_name);
}
((AlertDialog) dialog).setIcon(mContextIcon);
break;
case DIALOG_ABOUT:
break;
}
}
private void promptDestinationAndMoveFile() {
Intent intent = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY);
intent.setData(FileUtils.getUri(currentDirectory));
intent.putExtra(FileManagerIntents.EXTRA_TITLE, getString(R.string.move_title));
intent.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, getString(R.string.move_button));
startActivityForResult(intent, REQUEST_CODE_MOVE);
}
private void promptDestinationAndCopyFile() {
Intent intent = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY);
intent.setData(FileUtils.getUri(currentDirectory));
intent.putExtra(FileManagerIntents.EXTRA_TITLE, getString(R.string.copy_title));
intent.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, getString(R.string.copy_button));
startActivityForResult(intent, REQUEST_CODE_COPY);
}
private void createNewFolder(String foldername) {
if (!TextUtils.isEmpty(foldername)) {
File file = FileUtils.getFile(currentDirectory, foldername);
if (file.mkdirs()) {
// Change into new directory:
browseTo(file);
} else {
Toast.makeText(this, R.string.error_creating_new_folder, Toast.LENGTH_SHORT).show();
}
}
}
/*! Recursively delete a directory and all of its children.
* @params toastOnError If set to true, this function will toast if an error occurs.
* @returns true if successful, false otherwise.
*/
private boolean recursiveDelete(File file, boolean toastOnError) {
// Recursively delete all contents.
File[] files = file.listFiles();
for (int x=0; x<files.length; x++) {
File childFile = files[x];
if (childFile.isDirectory()) {
if (!recursiveDelete(childFile, toastOnError)) {
return false;
}
} else {
if (!childFile.delete()) {
Toast.makeText(this, getString(R.string.error_deleting_child_file, childFile.getAbsolutePath()), Toast.LENGTH_LONG);
return false;
}
}
}
if (!file.delete()) {
Toast.makeText(this, getString(R.string.error_deleting_folder, file.getAbsolutePath()), Toast.LENGTH_LONG);
return false;
}
return true;
}
private void deleteFileOrFolder(File file) {
if (file.isDirectory()) {
if (recursiveDelete(file, true)) {
refreshList();
Toast.makeText(this, R.string.folder_deleted, Toast.LENGTH_SHORT).show();
}
} else {
if (file.delete()) {
// Delete was successful.
refreshList();
Toast.makeText(this, R.string.file_deleted, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, R.string.error_deleting_file, Toast.LENGTH_SHORT).show();
}
}
}
private void renameFileOrFolder(File file, String newFileName) {
File newFile = FileUtils.getFile(currentDirectory, newFileName);
rename(file, newFile);
}
/**
* @param oldFile
* @param newFile
*/
private void rename(File oldFile, File newFile) {
int toast = 0;
if (oldFile.renameTo(newFile)) {
// Rename was successful.
refreshList();
if (newFile.isDirectory()) {
toast = R.string.folder_renamed;
} else {
toast = R.string.file_renamed;
}
} else {
if (newFile.isDirectory()) {
toast = R.string.error_renaming_folder;
} else {
toast = R.string.error_renaming_file;
}
}
Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
}
/**
* @param oldFile
* @param newFile
*/
private void move(File oldFile, File newFile) {
int toast = 0;
if (oldFile.renameTo(newFile)) {
// Rename was successful.
refreshList();
if (newFile.isDirectory()) {
toast = R.string.folder_moved;
} else {
toast = R.string.file_moved;
}
} else {
if (newFile.isDirectory()) {
toast = R.string.error_moving_folder;
} else {
toast = R.string.error_moving_file;
}
}
Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
}
/*@ RETURNS: A file name that is guaranteed to not exist yet.
*
* PARAMS:
* context - Application context.
* path - The path that the file is supposed to be in.
* fileName - Desired file name. This name will be modified to
* create a unique file if necessary.
*
*/
private File createUniqueCopyName(Context context, File path, String fileName) {
// Does that file exist?
File file = FileUtils.getFile(path, fileName);
if (!file.exists()) {
// Nope - we can take that.
return file;
}
// Try a simple "copy of".
file = FileUtils.getFile(path, context.getString(R.string.copied_file_name, fileName));
if (!file.exists()) {
// Nope - we can take that.
return file;
}
int copyIndex = 2;
// Well, we gotta find a unique name at some point.
while (copyIndex < 500) {
file = FileUtils.getFile(path, context.getString(R.string.copied_file_name_2, copyIndex, fileName));
if (!file.exists()) {
// Nope - we can take that.
return file;
}
copyIndex++;
}
// I GIVE UP.
return null;
}
private void copy(File oldFile, File newFile) {
int toast = 0;
try {
FileInputStream input = new FileInputStream(oldFile);
FileOutputStream output = new FileOutputStream(newFile);
byte[] buffer = new byte[COPY_BUFFER_SIZE];
while (true) {
int bytes = input.read(buffer);
if (bytes <= 0) {
break;
}
output.write(buffer, 0, bytes);
}
output.close();
input.close();
toast = R.string.file_copied;
refreshList();
} catch (Exception e) {
toast = R.string.error_copying_file;
}
Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
}
private void sendFile(File file) {
String filename = file.getName();
String content = "hh";
Log.i(TAG, "Title to send: " + filename);
Log.i(TAG, "Content to send: " + content);
Intent i = new Intent();
i.setAction(Intent.ACTION_SEND);
i.setType(mMimeTypes.getMimeType(file.getName()));
i.putExtra(Intent.EXTRA_SUBJECT, filename);
//i.putExtra(Intent.EXTRA_STREAM, FileUtils.getUri(file));
i.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + FileManagerProvider.AUTHORITY + "/mimetype/" + file.getAbsolutePath()));
i = Intent.createChooser(i, getString(R.string.menu_send));
try {
startActivity(i);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.send_not_available,
Toast.LENGTH_SHORT).show();
Log.e(TAG, "Email client not installed");
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (mStepsBack > 0) {
upOneLevel();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
// For targetSdkVersion="5" or higher, one needs to use the following code instead of the one above:
// (See http://android-developers.blogspot.com/2009/12/back-and-other-hard-keys-three-stories.html )
/*
//@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR
&& keyCode == KeyEvent.KEYCODE_BACK
&& event.getRepeatCount() == 0) {
// Take care of calling this method on earlier versions of
// the platform where it doesn't exist.
onBackPressed();
}
return super.onKeyDown(keyCode, event);
}
//@Override
public void onBackPressed() {
// This will be called either automatically for you on 2.0
// or later, or by the code above on earlier versions of the
// platform.
if (mStepsBack > 0) {
upOneLevel();
} else {
finish();
}
}
*/
/**
* This is called after the file manager finished.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE_MOVE:
if (resultCode == RESULT_OK && data != null) {
// obtain the filename
File movefrom = mContextFile;
File moveto = FileUtils.getFile(data.getData());
if (moveto != null) {
moveto = FileUtils.getFile(moveto, movefrom.getName());
move(movefrom, moveto);
}
}
break;
case REQUEST_CODE_COPY:
if (resultCode == RESULT_OK && data != null) {
// obtain the filename
File copyfrom = mContextFile;
File copyto = FileUtils.getFile(data.getData());
if (copyto != null) {
copyto = createUniqueCopyName(this, copyto, copyfrom.getName());
if (copyto != null) {
copy(copyfrom, copyto);
}
}
}
break;
}
}
}