mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-11-25 02:32:26 -05:00
+ Kp2a FileChooser base class
This commit is contained in:
parent
881c77c565
commit
9deeeef382
@ -0,0 +1,17 @@
|
|||||||
|
package keepass2android.kp2afilechooser;
|
||||||
|
|
||||||
|
|
||||||
|
public class FileEntry {
|
||||||
|
public String path;
|
||||||
|
public boolean isDirectory;
|
||||||
|
public long lastModifiedTime;
|
||||||
|
public boolean canRead;
|
||||||
|
public boolean canWrite;
|
||||||
|
public long sizeInBytes;
|
||||||
|
|
||||||
|
public FileEntry()
|
||||||
|
{
|
||||||
|
isDirectory = false;
|
||||||
|
canRead = canWrite = true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package keepass2android.kp2afilechooser;
|
||||||
|
|
||||||
|
import group.pals.android.lib.ui.filechooser.FileChooserActivity;
|
||||||
|
//import group.pals.android.lib.ui.filechooser.FileChooserActivity_v7;
|
||||||
|
import group.pals.android.lib.ui.filechooser.providers.basefile.BaseFileContract.BaseFile;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
public class Kp2aFileChooserBridge {
|
||||||
|
public static Intent getLaunchFileChooserIntent(Context ctx, String authority, String defaultPath)
|
||||||
|
{
|
||||||
|
//Always use FileChooserActivity. _v7 was removed due to problems with Mono for Android binding.
|
||||||
|
Class<?> cls = FileChooserActivity.class;
|
||||||
|
|
||||||
|
Intent intent = new Intent(ctx, cls);
|
||||||
|
|
||||||
|
intent.putExtra(FileChooserActivity.EXTRA_ROOTPATH,
|
||||||
|
BaseFile.genContentIdUriBase(authority)
|
||||||
|
.buildUpon()
|
||||||
|
.appendPath(defaultPath)
|
||||||
|
.build());
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,672 @@
|
|||||||
|
package keepass2android.kp2afilechooser;
|
||||||
|
/* Author: Philipp Crocoll
|
||||||
|
*
|
||||||
|
* Based on a file provider by Hai Bison
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.database.MatrixCursor.RowBuilder;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import group.pals.android.lib.ui.filechooser.R;
|
||||||
|
import group.pals.android.lib.ui.filechooser.providers.BaseFileProviderUtils;
|
||||||
|
import group.pals.android.lib.ui.filechooser.providers.ProviderUtils;
|
||||||
|
import group.pals.android.lib.ui.filechooser.providers.basefile.BaseFileContract.BaseFile;
|
||||||
|
import group.pals.android.lib.ui.filechooser.providers.basefile.BaseFileProvider;
|
||||||
|
|
||||||
|
import group.pals.android.lib.ui.filechooser.utils.FileUtils;
|
||||||
|
import group.pals.android.lib.ui.filechooser.utils.Utils;
|
||||||
|
|
||||||
|
public abstract class Kp2aFileProvider extends BaseFileProvider {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the authority of this provider.
|
||||||
|
*
|
||||||
|
* abstract because the concrete authority can be decided by the overriding class.
|
||||||
|
*
|
||||||
|
* @param context the context.
|
||||||
|
* @return the authority.
|
||||||
|
*/
|
||||||
|
public abstract String getAuthority();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique ID of this provider.
|
||||||
|
*/
|
||||||
|
public static final String _ID = "9dab9818-0a8b-47ef-88cc-10fe538bf8f7";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for debugging or something...
|
||||||
|
*/
|
||||||
|
private static final String CLASSNAME = Kp2aFileProvider.class.getName();
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
BaseFileProviderUtils.registerProviderInfo(_ID,
|
||||||
|
getAuthority());
|
||||||
|
|
||||||
|
URI_MATCHER.addURI(getAuthority(),
|
||||||
|
BaseFile.PATH_DIR + "/*", URI_DIRECTORY);
|
||||||
|
URI_MATCHER.addURI(getAuthority(),
|
||||||
|
BaseFile.PATH_FILE + "/*", URI_FILE);
|
||||||
|
URI_MATCHER.addURI(getAuthority(),
|
||||||
|
BaseFile.PATH_API, URI_API);
|
||||||
|
URI_MATCHER.addURI(getAuthority(),
|
||||||
|
BaseFile.PATH_API + "/*", URI_API_COMMAND);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}// onCreate()
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
if (Utils.doLog())
|
||||||
|
Log.d(CLASSNAME, "delete() >> " + uri);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
switch (URI_MATCHER.match(uri)) {
|
||||||
|
case URI_FILE: {
|
||||||
|
boolean isRecursive = ProviderUtils.getBooleanQueryParam(uri,
|
||||||
|
BaseFile.PARAM_RECURSIVE, true);
|
||||||
|
String filename = extractFile(uri);
|
||||||
|
if (deletePath(filename, isRecursive))
|
||||||
|
{
|
||||||
|
getContext()
|
||||||
|
.getContentResolver()
|
||||||
|
.notifyChange(
|
||||||
|
BaseFile.genContentUriBase(
|
||||||
|
|
||||||
|
getAuthority())
|
||||||
|
.buildUpon()
|
||||||
|
.appendPath(
|
||||||
|
addProtocol(getParentPath(filename))
|
||||||
|
.toString())
|
||||||
|
.build(), null);
|
||||||
|
count = 1; //success
|
||||||
|
}
|
||||||
|
|
||||||
|
break;// URI_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("UNKNOWN URI " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
getContext().getContentResolver().notifyChange(uri, null);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}// delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Uri uri, ContentValues values) {
|
||||||
|
if (Utils.doLog())
|
||||||
|
Log.d(CLASSNAME, "insert() >> " + uri);
|
||||||
|
|
||||||
|
switch (URI_MATCHER.match(uri)) {
|
||||||
|
case URI_DIRECTORY:
|
||||||
|
String dirname = extractFile(uri);
|
||||||
|
String newDirName = uri.getQueryParameter(BaseFile.PARAM_NAME);
|
||||||
|
String newFullName = removeTrailingSlash(dirname)+"/"+newDirName;
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
|
||||||
|
switch (ProviderUtils.getIntQueryParam(uri,
|
||||||
|
BaseFile.PARAM_FILE_TYPE, BaseFile.FILE_TYPE_DIRECTORY)) {
|
||||||
|
case BaseFile.FILE_TYPE_DIRECTORY:
|
||||||
|
success = createDirectory(dirname, newDirName);
|
||||||
|
break;// FILE_TYPE_DIRECTORY
|
||||||
|
|
||||||
|
case BaseFile.FILE_TYPE_FILE:
|
||||||
|
//not supported at the moment
|
||||||
|
break;// FILE_TYPE_FILE
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
Uri newUri = BaseFile
|
||||||
|
.genContentIdUriBase(
|
||||||
|
getAuthority())
|
||||||
|
.buildUpon()
|
||||||
|
.appendPath( addProtocol(newFullName)).build();
|
||||||
|
getContext().getContentResolver().notifyChange(uri, null);
|
||||||
|
return newUri;
|
||||||
|
}
|
||||||
|
return null;// URI_FILE
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("UNKNOWN URI " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
}// insert()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(Uri uri, String[] projection, String selection,
|
||||||
|
String[] selectionArgs, String sortOrder) {
|
||||||
|
if (Utils.doLog())
|
||||||
|
Log.d(CLASSNAME, String.format(
|
||||||
|
"query() >> uri = %s (%s) >> match = %s", uri,
|
||||||
|
uri.getLastPathSegment(), URI_MATCHER.match(uri)));
|
||||||
|
|
||||||
|
switch (URI_MATCHER.match(uri)) {
|
||||||
|
case URI_API: {
|
||||||
|
/*
|
||||||
|
* If there is no command given, return provider ID and name.
|
||||||
|
*/
|
||||||
|
MatrixCursor matrixCursor = new MatrixCursor(new String[] {
|
||||||
|
BaseFile.COLUMN_PROVIDER_ID, BaseFile.COLUMN_PROVIDER_NAME,
|
||||||
|
BaseFile.COLUMN_PROVIDER_ICON_ATTR });
|
||||||
|
matrixCursor.newRow().add(_ID)
|
||||||
|
.add("KP2A")
|
||||||
|
.add(R.attr.afc_badge_file_provider_localfile);
|
||||||
|
return matrixCursor;
|
||||||
|
}
|
||||||
|
case URI_API_COMMAND: {
|
||||||
|
return doAnswerApiCommand(uri);
|
||||||
|
}// URI_API
|
||||||
|
|
||||||
|
case URI_DIRECTORY: {
|
||||||
|
return doListFiles(uri);
|
||||||
|
}// URI_DIRECTORY
|
||||||
|
|
||||||
|
case URI_FILE: {
|
||||||
|
return doRetrieveFileInfo(uri);
|
||||||
|
}// URI_FILE
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("UNKNOWN URI " + uri);
|
||||||
|
}
|
||||||
|
}// query()
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UTILITIES
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Answers the incoming URI.
|
||||||
|
*
|
||||||
|
* @param uri
|
||||||
|
* the request URI.
|
||||||
|
* @return the response.
|
||||||
|
*/
|
||||||
|
private MatrixCursor doAnswerApiCommand(Uri uri) {
|
||||||
|
MatrixCursor matrixCursor = null;
|
||||||
|
|
||||||
|
if (BaseFile.CMD_CANCEL.equals(uri.getLastPathSegment())) {
|
||||||
|
int taskId = ProviderUtils.getIntQueryParam(uri,
|
||||||
|
BaseFile.PARAM_TASK_ID, 0);
|
||||||
|
synchronized (mMapInterruption) {
|
||||||
|
if (taskId == 0) {
|
||||||
|
for (int i = 0; i < mMapInterruption.size(); i++)
|
||||||
|
mMapInterruption.put(mMapInterruption.keyAt(i), true);
|
||||||
|
} else if (mMapInterruption.indexOfKey(taskId) >= 0)
|
||||||
|
mMapInterruption.put(taskId, true);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} else if (BaseFile.CMD_GET_DEFAULT_PATH.equals(uri
|
||||||
|
.getLastPathSegment())) {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}// get default path
|
||||||
|
else if (BaseFile.CMD_IS_ANCESTOR_OF.equals(uri.getLastPathSegment())) {
|
||||||
|
return doCheckAncestor(uri);
|
||||||
|
} else if (BaseFile.CMD_GET_PARENT.equals(uri.getLastPathSegment())) {
|
||||||
|
|
||||||
|
{
|
||||||
|
String path = Uri.parse(
|
||||||
|
uri.getQueryParameter(BaseFile.PARAM_SOURCE)).getPath();
|
||||||
|
|
||||||
|
String parentPath = addProtocol(getParentPath(path));
|
||||||
|
|
||||||
|
|
||||||
|
if (parentPath == null)
|
||||||
|
{
|
||||||
|
if (Utils.doLog())
|
||||||
|
Log.d(CLASSNAME, "parent file is null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fname = getName(parentPath);
|
||||||
|
|
||||||
|
|
||||||
|
matrixCursor = BaseFileProviderUtils.newBaseFileCursor();
|
||||||
|
|
||||||
|
int type = parentPath != null ? BaseFile.FILE_TYPE_DIRECTORY
|
||||||
|
: BaseFile.FILE_TYPE_NOT_EXISTED;
|
||||||
|
|
||||||
|
|
||||||
|
RowBuilder newRow = matrixCursor.newRow();
|
||||||
|
newRow.add(0);// _ID
|
||||||
|
newRow.add(BaseFile
|
||||||
|
.genContentIdUriBase(
|
||||||
|
getAuthority())
|
||||||
|
.buildUpon().appendPath(addProtocol(parentPath))
|
||||||
|
.build().toString());
|
||||||
|
newRow.add(parentPath);
|
||||||
|
newRow.add(fname);
|
||||||
|
newRow.add(true); //can read
|
||||||
|
newRow.add(true); //can write
|
||||||
|
newRow.add(0);
|
||||||
|
newRow.add(type);
|
||||||
|
newRow.add(0);
|
||||||
|
newRow.add(FileUtils.getResIcon(type, fname));
|
||||||
|
return matrixCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (BaseFile.CMD_SHUTDOWN.equals(uri.getLastPathSegment())) {
|
||||||
|
/*
|
||||||
|
* TODO Stop all tasks. If the activity call this command in
|
||||||
|
* onDestroy(), it seems that this code block will be suspended and
|
||||||
|
* started next time the activity starts. So we comment out this.
|
||||||
|
* Let the Android system do what it wants to do!!!! I hate this.
|
||||||
|
*/
|
||||||
|
// synchronized (mMapInterruption) {
|
||||||
|
// for (int i = 0; i < mMapInterruption.size(); i++)
|
||||||
|
// mMapInterruption.put(mMapInterruption.keyAt(i), true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return matrixCursor;
|
||||||
|
}// doAnswerApiCommand()
|
||||||
|
|
||||||
|
private String getName(String path) {
|
||||||
|
path = removeTrailingSlash(path);
|
||||||
|
int lastSlashPos = path.lastIndexOf("/");
|
||||||
|
//if path is root, return its name. empty is ok
|
||||||
|
if (lastSlashPos == -1)
|
||||||
|
return path;
|
||||||
|
return path.substring(lastSlashPos+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String addProtocol(String path) {
|
||||||
|
if (path == null)
|
||||||
|
return null;
|
||||||
|
if (path.startsWith(getProtocolId()+"://"))
|
||||||
|
return path;
|
||||||
|
return getProtocolId()+"://"+path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the content of a directory, if available.
|
||||||
|
*
|
||||||
|
* @param uri
|
||||||
|
* the URI pointing to a directory.
|
||||||
|
* @return the content of a directory, or {@code null} if not available.
|
||||||
|
*/
|
||||||
|
private MatrixCursor doListFiles(Uri uri) {
|
||||||
|
MatrixCursor matrixCursor = BaseFileProviderUtils.newBaseFileCursor();
|
||||||
|
|
||||||
|
String dirName = extractFile(uri);
|
||||||
|
|
||||||
|
if (Utils.doLog())
|
||||||
|
Log.d(CLASSNAME, "doListFiles. srcFile = " + dirName);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare params...
|
||||||
|
*/
|
||||||
|
int taskId = ProviderUtils.getIntQueryParam(uri,
|
||||||
|
BaseFile.PARAM_TASK_ID, 0);
|
||||||
|
boolean showHiddenFiles = ProviderUtils.getBooleanQueryParam(uri,
|
||||||
|
BaseFile.PARAM_SHOW_HIDDEN_FILES);
|
||||||
|
boolean sortAscending = ProviderUtils.getBooleanQueryParam(uri,
|
||||||
|
BaseFile.PARAM_SORT_ASCENDING, true);
|
||||||
|
int sortBy = ProviderUtils.getIntQueryParam(uri,
|
||||||
|
BaseFile.PARAM_SORT_BY, BaseFile.SORT_BY_NAME);
|
||||||
|
int filterMode = ProviderUtils.getIntQueryParam(uri,
|
||||||
|
BaseFile.PARAM_FILTER_MODE,
|
||||||
|
BaseFile.FILTER_FILES_AND_DIRECTORIES);
|
||||||
|
int limit = ProviderUtils.getIntQueryParam(uri, BaseFile.PARAM_LIMIT,
|
||||||
|
1000);
|
||||||
|
String positiveRegex = uri
|
||||||
|
.getQueryParameter(BaseFile.PARAM_POSITIVE_REGEX_FILTER);
|
||||||
|
String negativeRegex = uri
|
||||||
|
.getQueryParameter(BaseFile.PARAM_NEGATIVE_REGEX_FILTER);
|
||||||
|
|
||||||
|
mMapInterruption.put(taskId, false);
|
||||||
|
|
||||||
|
boolean[] hasMoreFiles = { false };
|
||||||
|
List<FileEntry> files = new ArrayList<FileEntry>();
|
||||||
|
listFiles(taskId, dirName, showHiddenFiles, filterMode, limit,
|
||||||
|
positiveRegex, negativeRegex, files, hasMoreFiles);
|
||||||
|
if (!mMapInterruption.get(taskId)) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
sortFiles(taskId, files, sortAscending, sortBy);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (!mMapInterruption.get(taskId)) {
|
||||||
|
|
||||||
|
for (int i = 0; i < files.size(); i++) {
|
||||||
|
if (mMapInterruption.get(taskId))
|
||||||
|
break;
|
||||||
|
|
||||||
|
FileEntry f = files.get(i);
|
||||||
|
|
||||||
|
Log.d(CLASSNAME, "listing " + f.path +" for "+dirName);
|
||||||
|
|
||||||
|
addFileInfo(matrixCursor, i, f);
|
||||||
|
}// for files
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The last row contains:
|
||||||
|
*
|
||||||
|
* - The ID;
|
||||||
|
*
|
||||||
|
* - The base file URI to original directory, which has
|
||||||
|
* parameter BaseFile.PARAM_HAS_MORE_FILES to indicate the
|
||||||
|
* directory has more files or not.
|
||||||
|
*
|
||||||
|
* - The system absolute path to original directory.
|
||||||
|
*
|
||||||
|
* - The name of original directory.
|
||||||
|
*/
|
||||||
|
RowBuilder newRow = matrixCursor.newRow();
|
||||||
|
newRow.add(files.size());// _ID
|
||||||
|
newRow.add(BaseFile
|
||||||
|
.genContentIdUriBase(
|
||||||
|
getAuthority())
|
||||||
|
.buildUpon()
|
||||||
|
.appendPath(addProtocol(dirName))
|
||||||
|
.appendQueryParameter(BaseFile.PARAM_HAS_MORE_FILES,
|
||||||
|
Boolean.toString(hasMoreFiles[0])).build()
|
||||||
|
.toString());
|
||||||
|
newRow.add(addProtocol(dirName));
|
||||||
|
newRow.add(getName(dirName));
|
||||||
|
|
||||||
|
Log.d(CLASSNAME, "Returning name " + getName(dirName)+" for " +addProtocol(dirName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (mMapInterruption.get(taskId)) {
|
||||||
|
if (Utils.doLog())
|
||||||
|
Log.d(CLASSNAME, "query() >> cancelled...");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
mMapInterruption.delete(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tells the Cursor what URI to watch, so it knows when its source data
|
||||||
|
* changes.
|
||||||
|
*/
|
||||||
|
matrixCursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||||
|
return matrixCursor;
|
||||||
|
}// doListFiles()
|
||||||
|
|
||||||
|
private RowBuilder addFileInfo(MatrixCursor matrixCursor, int id,
|
||||||
|
FileEntry f) {
|
||||||
|
int type = !f.isDirectory ? BaseFile.FILE_TYPE_FILE : BaseFile.FILE_TYPE_DIRECTORY;
|
||||||
|
RowBuilder newRow = matrixCursor.newRow();
|
||||||
|
newRow.add(id);// _ID
|
||||||
|
newRow.add(BaseFile
|
||||||
|
.genContentIdUriBase(
|
||||||
|
getAuthority())
|
||||||
|
.buildUpon().appendPath(addProtocol(f.path))
|
||||||
|
.build().toString());
|
||||||
|
newRow.add(addProtocol(f.path));
|
||||||
|
newRow.add(getName(f.path));
|
||||||
|
newRow.add(f.canRead ? 1 : 0);
|
||||||
|
newRow.add(f.canWrite ? 1 : 0);
|
||||||
|
newRow.add(f.sizeInBytes);
|
||||||
|
newRow.add(type);
|
||||||
|
if (f.lastModifiedTime > 0)
|
||||||
|
newRow.add(f.lastModifiedTime);
|
||||||
|
else
|
||||||
|
newRow.add(null);
|
||||||
|
newRow.add(FileUtils.getResIcon(type, getName(f.path)));
|
||||||
|
return newRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves file information of a single file.
|
||||||
|
*
|
||||||
|
* @param uri
|
||||||
|
* the URI pointing to a file.
|
||||||
|
* @return the file information. Can be {@code null}, based on the input
|
||||||
|
* parameters.
|
||||||
|
*/
|
||||||
|
private MatrixCursor doRetrieveFileInfo(Uri uri) {
|
||||||
|
Log.d(CLASSNAME, "retrieve file info");
|
||||||
|
MatrixCursor matrixCursor = BaseFileProviderUtils.newBaseFileCursor();
|
||||||
|
|
||||||
|
String filename = extractFile(uri);
|
||||||
|
|
||||||
|
FileEntry f = getFileEntry(filename);
|
||||||
|
|
||||||
|
addFileInfo(matrixCursor, 0, f);
|
||||||
|
|
||||||
|
return matrixCursor;
|
||||||
|
}// doRetrieveFileInfo()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts {@code files}.
|
||||||
|
*
|
||||||
|
* @param taskId
|
||||||
|
* the task ID.
|
||||||
|
* @param files
|
||||||
|
* list of files.
|
||||||
|
* @param ascending
|
||||||
|
* {@code true} or {@code false}.
|
||||||
|
* @param sortBy
|
||||||
|
* can be one of {@link BaseFile.#_SortByModificationTime},
|
||||||
|
* {@link BaseFile.#_SortByName}, {@link BaseFile.#_SortBySize}.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private void sortFiles(final int taskId, final List<FileEntry> files,
|
||||||
|
final boolean ascending, final int sortBy) throws Exception {
|
||||||
|
try {
|
||||||
|
Collections.sort(files, new Comparator<FileEntry>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(FileEntry lhs, FileEntry rhs) {
|
||||||
|
if (mMapInterruption.get(taskId))
|
||||||
|
throw new CancellationException();
|
||||||
|
|
||||||
|
if (lhs.isDirectory && !rhs.isDirectory)
|
||||||
|
return -1;
|
||||||
|
if (!lhs.isDirectory && rhs.isDirectory)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default is to compare by name (case insensitive).
|
||||||
|
*/
|
||||||
|
int res = mCollator.compare(lhs.path, rhs.path);
|
||||||
|
|
||||||
|
switch (sortBy) {
|
||||||
|
case BaseFile.SORT_BY_NAME:
|
||||||
|
break;// SortByName
|
||||||
|
|
||||||
|
case BaseFile.SORT_BY_SIZE:
|
||||||
|
if (lhs.sizeInBytes > rhs.sizeInBytes)
|
||||||
|
res = 1;
|
||||||
|
else if (lhs.sizeInBytes < rhs.sizeInBytes)
|
||||||
|
res = -1;
|
||||||
|
break;// SortBySize
|
||||||
|
|
||||||
|
case BaseFile.SORT_BY_MODIFICATION_TIME:
|
||||||
|
if (lhs.lastModifiedTime > rhs.lastModifiedTime)
|
||||||
|
res = 1;
|
||||||
|
else if (lhs.lastModifiedTime < rhs.lastModifiedTime)
|
||||||
|
res = -1;
|
||||||
|
break;// SortByDate
|
||||||
|
}
|
||||||
|
|
||||||
|
return ascending ? res : -res;
|
||||||
|
}// compare()
|
||||||
|
});
|
||||||
|
} catch (CancellationException e) {
|
||||||
|
if (Utils.doLog())
|
||||||
|
Log.d(CLASSNAME, "sortFiles() >> cancelled...");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.d(CLASSNAME, "sortFiles() >> "+e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}// sortFiles()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks ancestor with {@link BaseFile#CMD_IS_ANCESTOR_OF},
|
||||||
|
* {@link BaseFile#PARAM_SOURCE} and {@link BaseFile#PARAM_TARGET}.
|
||||||
|
*
|
||||||
|
* @param uri
|
||||||
|
* the original URI from client.
|
||||||
|
* @return {@code null} if source is not ancestor of target; or a
|
||||||
|
* <i>non-null but empty</i> cursor if the source is.
|
||||||
|
*/
|
||||||
|
private MatrixCursor doCheckAncestor(Uri uri) {
|
||||||
|
File source = new File(Uri.parse(
|
||||||
|
uri.getQueryParameter(BaseFile.PARAM_SOURCE)).getPath());
|
||||||
|
File target = new File(Uri.parse(
|
||||||
|
uri.getQueryParameter(BaseFile.PARAM_TARGET)).getPath());
|
||||||
|
if (source == null || target == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
boolean validate = ProviderUtils.getBooleanQueryParam(uri,
|
||||||
|
BaseFile.PARAM_VALIDATE, true);
|
||||||
|
if (validate) {
|
||||||
|
if (!source.isDirectory() || !target.exists())
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.equals(target.getParentFile())
|
||||||
|
|| (target.getParent() != null && target.getParent()
|
||||||
|
.startsWith(source.getAbsolutePath())))
|
||||||
|
return BaseFileProviderUtils.newClosedCursor();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}// doCheckAncestor()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts source file from request URI.
|
||||||
|
*
|
||||||
|
* @param uri
|
||||||
|
* the original URI.
|
||||||
|
* @return the filename.
|
||||||
|
*/
|
||||||
|
private static String extractFile(Uri uri) {
|
||||||
|
String fileName = Uri.parse(uri.getLastPathSegment()).getPath();
|
||||||
|
if (uri.getQueryParameter(BaseFile.PARAM_APPEND_PATH) != null)
|
||||||
|
fileName += Uri.parse(
|
||||||
|
uri.getQueryParameter(BaseFile.PARAM_APPEND_PATH))
|
||||||
|
.getPath();
|
||||||
|
if (uri.getQueryParameter(BaseFile.PARAM_APPEND_NAME) != null)
|
||||||
|
fileName += "/" + uri.getQueryParameter(BaseFile.PARAM_APPEND_NAME);
|
||||||
|
|
||||||
|
if (Utils.doLog())
|
||||||
|
Log.d(CLASSNAME, "extractFile() >> " + fileName);
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
}// extractFile()
|
||||||
|
|
||||||
|
private static String removeTrailingSlash(String path)
|
||||||
|
{
|
||||||
|
if (path.endsWith("/")) {
|
||||||
|
return path.substring(0, path.length() - 1);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getParentPath(String path)
|
||||||
|
{
|
||||||
|
path = removeTrailingSlash(path);
|
||||||
|
path = removeProtocol(path);
|
||||||
|
int lastSlashPos = path.lastIndexOf("/");
|
||||||
|
if (lastSlashPos == -1)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return path.substring(0, lastSlashPos)+"/";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String removeProtocol(String path) {
|
||||||
|
if (path.lastIndexOf("://") == -1)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
if (!path.startsWith(getProtocolId()+"://"))
|
||||||
|
{
|
||||||
|
String msg = path+" does not start with "+getProtocolId();
|
||||||
|
Log.d(CLASSNAME, msg);
|
||||||
|
throw new IllegalArgumentException(msg);
|
||||||
|
}
|
||||||
|
return path.substring(getProtocolId().length()+3);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getRootDirectory(String currentPath)
|
||||||
|
{
|
||||||
|
return getProtocolId() + ":///";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FileEntry getFileEntry(String path) {
|
||||||
|
FileEntry f = new FileEntry();
|
||||||
|
f.path = path;
|
||||||
|
f.isDirectory = path.lastIndexOf(".") == -1;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all file inside {@code dirName}.
|
||||||
|
*
|
||||||
|
* @param taskId
|
||||||
|
* the task ID.
|
||||||
|
* @param dir
|
||||||
|
* the source directory.
|
||||||
|
* @param showHiddenFiles
|
||||||
|
* {@code true} or {@code false}.
|
||||||
|
* @param filterMode
|
||||||
|
* can be one of {@link BaseFile#FILTER_DIRECTORIES_ONLY},
|
||||||
|
* {@link BaseFile#FILTER_FILES_ONLY},
|
||||||
|
* {@link BaseFile#FILTER_FILES_AND_DIRECTORIES}.
|
||||||
|
* @param limit
|
||||||
|
* the limit.
|
||||||
|
* @param positiveRegex
|
||||||
|
* the positive regex filter.
|
||||||
|
* @param negativeRegex
|
||||||
|
* the negative regex filter.
|
||||||
|
* @param results
|
||||||
|
* the results.
|
||||||
|
* @param hasMoreFiles
|
||||||
|
* the first item will contain a value representing that there is
|
||||||
|
* more files (exceeding {@code limit}) or not.
|
||||||
|
*/
|
||||||
|
protected abstract void listFiles(final int taskId, final String dirName,
|
||||||
|
final boolean showHiddenFiles, final int filterMode,
|
||||||
|
final int limit, String positiveRegex, String negativeRegex,
|
||||||
|
final List<FileEntry> results, final boolean hasMoreFiles[]);
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract boolean deletePath(String filename, boolean isRecursive);
|
||||||
|
protected abstract boolean createDirectory(String dirname, String newDirName);
|
||||||
|
|
||||||
|
protected abstract String getProtocolId();
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user