updated android-filechooser library (non-public release from HBA)

fixed bug with date parsing in Dropbox
added caching of FileEntry in Kp2aFileProvider.java
This commit is contained in:
Philipp Crocoll 2013-10-03 03:47:07 +02:00
parent a44e8a9680
commit c53bfefee5
14 changed files with 5168 additions and 1993 deletions

View File

@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import android.app.Activity;
@ -298,13 +299,22 @@ public class DropboxFileStorage implements JavaFileStorage {
}
private FileEntry convertToFileEntry(com.dropbox.client2.DropboxAPI.Entry e) {
//Log.d("JFS","e="+e);
FileEntry fileEntry = new FileEntry();
fileEntry.canRead = true;
fileEntry.canWrite = true;
fileEntry.isDirectory = e.isDir;
fileEntry.sizeInBytes = e.bytes;
fileEntry.path = e.path;
fileEntry.lastModifiedTime = com.dropbox.client2.RESTUtility.parseDate(e.modified).getTime();
//Log.d("JFS","fileEntry="+fileEntry);
Date lastModifiedDate = null;
if (e.modified != null)
lastModifiedDate = com.dropbox.client2.RESTUtility.parseDate(e.modified);
if (lastModifiedDate != null)
fileEntry.lastModifiedTime = lastModifiedDate.getTime();
else
fileEntry.lastModifiedTime = -1;
//Log.d("JFS","Ok. Dir="+fileEntry.isDirectory);
return fileEntry;
}
@ -324,7 +334,10 @@ public class DropboxFileStorage implements JavaFileStorage {
public FileEntry getFileEntry(String filename) throws Exception {
try
{
Log.d("JFS", "Hi!");
Log.d("JFS", "mApi = "+mApi);
com.dropbox.client2.DropboxAPI.Entry dbEntry = mApi.metadata(filename, 0, null, false, null);
Log.d("JFS", "dbEntry = "+dbEntry);
if (dbEntry.isDeleted)
throw new FileNotFoundException(filename+" is deleted!");

View File

@ -13,7 +13,6 @@ import group.pals.android.lib.ui.filechooser.providers.BaseFileProviderUtils;
import group.pals.android.lib.ui.filechooser.providers.basefile.BaseFileContract.BaseFile;
import group.pals.android.lib.ui.filechooser.utils.Converter;
import group.pals.android.lib.ui.filechooser.utils.DateUtils;
import group.pals.android.lib.ui.filechooser.utils.Utils;
import group.pals.android.lib.ui.filechooser.utils.ui.ContextMenuUtils;
import group.pals.android.lib.ui.filechooser.utils.ui.LoadingDialog;
import group.pals.android.lib.ui.filechooser.utils.ui.Ui;
@ -510,11 +509,11 @@ public class BaseFileAdapter extends ResourceCursorAdapter {
@Override
public void onClick(final int resId) {
new LoadingDialog(v.getContext(),
new LoadingDialog<Void, Void, Void>(v.getContext(),
R.string.afc_msg_loading, false) {
@Override
protected Object doInBackground(Void... params) {
protected Void doInBackground(Void... params) {
if (resId == R.string.afc_cmd_advanced_selection_all)
asyncSelectAll(-1, true);
else if (resId == R.string.afc_cmd_advanced_selection_none)
@ -533,7 +532,7 @@ public class BaseFileAdapter extends ResourceCursorAdapter {
}// doInBackground()
@Override
protected void onPostExecute(Object result) {
protected void onPostExecute(Void result) {
super.onPostExecute(result);
notifyDataSetChanged();
}// onPostExecute()
@ -544,4 +543,5 @@ public class BaseFileAdapter extends ResourceCursorAdapter {
return true;
}// onLongClick()
};// mCheckboxSelectionOnLongClickListener
}

View File

@ -214,7 +214,7 @@ public class FragmentFiles extends Fragment implements
private boolean mNewLoader = true;
/*
* Controls.
* CONTROLS
*/
private View mBtnGoHome;
@ -268,7 +268,7 @@ public class FragmentFiles extends Fragment implements
FileChooserActivity.EXTRA_MAX_FILE_COUNT, 1000);
mFileAdapter = new BaseFileAdapter(getActivity(), mFilterMode,
mIsMultiSelection);
mFileAdapter.setBuildOptionsMenuListener(mOnBuildOptionsMenuListener);
/*
* History.
@ -320,12 +320,16 @@ public class FragmentFiles extends Fragment implements
.findViewById(R.id.afc_textview_saveas_filename);
mBtnOk = (Button) rootView.findViewById(R.id.afc_button_ok);
/*
* INIT CONTROLS
*/
return rootView;
}// onCreateView()
@Override
public void onActivityCreated(Bundle savedInstanceState) {
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupHeader();
@ -343,7 +347,7 @@ public class FragmentFiles extends Fragment implements
@Override
public void onPrepareOptionsMenu(Menu menu) {
if (Utils.doLog())
if (BuildConfig.DEBUG)
Log.d(CLASSNAME, "onPrepareOptionsMenu()");
/*
@ -406,13 +410,13 @@ public class FragmentFiles extends Fragment implements
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.afc_menuitem_sort)
doResortViewFiles();
resortViewFiles();
else if (item.getItemId() == R.id.afc_menuitem_new_folder)
doCreateNewDir();
createNewDir();
else if (item.getItemId() == R.id.afc_menuitem_switch_viewmode)
doSwitchViewType();
switchViewType();
else if (item.getItemId() == R.id.afc_menuitem_home)
doGoHome();
goHome();
else
return false;
@ -427,16 +431,16 @@ public class FragmentFiles extends Fragment implements
@Override
public void onStop() {
if (Utils.doLog())
if (BuildConfig.DEBUG)
Log.d(CLASSNAME, "onStop()");
super.onStop();
HistoryProviderUtils.clearHistory(getActivity());
HistoryProviderUtils.doCleanupOutdatedHistoryItems(getActivity());
}// onStop()
@Override
public void onDestroy() {
if (Utils.doLog())
if (BuildConfig.DEBUG)
Log.d(CLASSNAME, "onDestroy()");
super.onDestroy();
@ -470,7 +474,7 @@ public class FragmentFiles extends Fragment implements
String negativeRegex = getArguments().getString(
FileChooserActivity.EXTRA_NEGATIVE_REGEX_FILTER);
if (Utils.doLog())
if (BuildConfig.DEBUG)
Log.d(CLASSNAME, "onCreateLoader() >> path = " + path);
return new CursorLoader(
@ -532,7 +536,6 @@ public class FragmentFiles extends Fragment implements
final Uri uriInfo = BaseFileProviderUtils.getUri(data);
final Uri selectedFile = (Uri) getArguments().getParcelable(
FileChooserActivity.EXTRA_SELECT_FILE);
final int colUri = data.getColumnIndex(BaseFile.COLUMN_URI);
if (selectedFile != null)
getArguments().remove(FileChooserActivity.EXTRA_SELECT_FILE);
@ -554,76 +557,8 @@ public class FragmentFiles extends Fragment implements
: getString(R.string.afc_msg_empty),
mFileAdapter.isEmpty());
if (mNewLoader || selectedFile != null) {
/*
* Select either the parent path of last path, or the file provided
* by key EXTRA_SELECT_FILE. Use a Runnable to make sure this work.
* Because if the list view is handling data, this might not work.
*/
mViewFiles.post(new Runnable() {
@Override
public void run() {
int shouldBeSelectedIdx = -1;
final Uri uri = selectedFile != null ? selectedFile
: getLastLocation();
if (uri != null
&& BaseFileProviderUtils.fileExists(getActivity(),
uri)) {
final String fileName = BaseFileProviderUtils
.getFileName(getActivity(), uri);
if (fileName != null) {
Uri parentUri = BaseFileProviderUtils
.getParentFile(getActivity(), uri);
if ((uri == getLastLocation()
&& !getCurrentLocation().equals(
getLastLocation()) && BaseFileProviderUtils
.isAncestorOf(getActivity(),
getCurrentLocation(), uri))
|| getCurrentLocation().equals(parentUri)) {
if (data.moveToFirst()) {
while (!data.isLast()) {
Uri subUri = Uri.parse(data
.getString(colUri));
if (uri == getLastLocation()) {
if (data.getInt(data
.getColumnIndex(BaseFile.COLUMN_TYPE)) == BaseFile.FILE_TYPE_DIRECTORY) {
if (subUri.equals(uri)
|| BaseFileProviderUtils
.isAncestorOf(
getActivity(),
subUri,
uri)) {
shouldBeSelectedIdx = Math.max(
0,
data.getPosition() - 2);
break;
}
}
} else {
if (uri.equals(subUri)) {
shouldBeSelectedIdx = Math.max(
0,
data.getPosition() - 2);
break;
}
}
data.moveToNext();
}
}
}
}
}
if (shouldBeSelectedIdx >= 0
&& shouldBeSelectedIdx < mFileAdapter.getCount())
mViewFiles.setSelection(shouldBeSelectedIdx);
else if (!mFileAdapter.isEmpty())
mViewFiles.setSelection(0);
}// run()
});
}
if (mNewLoader || selectedFile != null)
createFileSelector();
mNewLoader = false;
}// onLoadFinished()
@ -693,7 +628,7 @@ public class FragmentFiles extends Fragment implements
mViewGoForward.setEnabled(false);
mViewGoForward.setOnClickListener(mBtnGoForwardOnClickListener);
}// setupHeader()
/**
@ -770,7 +705,9 @@ public class FragmentFiles extends Fragment implements
* </ul>
*/
private void setupFooter() {
// by default, view group footer and all its child views are hidden
/*
* By default, view group footer and all its child views are hidden.
*/
ViewGroup viewGroupFooterContainer = (ViewGroup) getView()
.findViewById(R.id.afc_viewgroup_footer_container);
@ -851,6 +788,10 @@ public class FragmentFiles extends Fragment implements
mFooterView.setVisibility(View.GONE);
}// showFooterView()
/**
* This should be called after the owner activity has been created
* successfully.
*/
private void initGestureDetector() {
mListviewFilesGestureDetector = new GestureDetector(getActivity(),
new GestureDetector.SimpleOnGestureListener() {
@ -920,12 +861,12 @@ public class FragmentFiles extends Fragment implements
if (BaseFileProviderUtils.isFile(data)) {
mTxtSaveas.setText(BaseFileProviderUtils
.getFileName(data));
doCheckSaveasFilenameAndFinish(BaseFileProviderUtils
checkSaveasFilenameAndFinish(BaseFileProviderUtils
.getFileName(data));
} else
return false;
} else
doFinish(BaseFileProviderUtils.getUri(data));
finish(BaseFileProviderUtils.getUri(data));
}// double tap to choose files
else {
// do nothing
@ -963,8 +904,8 @@ public class FragmentFiles extends Fragment implements
.setAction(MotionEvent.ACTION_CANCEL);
mViewFiles.onTouchEvent(cancelEvent);
doDeleteFile(mViewFiles
.getFirstVisiblePosition() + pos);
deleteFile(mViewFiles.getFirstVisiblePosition()
+ pos);
}
}
@ -985,7 +926,7 @@ public class FragmentFiles extends Fragment implements
* @param savedInstanceState
*/
private void loadInitialPath(final Bundle savedInstanceState) {
if (Utils.doLog())
if (BuildConfig.DEBUG)
Log.d(CLASSNAME, String.format(
"loadInitialPath() >> authority=[%s] | mRoot=[%s]",
mFileProviderAuthority, mRoot));
@ -1046,11 +987,24 @@ public class FragmentFiles extends Fragment implements
.getAuthority());
if (path == null) {
doShowCannotConnectToServiceAndFinish();
showCannotConnectToServiceAndFinish();
return;
}
if (!BaseFileProviderUtils.fileCanRead(getActivity(), path)) {
if (BuildConfig.DEBUG)
Log.d(CLASSNAME, "loadInitialPath() >> " + path);
setCurrentLocation(path);
if (BaseFileProviderUtils.fileCanRead(getActivity(), path)) {
/*
* Prepare the loader. Either re-connect with an existing one, or
* start a new one.
*/
Bundle args = new Bundle();
args.putParcelable(PATH, path);
getLoaderManager().initLoader(mIdLoaderData, args, this);
} else {
Dlg.toast(
getActivity(),
getString(R.string.afc_pmsg_cannot_access_dir,
@ -1058,19 +1012,6 @@ public class FragmentFiles extends Fragment implements
path)), Dlg.LENGTH_SHORT);
getActivity().finish();
}
if (Utils.doLog())
Log.d(CLASSNAME, "loadInitialPath() >> " + path);
setCurrentLocation(path);
/*
* Prepare the loader. Either re-connect with an existing one, or start
* a new one.
*/
Bundle b = new Bundle();
b.putParcelable(PATH, path);
getLoaderManager().initLoader(mIdLoaderData, b, this);
}// loadInitialPath()
/**
@ -1100,7 +1041,7 @@ public class FragmentFiles extends Fragment implements
/**
* As the name means...
*/
private void doShowCannotConnectToServiceAndFinish() {
private void showCannotConnectToServiceAndFinish() {
Dlg.showError(getActivity(),
R.string.afc_msg_cannot_connect_to_file_provider_service,
new DialogInterface.OnCancelListener() {
@ -1111,7 +1052,7 @@ public class FragmentFiles extends Fragment implements
getActivity().finish();
}// onCancel()
});
}// doShowCannotConnectToServiceAndFinish()
}// showCannotConnectToServiceAndFinish()
/**
* Gets last location.
@ -1154,9 +1095,10 @@ public class FragmentFiles extends Fragment implements
updateDbHistory(location);
}// setCurrentLocation()
private void doGoHome() {
private void goHome() {
goTo(mRoot);
}// doGoHome()
}// goHome()
private static final int[] BUTTON_SORT_IDS = {
R.id.afc_button_sort_by_name_asc,
@ -1169,7 +1111,7 @@ public class FragmentFiles extends Fragment implements
* Show a dialog for sorting options and resort file list after user
* selected an option.
*/
private void doResortViewFiles() {
private void resortViewFiles() {
final Dialog dialog = new Dialog(getActivity(), Ui.resolveAttribute(
getActivity(), R.attr.afc_theme_dialog));
dialog.setCanceledOnTouchOutside(true);
@ -1245,12 +1187,12 @@ public class FragmentFiles extends Fragment implements
dialog.setTitle(R.string.afc_title_sort_by);
dialog.setContentView(view);
dialog.show();
}// doResortViewFiles()
}// resortViewFiles()
/**
* Switch view type between {@link ViewType#LIST} and {@link ViewType#GRID}
*/
private void doSwitchViewType() {
private void switchViewType() {
switch (DisplayPrefs.getViewType(getActivity())) {
case GRID:
DisplayPrefs.setViewType(getActivity(), ViewType.LIST);
@ -1263,12 +1205,12 @@ public class FragmentFiles extends Fragment implements
setupViewFiles();
getActivity().supportInvalidateOptionsMenu();
goTo(getCurrentLocation());
}// doSwitchViewType()
}// switchViewType()
/**
* Confirms user to create new directory.
*/
private void doCreateNewDir() {
private void createNewDir() {
if (LocalFileContract.getAuthority(getActivity()).equals(
mFileProviderAuthority)
&& !Utils.hasPermissions(getActivity(),
@ -1329,22 +1271,23 @@ public class FragmentFiles extends Fragment implements
return;
}
if (getActivity()
.getContentResolver()
.insert(BaseFile
.genContentUriBase(
if (BaseFileProviderUtils
.insertInBackground(
getActivity(),
BaseFile.genContentUriBase(
getCurrentLocation()
.getAuthority())
.buildUpon()
.appendPath(
getCurrentLocation()
.getLastPathSegment())
.appendQueryParameter(
BaseFile.PARAM_NAME, name)
.appendQueryParameter(
BaseFile.PARAM_FILE_TYPE,
Integer.toString(BaseFile.FILE_TYPE_DIRECTORY))
.build(), null) != null) {
.buildUpon()
.appendPath(
getCurrentLocation()
.getLastPathSegment())
.appendQueryParameter(
BaseFile.PARAM_NAME,
name)
.appendQueryParameter(
BaseFile.PARAM_FILE_TYPE,
Integer.toString(BaseFile.FILE_TYPE_DIRECTORY))
.build(), null) != null) {
Dlg.toast(getActivity(),
getString(R.string.afc_msg_done),
Dlg.LENGTH_SHORT);
@ -1383,7 +1326,7 @@ public class FragmentFiles extends Fragment implements
.trim()));
}
});
}// doCreateNewDir()
}// createNewDir()
/**
* Deletes a file.
@ -1391,7 +1334,7 @@ public class FragmentFiles extends Fragment implements
* @param position
* the position of item to be delete.
*/
private void doDeleteFile(final int position) {
private void deleteFile(final int position) {
Cursor cursor = (Cursor) mFileAdapter.getItem(position);
/*
@ -1440,13 +1383,15 @@ public class FragmentFiles extends Fragment implements
@Override
public void onClick(DialogInterface dialog, int which) {
new LoadingDialog(getActivity(), getString(
R.string.afc_pmsg_deleting_file,
isFile ? getString(R.string.afc_file)
: getString(R.string.afc_folder),
filename), true) {
new LoadingDialog<Void, Void, Void>(
getActivity(),
getString(
R.string.afc_pmsg_deleting_file,
isFile ? getString(R.string.afc_file)
: getString(R.string.afc_folder),
filename), true) {
final int mTaskId = EnvUtils.genId();
final int taskId = EnvUtils.genId();
private void notifyFileDeleted() {
mHistory.removeAll(new HistoryFilter<Uri>() {
@ -1472,15 +1417,15 @@ public class FragmentFiles extends Fragment implements
}// notifyFileDeleted()
@Override
protected Object doInBackground(Void... arg0) {
getActivity()
.getContentResolver()
.delete(uri
.buildUpon()
.appendQueryParameter(
BaseFile.PARAM_TASK_ID,
Integer.toString(mTaskId))
.build(), null, null);
protected Void doInBackground(Void... params) {
BaseFileProviderUtils
.deleteInBackground(
getActivity(),
uri.buildUpon()
.appendQueryParameter(
BaseFile.PARAM_TASK_ID,
Integer.toString(taskId))
.build(), null, null);
return null;
}// doInBackground()
@ -1490,7 +1435,7 @@ public class FragmentFiles extends Fragment implements
if (getCurrentLocation() != null)
BaseFileProviderUtils.cancelTask(
getActivity(), getCurrentLocation()
.getAuthority(), mTaskId);
.getAuthority(), taskId);
if (BaseFileProviderUtils.fileExists(
getActivity(), uri)) {
@ -1505,7 +1450,7 @@ public class FragmentFiles extends Fragment implements
}// onCancelled()
@Override
protected void onPostExecute(Object result) {
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (BaseFileProviderUtils.fileExists(
@ -1530,7 +1475,7 @@ public class FragmentFiles extends Fragment implements
mFileAdapter.markItemAsDeleted(id, false);
}// onCancel()
});
}// doDeleteFile()
}// deleteFile()
/**
* As the name means.
@ -1538,7 +1483,7 @@ public class FragmentFiles extends Fragment implements
* @param filename
* @since v1.91
*/
private void doCheckSaveasFilenameAndFinish(String filename) {
private void checkSaveasFilenameAndFinish(String filename) {
if (!BaseFileProviderUtils.fileCanWrite(getActivity(),
getCurrentLocation())) {
Dlg.toast(getActivity(),
@ -1553,7 +1498,8 @@ public class FragmentFiles extends Fragment implements
return;
}
final Cursor cursor = getActivity().getContentResolver().query(
final Cursor cursor = BaseFileProviderUtils.queryInBackground(
getActivity(),
getCurrentLocation()
.buildUpon()
.appendQueryParameter(BaseFile.PARAM_APPEND_NAME,
@ -1583,7 +1529,7 @@ public class FragmentFiles extends Fragment implements
@Override
public void onClick(DialogInterface dialog,
int which) {
doFinish(uri);
finish(uri);
}// onClick()
});
@ -1593,7 +1539,7 @@ public class FragmentFiles extends Fragment implements
/*
* TODO file type unknown?
*/
doFinish(uri);
finish(uri);
break;// FILE_TYPE_NOT_EXISTED
}
}
@ -1601,7 +1547,7 @@ public class FragmentFiles extends Fragment implements
cursor.close();
}
}
}// doCheckSaveasFilenameAndFinish()
}// checkSaveasFilenameAndFinish()
/**
* Goes to a specified location.
@ -1616,7 +1562,7 @@ public class FragmentFiles extends Fragment implements
dir = BaseFileProviderUtils.getDefaultPath(getActivity(),
mFileProviderAuthority);
if (dir == null) {
doShowCannotConnectToServiceAndFinish();
showCannotConnectToServiceAndFinish();
return false;
}
@ -1655,7 +1601,7 @@ public class FragmentFiles extends Fragment implements
* Updates or inserts {@code path} into history database.
*/
private void updateDbHistory(Uri path) {
if (Utils.doLog())
if (BuildConfig.DEBUG)
Log.d(CLASSNAME, "updateDbHistory() >> path = " + path);
Calendar cal = Calendar.getInstance();
@ -1663,7 +1609,7 @@ public class FragmentFiles extends Fragment implements
- (cal.get(Calendar.HOUR_OF_DAY) * 60 * 60 * 1000
+ cal.get(Calendar.MINUTE) * 60 * 1000 + cal
.get(Calendar.SECOND) * 1000);
if (Utils.doLog()) {
if (BuildConfig.DEBUG) {
Log.d(CLASSNAME,
String.format("beginToday = %s (%s)", DbUtils
.formatNumber(beginTodayMillis), new Date(
@ -1730,8 +1676,8 @@ public class FragmentFiles extends Fragment implements
final int dim = getResources().getDimensionPixelSize(R.dimen.afc_5dp);
int count = 0;
Cursor cursor = getActivity().getContentResolver().query(path, null,
null, null, null);
Cursor cursor = BaseFileProviderUtils.queryInBackground(getActivity(),
path, null, null, null, null);
while (cursor != null) {
Uri lastUri = null;
if (cursor.moveToFirst()) {
@ -1771,7 +1717,8 @@ public class FragmentFiles extends Fragment implements
/*
* Process the parent directory.
*/
cursor = getActivity().getContentResolver().query(
cursor = BaseFileProviderUtils.queryInBackground(
getActivity(),
BaseFile.genContentUriApi(lastUri.getAuthority())
.buildUpon()
.appendPath(BaseFile.CMD_GET_PARENT)
@ -1809,12 +1756,12 @@ public class FragmentFiles extends Fragment implements
* @param files
* list of {@link Uri}.
*/
private void doFinish(Uri... files) {
private void finish(Uri... files) {
List<Uri> list = new ArrayList<Uri>();
for (Uri uri : files)
list.add(uri);
doFinish((ArrayList<Uri>) list);
}// doFinish()
finish((ArrayList<Uri>) list);
}// finish()
/**
* Finishes this activity.
@ -1822,7 +1769,7 @@ public class FragmentFiles extends Fragment implements
* @param files
* list of {@link Uri}.
*/
private void doFinish(ArrayList<Uri> files) {
private void finish(ArrayList<Uri> files) {
if (files == null || files.isEmpty()) {
getActivity().setResult(Activity.RESULT_CANCELED);
getActivity().finish();
@ -1842,7 +1789,7 @@ public class FragmentFiles extends Fragment implements
DisplayPrefs.setLastLocation(getActivity(), null);
getActivity().finish();
}// doFinish()
}// finish()
/**
* ******************************************************* BUTTON LISTENERS
@ -1852,10 +1799,12 @@ public class FragmentFiles extends Fragment implements
@Override
public void onClick(View v) {
doGoHome();
goHome();
}// onClick()
};// mBtnGoHomeOnClickListener
private final View.OnClickListener mBtnGoBackOnClickListener = new View.OnClickListener() {
@Override
@ -1894,7 +1843,7 @@ public class FragmentFiles extends Fragment implements
if (BaseFile.FILTER_FILES_ONLY == mFilterMode || mIsSaveDialog)
return false;
doFinish((Uri) v.getTag());
finish((Uri) v.getTag());
return false;
}// onLongClick()
@ -1922,6 +1871,7 @@ public class FragmentFiles extends Fragment implements
}// onClick()
};// mBtnGoForwardOnClickListener
private final TextView.OnEditorActionListener mTxtFilenameOnEditorActionListener = new TextView.OnEditorActionListener() {
@Override
@ -1941,7 +1891,7 @@ public class FragmentFiles extends Fragment implements
public void onClick(View v) {
Ui.showSoftKeyboard(v, false);
String filename = mTxtSaveas.getText().toString().trim();
doCheckSaveasFilenameAndFinish(filename);
checkSaveasFilenameAndFinish(filename);
}// onClick()
};// mBtnOk_SaveDialog_OnClickListener
@ -1949,7 +1899,7 @@ public class FragmentFiles extends Fragment implements
@Override
public void onClick(View v) {
doFinish(mFileAdapter.getSelectedItems());
finish(mFileAdapter.getSelectedItems());
}// onClick()
};// mBtnOk_OpenDialog_OnClickListener
@ -1957,7 +1907,7 @@ public class FragmentFiles extends Fragment implements
* FRAGMENT LISTENERS
*/
/*
* LISTVIEW HELPER
*/
@ -1986,10 +1936,10 @@ public class FragmentFiles extends Fragment implements
return;
if (mIsSaveDialog)
doCheckSaveasFilenameAndFinish(BaseFileProviderUtils
checkSaveasFilenameAndFinish(BaseFileProviderUtils
.getFileName(cursor));
else
doFinish(BaseFileProviderUtils.getUri(cursor));
finish(BaseFileProviderUtils.getUri(cursor));
}// single tap to choose files
}// onItemClick()
};// mViewFilesOnItemClickListener
@ -2009,7 +1959,7 @@ public class FragmentFiles extends Fragment implements
&& !mIsMultiSelection
&& BaseFileProviderUtils.isDirectory(cursor)
&& (BaseFile.FILTER_DIRECTORIES_ONLY == mFilterMode || BaseFile.FILTER_FILES_AND_DIRECTORIES == mFilterMode)) {
doFinish(BaseFileProviderUtils.getUri(cursor));
finish(BaseFileProviderUtils.getUri(cursor));
}
}// single tap to choose files
@ -2020,22 +1970,119 @@ public class FragmentFiles extends Fragment implements
}// onItemLongClick()
};// mViewFilesOnItemLongClickListener
private final BaseFileAdapter.OnBuildOptionsMenuListener mOnBuildOptionsMenuListener = new BaseFileAdapter.OnBuildOptionsMenuListener() {
@Override
public void onBuildOptionsMenu(View view, Cursor cursor) {
if (!BaseFileProviderUtils.fileCanRead(cursor)
|| !BaseFileProviderUtils.isDirectory(cursor))
return;
/**
* We use a {@link LoadingDialog} to avoid of
* {@code NetworkOnMainThreadException}.
*/
private LoadingDialog<Void, Void, Integer> mFileSelector;
final Uri uri = BaseFileProviderUtils.getUri(cursor);
final String name = BaseFileProviderUtils.getFileName(cursor);
/**
* Creates new {@link #mFileSelector} to select appropriate file after
* loading a folder's content. It's either the parent path of last path, or
* the file provided by key {@link FileChooserActivity#EXTRA_SELECT_FILE}.
* Note that this also cancels previous selector if there is such one.
*/
private void createFileSelector() {
if (mFileSelector != null)
mFileSelector.cancel(true);
}// onBuildOptionsMenu()
mFileSelector = new LoadingDialog<Void, Void, Integer>(getActivity(),
R.string.afc_msg_loading, true) {
@Override
protected Integer doInBackground(Void... params) {
final Cursor cursor = mFileAdapter.getCursor();
if (cursor == null || cursor.isClosed())
return -1;
final Uri selectedFile = (Uri) getArguments().getParcelable(
FileChooserActivity.EXTRA_SELECT_FILE);
final int colUri = cursor.getColumnIndex(BaseFile.COLUMN_URI);
if (selectedFile != null)
getArguments()
.remove(FileChooserActivity.EXTRA_SELECT_FILE);
int shouldBeSelectedIdx = -1;
final Uri uri = selectedFile != null ? selectedFile
: getLastLocation();
if (uri == null
|| !BaseFileProviderUtils
.fileExists(getActivity(), uri))
return -1;
final String fileName = BaseFileProviderUtils.getFileName(
getActivity(), uri);
if (fileName == null)
return -1;
Uri parentUri = BaseFileProviderUtils.getParentFile(
getActivity(), uri);
if ((uri == getLastLocation()
&& !getCurrentLocation().equals(getLastLocation()) && BaseFileProviderUtils
.isAncestorOf(getActivity(), getCurrentLocation(),
uri))
|| getCurrentLocation().equals(parentUri)) {
if (cursor.moveToFirst()) {
while (!cursor.isLast()) {
if (isCancelled())
return -1;
Uri subUri = Uri.parse(cursor.getString(colUri));
if (uri == getLastLocation()) {
if (cursor.getInt(cursor
.getColumnIndex(BaseFile.COLUMN_TYPE)) == BaseFile.FILE_TYPE_DIRECTORY) {
if (subUri.equals(uri)
|| BaseFileProviderUtils
.isAncestorOf(
getActivity(),
subUri, uri)) {
shouldBeSelectedIdx = Math.max(0,
cursor.getPosition() - 2);
break;
}
}
} else {
if (uri.equals(subUri)) {
shouldBeSelectedIdx = Math.max(0,
cursor.getPosition() - 2);
break;
}
}
cursor.moveToNext();
}// while
}// if
}// if
return shouldBeSelectedIdx;
}// doInBackground()
@Override
protected void onPostExecute(final Integer result) {
super.onPostExecute(result);
if (isCancelled() || mFileAdapter.isEmpty())
return;
/*
* Use a Runnable to make sure this works. Because if the list
* view is handling data, this might not work.
*/
mViewFiles.post(new Runnable() {
@Override
public void run() {
if (result >= 0 && result < mFileAdapter.getCount())
mViewFiles.setSelection(result);
else if (!mFileAdapter.isEmpty())
mViewFiles.setSelection(0);
}// run()
});
}// onPostExecute()
};
mFileSelector.execute();
}// createFileSelector()
@Override
public void onBuildAdvancedOptionsMenu(View view, Cursor cursor) {
// TODO Auto-generated method stub
}// onBuildAdvancedOptionsMenu()
};// mOnBuildOptionsMenuListener
}

View File

@ -7,6 +7,7 @@
package group.pals.android.lib.ui.filechooser.prefs;
import group.pals.android.lib.ui.filechooser.utils.Sys;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
@ -14,11 +15,10 @@ import android.os.Build;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import group.pals.android.lib.ui.filechooser.R;
/**
* Convenient class for working with preferences.
*
*
* @author Hai Bison
* @since v4.3 beta
*/
@ -26,60 +26,58 @@ public class Prefs {
/**
* This unique ID is used for storing preferences.
*
*
* @since v4.9 beta
*/
public static final String UID = "9795e88b-2ab4-4b81-a548-409091a1e0c6";
/**
* Generates global preference filename of this library.
*
* @param context the context.
*
* @return the global preference filename.
*/
public static final String genPreferenceFilename(Context context) {
return String.format("%s_%s", context.getString(R.string.afc_lib_name),
UID);
public static final String genPreferenceFilename() {
return String.format("%s_%s", Sys.LIB_NAME, UID);
}
/**
* Generates global database filename.
*
* @param context the context.
* @param name the database filename.
*
* @param name
* the database filename.
* @return the global database filename.
*/
public static final String genDatabaseFilename(Context context, String name) {
return String.format("%s_%s_%s",
context.getString(R.string.afc_lib_name), UID, name);
public static final String genDatabaseFilename(String name) {
return String.format("%s_%s_%s", Sys.LIB_NAME, UID, name);
}
/**
* Gets new {@link SharedPreferences}
*
* @param context the context.
*
* @param context
* the context.
* @return {@link SharedPreferences}
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static SharedPreferences p(Context context) {
// always use application context
return context.getApplicationContext().getSharedPreferences(
genPreferenceFilename(context), Context.MODE_MULTI_PROCESS);
genPreferenceFilename(), Context.MODE_MULTI_PROCESS);
}
/**
* Setup {@code pm} to use global unique filename and global access mode.
* You must use this method if you let the user change preferences via UI
* (such as {@link PreferenceActivity}, {@link PreferenceFragment}...).
*
* @param context the context.
* @param pm {@link PreferenceManager}.
*
* @param pm
* {@link PreferenceManager}.
* @since v4.9 beta
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static void setupPreferenceManager(Context context,
PreferenceManager pm) {
public static void setupPreferenceManager(PreferenceManager pm) {
pm.setSharedPreferencesMode(Context.MODE_MULTI_PROCESS);
pm.setSharedPreferencesName(genPreferenceFilename(context));
pm.setSharedPreferencesName(genPreferenceFilename());
}// setupPreferenceManager()
}

View File

@ -16,11 +16,14 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
/**
* Utilities for base file provider.
@ -115,11 +118,10 @@ public class BaseFileProviderUtils {
String result = getProviderName(providerId);
if (result == null) {
Cursor cursor = context
.getContentResolver()
.query(BaseFile
.genContentUriApi(getProviderAuthority(providerId)),
null, null, null, null);
Cursor cursor = queryInBackground(
context,
BaseFile.genContentUriApi(getProviderAuthority(providerId)),
null, null, null, null);
if (cursor == null)
return null;
@ -165,11 +167,10 @@ public class BaseFileProviderUtils {
int attr = MAP_PROVIDER_INFO.get(providerId).getInt(
BaseFile.COLUMN_PROVIDER_ICON_ATTR);
if (attr == 0) {
Cursor cursor = context
.getContentResolver()
.query(BaseFile
.genContentUriApi(getProviderAuthority(providerId)),
null, null, null, null);
Cursor cursor = queryInBackground(
context,
BaseFile.genContentUriApi(getProviderAuthority(providerId)),
null, null, null, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
@ -249,8 +250,8 @@ public class BaseFileProviderUtils {
* otherwise.
*/
public static boolean isDirectory(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null,
null, null);
//Log.d("AFC", "isDir? "+uri.toString());
Cursor cursor = queryInBackground(context, uri, null, null, null, null);
if (cursor == null)
return false;
@ -285,8 +286,8 @@ public class BaseFileProviderUtils {
* @return {@code true} if {@code uri} is a file, {@code false} otherwise.
*/
public static boolean isFile(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null,
null, null);
//Log.d("AFC", "isFile? "+uri.toString());
Cursor cursor = queryInBackground(context, uri, null, null, null, null);
if (cursor == null)
return false;
@ -320,8 +321,8 @@ public class BaseFileProviderUtils {
* @return the file name if {@code uri} is a file, {@code null} otherwise.
*/
public static String getFileName(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null,
null, null);
//Log.d("AFC", "getFileName "+uri.toString());
Cursor cursor = queryInBackground(context, uri, null, null, null, null);
if (cursor == null)
return null;
@ -358,8 +359,8 @@ public class BaseFileProviderUtils {
* @return the real URI of {@code uri}.
*/
public static Uri getRealUri(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null,
null, null);
//Log.d("AFC", "getRealUri "+uri.toString());
Cursor cursor = queryInBackground(context, uri, null, null, null, null);
if (cursor == null)
return null;
@ -399,8 +400,8 @@ public class BaseFileProviderUtils {
* {@link #FILE_TYPE_UNKNOWN}, {@link #FILE_TYPE_NOT_EXISTED}.
*/
public static int getFileType(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null,
null, null);
//Log.d("AFC", "filetype? "+uri.toString());
Cursor cursor = queryInBackground(context, uri, null, null, null, null);
if (cursor == null)
return BaseFile.FILE_TYPE_NOT_EXISTED;
@ -448,8 +449,8 @@ public class BaseFileProviderUtils {
* @return {@code true} or {@code false}.
*/
public static boolean fileExists(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null,
null, null);
//Log.d("AFC", "exists? "+uri.toString());
Cursor cursor = queryInBackground(context, uri, null, null, null, null);
if (cursor == null)
return false;
@ -473,8 +474,8 @@ public class BaseFileProviderUtils {
* @return {@code true} or {@code false}.
*/
public static boolean fileCanRead(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null,
null, null);
//Log.d("AFC", "canread? "+uri.toString());
Cursor cursor = queryInBackground(context, uri, null, null, null, null);
if (cursor == null)
return false;
@ -510,8 +511,8 @@ public class BaseFileProviderUtils {
* @return {@code true} or {@code false}.
*/
public static boolean fileCanWrite(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null,
null, null);
//Log.d("AFC", "canWrite? "+uri.toString());
Cursor cursor = queryInBackground(context, uri, null, null, null, null);
if (cursor == null)
return false;
@ -547,7 +548,8 @@ public class BaseFileProviderUtils {
* @return the default path, can be {@code null}.
*/
public static Uri getDefaultPath(Context context, String authority) {
Cursor cursor = context.getContentResolver().query(
Cursor cursor = queryInBackground(
context,
BaseFile.genContentUriApi(authority).buildUpon()
.appendPath(BaseFile.CMD_GET_DEFAULT_PATH).build(),
null, null, null, null);
@ -574,7 +576,8 @@ public class BaseFileProviderUtils {
* @return the parent file if it exists, {@code null} otherwise.
*/
public static Uri getParentFile(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(
Cursor cursor = queryInBackground(
context,
BaseFile.genContentUriApi(uri.getAuthority())
.buildUpon()
.appendPath(BaseFile.CMD_GET_PARENT)
@ -607,7 +610,8 @@ public class BaseFileProviderUtils {
* {@code false} otherwise.
*/
public static boolean isAncestorOf(Context context, Uri uri1, Uri uri2) {
return context.getContentResolver().query(
return queryInBackground(
context,
BaseFile.genContentUriApi(uri1.getAuthority())
.buildUpon()
.appendPath(BaseFile.CMD_IS_ANCESTOR_OF)
@ -629,7 +633,8 @@ public class BaseFileProviderUtils {
* the task ID.
*/
public static void cancelTask(Context context, String authority, int taskId) {
context.getContentResolver().query(
queryInBackground(
context,
BaseFile.genContentUriApi(authority)
.buildUpon()
.appendPath(BaseFile.CMD_CANCEL)
@ -637,4 +642,186 @@ public class BaseFileProviderUtils {
Integer.toString(taskId)).build(), null, null,
null, null);
}// cancelTask()
/**
* Creates new background thread to delete given URI, waits for the thread
* to finish (or be interrupted) and returns the result.
*
* @param context
* the context.
* @param uri
* the URI to delete, see
* {@link ContentResolver#delete(Uri, String, String[])} for more
* details.
* @param where
* the {@code WHERE} clause, see
* {@link ContentResolver#delete(Uri, String, String[])} for more
* details.
* @param selectionArgs
* the selection arguments, see
* {@link ContentResolver#delete(Uri, String, String[])} for more
* details.
* @return the value returned from
* {@link ContentResolver#delete(Uri, String, String[])} , or
* {@code -1} if an error occurred.
*/
public static int deleteInBackground(final Context context, final Uri uri,
final String where, final String[] selectionArgs) {
final int[] result = { 0 };
Thread thread = new Thread() {
@Override
public void run() {
result[0] = context.getContentResolver().delete(uri, where,
selectionArgs);
}// run()
};
thread.start();
try {
thread.join();
return result[0];
} catch (InterruptedException e) {
return -1;
}
}// deleteInBackground()
/**
* Creates new background thread to insert values to given URI, waits for
* the thread to finish (or be interrupted) and returns the result.
*
* @param context
* the context.
* @param uri
* the URI to insert values into, see
* {@link ContentResolver#insert(Uri, ContentValues)} for more
* details.
* @param values
* the values to insert into, see
* {@link ContentResolver#insert(Uri, ContentValues)} for more
* details.
* @return the URI returned from
* {@link ContentResolver#insert(Uri, ContentValues)}, or
* {@code null} if an error occurred.
*/
public static Uri insertInBackground(final Context context, final Uri uri,
final ContentValues values) {
final Uri[] result = { null };
Thread thread = new Thread() {
@Override
public void run() {
result[0] = context.getContentResolver().insert(uri, values);
}// run()
};
thread.start();
try {
thread.join();
return result[0];
} catch (InterruptedException e) {
return null;
}
}// insertInBackground()
/**
* Creates new background thread to query given URI, waits for the thread to
* finish (or be interrupted) and returns the result.
*
* @param context
* the context.
* @param uri
* the URI to query, see
* {@link ContentResolver#query(Uri, String[], String, String[], String)}
* for more details.
* @param projection
* the projection, see
* {@link ContentResolver#query(Uri, String[], String, String[], String)}
* for more details.
* @param selection
* the selection, see
* {@link ContentResolver#query(Uri, String[], String, String[], String)}
* for more details.
* @param selectionArgs
* the selection arguments, see
* {@link ContentResolver#query(Uri, String[], String, String[], String)}
* for more details.
* @param sortOrder
* the sort order, see
* {@link ContentResolver#query(Uri, String[], String, String[], String)}
* for more details.
* @return the cursor returned from
* {@link ContentResolver#query(Uri, String[], String, String[], String)}
* , or {@code null} if an error occurred.
*/
public static Cursor queryInBackground(final Context context,
final Uri uri, final String[] projection, final String selection,
final String[] selectionArgs, final String sortOrder) {
final Cursor[] result = { null };
Thread thread = new Thread() {
@Override
public void run() {
result[0] = context.getContentResolver().query(uri, projection,
selection, selectionArgs, sortOrder);
}// run()
};
thread.start();
try {
thread.join();
return result[0];
} catch (InterruptedException e) {
return null;
}
}// queryInBackground()
/**
* Creates new background thread to update given URI, waits for the thread
* to finish (or be interrupted) and returns the result.
*
* @param context
* the context.
* @param uri
* the URI to update, see
* {@link ContentResolver#update(Uri, ContentValues, String, String[])}
* for more details.
* @param values
* the values to update, see
* {@link ContentResolver#update(Uri, ContentValues, String, String[])}
* for more details.
* @param where
* the {@code WHERE} clause, see
* {@link ContentResolver#update(Uri, ContentValues, String, String[])}
* for more details.
* @param selectionArgs
* the selection arguments, see
* {@link ContentResolver#update(Uri, ContentValues, String, String[])}
* for more details.
* @return the value returned from
* {@link ContentResolver#update(Uri, ContentValues, String, String[])}
* , or {@code -1} if an error occurred.
*/
public static int updateInBackground(final Context context, final Uri uri,
final ContentValues values, final String where,
final String[] selectionArgs) {
final int[] result = { 0 };
Thread thread = new Thread() {
@Override
public void run() {
result[0] = context.getContentResolver().update(uri, values,
where, selectionArgs);
}// run()
};
thread.start();
try {
thread.join();
return result[0];
} catch (InterruptedException e) {
return -1;
}
}// updateInBackground()
}

View File

@ -7,16 +7,16 @@
package group.pals.android.lib.ui.filechooser.providers.history;
import group.pals.android.lib.ui.filechooser.prefs.Prefs;
import group.pals.android.lib.ui.filechooser.providers.DbUtils;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import group.pals.android.lib.ui.filechooser.prefs.Prefs;
import group.pals.android.lib.ui.filechooser.providers.DbUtils;
/**
* SQLite helper for history database.
*
*
* @author Hai Bison
* @since v5.1 beta
*/
@ -38,8 +38,8 @@ public class HistoryHelper extends SQLiteOpenHelper {
public HistoryHelper(Context context) {
// always use application context
super(context.getApplicationContext(), Prefs.genDatabaseFilename(
context, DB_FILENAME), null, DB_VERSION);
super(context.getApplicationContext(), Prefs
.genDatabaseFilename(DB_FILENAME), null, DB_VERSION);
}// HistoryHelper()
@Override
@ -54,4 +54,5 @@ public class HistoryHelper extends SQLiteOpenHelper {
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO
}// onUpgrade()
}

View File

@ -10,7 +10,6 @@ package group.pals.android.lib.ui.filechooser.providers.history;
import group.pals.android.lib.ui.filechooser.BuildConfig;
import group.pals.android.lib.ui.filechooser.R;
import group.pals.android.lib.ui.filechooser.providers.DbUtils;
import group.pals.android.lib.ui.filechooser.utils.Utils;
import java.util.Date;
@ -35,8 +34,8 @@ public class HistoryProviderUtils {
* @param context
* {@link Context}.
*/
public static void clearHistory(Context context) {
if (Utils.doLog())
public static void doCleanupOutdatedHistoryItems(Context context) {
if (BuildConfig.DEBUG)
Log.d(CLASSNAME, "doCleanupCache()");
try {
@ -47,9 +46,9 @@ public class HistoryProviderUtils {
final long validityInMillis = new Date().getTime()
- 0;
if (Utils.doLog())
if (BuildConfig.DEBUG)
Log.d(CLASSNAME, String.format(
"clearHistory() - validity = %,d (%s)",
"doCleanupCache() - validity = %,d (%s)",
validityInMillis, new Date(validityInMillis)));
context.getContentResolver().delete(
HistoryContract.genContentUri(context),
@ -61,5 +60,6 @@ public class HistoryProviderUtils {
* Currently we just ignore it.
*/
}
}// clearHistory()
}// doCleanupOutdatedHistoryItems()
}

View File

@ -303,7 +303,6 @@ public class LocalFileProvider extends BaseFileProvider {
newRow.add(type);
newRow.add(file.lastModified());
newRow.add(FileUtils.getResIcon(type, file.getName()));
} else if (BaseFile.CMD_SHUTDOWN.equals(uri.getLastPathSegment())) {
/*
* TODO Stop all tasks. If the activity call this command in

View File

@ -7,6 +7,7 @@
package group.pals.android.lib.ui.filechooser.utils;
import group.pals.android.lib.ui.filechooser.R;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
@ -15,28 +16,27 @@ import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import group.pals.android.lib.ui.filechooser.R;
/**
* Something funny :-)
*
*
* @author Hai Bison
*/
public class E {
/**
* Shows it!
*
* @param context {@link Context}
*
* @param context
* {@link Context}
*/
public static void show(Context context) {
String msg = null;
try {
msg = String.format("Hi :-)\n\n" + "%s %s\n"
msg = String.format("Hi :-)\n\n" + "%s v%s\n"
+ "…by Hai Bison Apps\n\n" + "http://www.haibison.com\n\n"
+ "Hope you enjoy this library.",
context.getString(R.string.afc_lib_name),
context.getString(R.string.afc_lib_version_name));
+ "Hope you enjoy this library.", Sys.LIB_NAME,
Sys.LIB_VERSION_NAME);
} catch (Exception e) {
msg = "Oops… You've found a broken Easter egg, try again later :-(";
}
@ -70,4 +70,5 @@ public class E {
dialog.setContentView(textView);
dialog.show();
}// show()
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2012 Hai Bison
*
* See the file LICENSE at the root directory of this project for copying
* permission.
*/
package group.pals.android.lib.ui.filechooser.utils;
/**
* System variables.
*
* @author Hai Bison
*
*/
public class Sys {
/**
* The library name.
*/
public static final String LIB_NAME = "android-filechooser";
/**
* The library version name.
*/
public static final String LIB_VERSION_NAME = "5.4.3 beta";
/**
* The library version code.
*/
public static final int LIB_VERSION_CODE = 54;
}

View File

@ -35,7 +35,7 @@ public class Utils {
public static boolean doLog()
{
return false;
return true;
//return BuildConfig.DEBUG; //not working with Mono for Android
}

View File

@ -21,7 +21,8 @@ import android.util.Log;
* @author Hai Bison
* @since v2.1 alpha
*/
public abstract class LoadingDialog extends AsyncTask<Void, Void, Object> {
public abstract class LoadingDialog<Params, Progress, Result> extends
AsyncTask<Params, Progress, Result> {
public static final String CLASSNAME = LoadingDialog.class.getName();
@ -62,7 +63,7 @@ public abstract class LoadingDialog extends AsyncTask<Void, Void, Object> {
}
});
}
}// LoadingDialog
}// LoadingDialog()
/**
* Creates new {@link LoadingDialog}
@ -76,12 +77,13 @@ public abstract class LoadingDialog extends AsyncTask<Void, Void, Object> {
*/
public LoadingDialog(Context context, int msgId, boolean cancelable) {
this(context, context.getString(msgId), cancelable);
}
}// LoadingDialog()
/**
* If you override this method, you must call {@code super.onPreExecute()}
* at very first of the method.
*/
@Override
protected void onPreExecute() {
new Handler().postDelayed(new Runnable() {
@ -107,7 +109,8 @@ public abstract class LoadingDialog extends AsyncTask<Void, Void, Object> {
* If you override this method, you must call
* {@code super.onPostExecute(result)} at the entry point of the method.
*/
protected void onPostExecute(Object result) {
@Override
protected void onPostExecute(Result result) {
doFinish();
}// onPostExecute()
@ -115,6 +118,7 @@ public abstract class LoadingDialog extends AsyncTask<Void, Void, Object> {
* If you override this method, you must call {@code super.onCancelled()} at
* the entry point of the method.
*/
@Override
protected void onCancelled() {
doFinish();
super.onCancelled();
@ -148,9 +152,10 @@ public abstract class LoadingDialog extends AsyncTask<Void, Void, Object> {
*
* @param delayTime
* the delay time to set
* @return {@link LoadingDialog}
* @return the instance of this dialog, for chaining multiple calls into a
* single statement.
*/
public LoadingDialog setDelayTime(int delayTime) {
public LoadingDialog<Params, Progress, Result> setDelayTime(int delayTime) {
mDelayTime = delayTime >= 0 ? delayTime : 0;
return this;
}// setDelayTime()
@ -174,4 +179,5 @@ public abstract class LoadingDialog extends AsyncTask<Void, Void, Object> {
protected Throwable getLastException() {
return mLastException;
}// getLastException()
}

View File

@ -11,7 +11,11 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
import android.content.ContentValues;
import android.database.Cursor;
@ -50,6 +54,12 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
* Used for debugging or something...
*/
private static final String CLASSNAME = Kp2aFileProvider.class.getName();
//cache for FileEntry objects to reduce network traffic
private HashMap<String, FileEntry> fileEntryMap = new HashMap<String, FileEntry>();
//during write operations it is not desired to put entries to the cache. This set indicates which
//files cannot be cached currently:
private Set<String> cacheBlockedFiles = new HashSet<String>();
@Override
@ -75,12 +85,16 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
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);
removeFromCache(filename, isRecursive);
blockFromCache(filename);
if (deletePath(filename, isRecursive))
{
getContext()
@ -96,7 +110,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
.build(), null);
count = 1; //success
}
blockFromCache(filename);
break;// URI_FILE
}
@ -113,6 +127,10 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
@Override
public Uri insert(Uri uri, ContentValues values) {
if (Utils.doLog())
@ -247,7 +265,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
return null;
}
String fname = getName(parentPath);
String fname = getFilenameFromPath(parentPath);
matrixCursor = BaseFileProviderUtils.newBaseFileCursor();
@ -291,7 +309,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
return matrixCursor;
}// doAnswerApiCommand()
private String getName(String path) {
protected String getFilenameFromPath(String path) {
path = removeTrailingSlash(path);
int lastSlashPos = path.lastIndexOf("/");
//if path is root, return its name. empty is ok
@ -365,6 +383,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
break;
FileEntry f = files.get(i);
updateFileEntryCache(f);
if (Utils.doLog())
Log.d(CLASSNAME, "listing " + f.path +" for "+dirName);
@ -396,9 +415,9 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
Boolean.toString(hasMoreFiles[0])).build()
.toString());
newRow.add(dirName);
newRow.add(getName(dirName));
newRow.add(getFilenameFromPath(dirName));
Log.d(CLASSNAME, "Returning name " + getName(dirName)+" for " +dirName);
Log.d(CLASSNAME, "Returning name " + getFilenameFromPath(dirName)+" for " +dirName);
}
}
@ -420,6 +439,8 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
return matrixCursor;
}// doListFiles()
private RowBuilder addFileInfo(MatrixCursor matrixCursor, int id,
FileEntry f) {
int type = !f.isDirectory ? BaseFile.FILE_TYPE_FILE : BaseFile.FILE_TYPE_DIRECTORY;
@ -431,7 +452,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
.buildUpon().appendPath(f.path)
.build().toString());
newRow.add(f.path);
newRow.add(getName(f.path));
newRow.add(getFilenameFromPath(f.path));
newRow.add(f.canRead ? 1 : 0);
newRow.add(f.canWrite ? 1 : 0);
newRow.add(f.sizeInBytes);
@ -440,7 +461,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
newRow.add(f.lastModifiedTime);
else
newRow.add(null);
newRow.add(FileUtils.getResIcon(type, getName(f.path)));
newRow.add(FileUtils.getResIcon(type, getFilenameFromPath(f.path)));
return newRow;
}
@ -458,9 +479,9 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
String filename = extractFile(uri);
FileEntry f = getFileEntry(filename);
FileEntry f = getFileEntryCached(filename);
if (f == null)
addDeletedFileInfo(matrixCursor, filename);
addDeletedFileInfo(matrixCursor, filename);
else
addFileInfo(matrixCursor, 0, f);
@ -469,8 +490,60 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
//puts the file entry in the cache for later reuse with retrieveFileInfo
private void updateFileEntryCache(FileEntry f) {
fileEntryMap.put(f.path, f);
}
//removes the file entry from the cache (if cached). Should be called whenever the file changes
private void removeFromCache(String filename, boolean recursive) {
fileEntryMap.remove(filename);
if (recursive)
{
Set<String> keys = fileEntryMap.keySet();
for (String key: keys)
{
if (key.startsWith(key))
fileEntryMap.remove(key);
}
}
}
private void blockFromCache(String filename) {
cacheBlockedFiles.add(filename);
}
private void unblockFromCache(String filename) {
cacheBlockedFiles.remove(filename);
}
private void addDeletedFileInfo(MatrixCursor matrixCursor, String filename) {
//returns the file entry from the cache if present or queries the concrete provider method to return the file info
private FileEntry getFileEntryCached(String filename) {
//check if enry is cached:
FileEntry cachedEntry = fileEntryMap.get(filename);
if (cachedEntry != null)
{
if (Utils.doLog())
Log.d(CLASSNAME, "getFileEntryCached: from cache. " + filename);
return cachedEntry;
}
if (Utils.doLog())
Log.d(CLASSNAME, "getFileEntryCached: not in cache :-( " + filename);
//it's not -> query the information.
FileEntry newEntry = getFileEntry(filename);
if (!cacheBlockedFiles.contains(filename))
updateFileEntryCache(newEntry);
return newEntry;
}
private void addDeletedFileInfo(MatrixCursor matrixCursor, String filename) {
int type = BaseFile.FILE_TYPE_NOT_EXISTED;
RowBuilder newRow = matrixCursor.newRow();
newRow.add(0);// _ID
@ -480,13 +553,13 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
.buildUpon().appendPath(filename)
.build().toString());
newRow.add(filename);
newRow.add(getName(filename));
newRow.add(getFilenameFromPath(filename));
newRow.add(0);
newRow.add(0);
newRow.add(0);
newRow.add(type);
newRow.add(null);
newRow.add(FileUtils.getResIcon(type, getName(filename)));
newRow.add(FileUtils.getResIcon(type, getFilenameFromPath(filename)));
}
/**

File diff suppressed because it is too large Load Diff