diff --git a/.github/ISSUE_TEMPLATE.md b/.github/old_ISSUE_TEMPLATE.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE.md
rename to .github/old_ISSUE_TEMPLATE.md
diff --git a/README.md b/README.md
index e7c1ed35..29fa6625 100644
--- a/README.md
+++ b/README.md
@@ -77,5 +77,6 @@ Privacy:
Feedback, translation and contributions are welcomed.
+* [Discuss pro and cons, features, featurerequests and to provide news and support about the app on reddit.](https://www.reddit.com/r/APhotoManager/)
* [Issue tracker](https://github.com/k3b/APhotoManager/issues)
* You can help to translate this app via https://crowdin.com/project/AndroFotoFinder
diff --git a/app/build.gradle b/app/build.gradle
index cbeebaa0..39a5813b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -48,9 +48,11 @@ android {
// 0.6.3.180211 (35) Extended Applocking; Autoprocessingmode for copy/move; Private images
// 0.6.4.180314 (36) Buggy: Fix/Improved Autoprocessingmode, Menu "open in filemanager" & "rename folder"-Crash in Settings
// 0.6.4.180321 (37) Bugfix for 0.6.4.180314 (36)
+ // 0.6.9.180813 (37) public betta: Searchbar, vitual-folder, new icons
+ // 0.7.0.180823 (38) public betta: Searchbar, vitual-folder, new icons
- versionCode = 37
- versionName = '0.6.4.180321'
+ versionCode = 38
+ versionName = '0.7.0.180823'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 00509ee7..8cf54129 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -20,6 +20,7 @@
-dontwarn com.caverock.androidsvg.**
-keep class org.xmlpull.** { *; }
-keep class de.k3b.android.widget.EditTextPreferenceWithSummary { *; }
+-keep class de.k3b.android.widget.SearchViewWithHistory { *; }
-assumenosideeffects class com.google.android.gms.ads.MobileAds { *; }
###############
diff --git a/app/src/debug/res/values-ar/fdroid.xml b/app/src/debug/res/values-ar/fdroid.xml
new file mode 100644
index 00000000..007e9a15
--- /dev/null
+++ b/app/src/debug/res/values-ar/fdroid.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+ A Photo Manager
+
+ مدير للصور المحلية يقوم بـ: بحث ونسخ وتعديل الصور ووضعها في معرض صور او في خريطة.
+
+
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1fa2a709..c2e771c1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -79,6 +79,7 @@
>
@@ -115,12 +116,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -181,8 +200,24 @@
+ android:icon="@drawable/album"
+ android:label="@string/filter_menu_title" >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -387,6 +422,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- */
-
-package de.k3b.android.androFotoFinder;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.util.Log;
-import android.widget.Toast;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import de.k3b.android.androFotoFinder.queries.FotoSql;
-import de.k3b.android.util.IntentUtil;
-import de.k3b.android.widget.Dialogs;
-import de.k3b.database.QueryParameter;
-import de.k3b.io.FileUtils;
-
-/**
- * Created by k3b on 07.10.2015.
- */
-public class BookmarkController {
- /** #76: used as default for save-as */
- private static final String STATE_LastBookmarkFileName = "LastBookmarkFileName";
-
- /** virtual bookmarkfile to reset is sourounden by this. */
- private static final String RESET_PREFIX = "<< ";
- private static final String RESET_SUFFIX = " >>";
-
- private QueryParameter mCurrentFilter = null;
-
- /** #76: used as default for save-as */
- private String mLastBookmarkFileName = null;
-
- private final Activity mContext;
-
- public interface IQueryConsumer {
- void setQuery(String fileName, QueryParameter newQuery);
- }
-
- public BookmarkController(Activity context) {
- mContext = context;
- }
-
- public static String getlastBookmarkFileName(Intent intent) {
- return (intent != null) ? intent.getStringExtra(BookmarkController.STATE_LastBookmarkFileName) : null;
- }
-
- public String getlastBookmarkFileName() {return mLastBookmarkFileName;}
- public void setlastBookmarkFileName(String lastBookmarkFileName) {mLastBookmarkFileName = lastBookmarkFileName;}
-
- public void saveState(Intent intent, Bundle savedInstanceState) {
- saveState(getlastBookmarkFileName(), intent, savedInstanceState);
- }
-
- public static void saveState(String lastBookmarkFileName, Intent intent, Bundle savedInstanceState) {
- if (savedInstanceState != null) {
- savedInstanceState.putString(BookmarkController.STATE_LastBookmarkFileName, lastBookmarkFileName);
- } else if (intent != null) {
- intent.putExtra(BookmarkController.STATE_LastBookmarkFileName, lastBookmarkFileName);
- }
- }
-
- public static boolean isReset(String bookmarkFilname) {
- return (bookmarkFilname != null) && bookmarkFilname.startsWith(RESET_PREFIX);
- }
-
- public static boolean isReset(Intent intent) {
- return isReset(getlastBookmarkFileName(intent));
- }
-
- public void loadState(Intent intent, Bundle savedInstanceState) {
- if (savedInstanceState != null) {
- setlastBookmarkFileName(savedInstanceState.getString(BookmarkController.STATE_LastBookmarkFileName, null));
- } else if (intent != null) {
- setlastBookmarkFileName(intent.getStringExtra(BookmarkController.STATE_LastBookmarkFileName));
- }
- }
-
- public void onSaveAsQuestion(final String name, final QueryParameter currentFilter) {
- mCurrentFilter = currentFilter;
- Dialogs dlg = new Dialogs() {
- @Override
- protected void onDialogResult(String fileName, Object[] parameters) {
- onSaveAsAnswer(fileName, true);
- }
-
- };
-
- if (isReset(name)) {
- dlg.editFileName(mContext, mContext.getString(R.string.bookmark_save_as_menu_title), null, 0);
- } else {
- dlg.editFileName(mContext, mContext.getString(R.string.bookmark_save_as_menu_title), name, 0);
- }
- }
-
- private void onSaveAsAnswer(final String fileName, boolean askToOverwrite) {
- if (Global.debugEnabled) {
- Log.d(Global.LOG_CONTEXT, "onSaveAsAnswer(" + fileName +
- ")");
- }
- if (fileName != null) {
- Global.reportDir.mkdirs();
- File outFile = getFile(fileName);
- if (askToOverwrite && outFile.exists()) {
- Dialogs dialog = new Dialogs() {
- @Override
- protected void onDialogResult(String result, Object... parameters) {
- if (result != null) {
- // yes, overwrite
- onSaveAsAnswer(fileName, false);
- } else {
- // no, do not overwrite
- onSaveAsQuestion(fileName, mCurrentFilter);
- }
- }
- };
- dialog.yesNoQuestion(mContext, mContext.getString(R.string.overwrite_question_title) ,
- mContext.getString(R.string.image_err_file_exists_format, outFile.getAbsoluteFile()));
- } else {
- PrintWriter out = null;
- try {
- out = new PrintWriter(outFile);
- out.println(mCurrentFilter.toReParseableString());
- out.close();
- out = null;
- } catch (IOException err) {
- String errorMessage = mContext.getString(R.string.mk_err_failed_format, outFile.getAbsoluteFile());
- Toast.makeText(mContext, errorMessage, Toast.LENGTH_LONG).show();
- Log.e(Global.LOG_CONTEXT, errorMessage, err);
- }
- }
- }
- }
-
- @NonNull
- private File getFile(String fileName) {
- String fileNameWithExt = fileName;
-
- if (!fileNameWithExt.contains(".")) {
- fileNameWithExt += Global.reportExt;
- }
- return new File(Global.reportDir, fileNameWithExt);
- }
-
- public void onLoadFromQuestion(final IQueryConsumer consumer, final QueryParameter currentFilter) {
- mCurrentFilter = currentFilter;
- List fileNamesPlusReset = new ArrayList();
- fileNamesPlusReset.add(RESET_PREFIX + mContext.getString(R.string.bookmark_reset) + RESET_SUFFIX);
- String[] fileNames = Global.reportDir.list(new FilenameFilter() {
- @Override public boolean accept(File dir, String filename) {
- return ((filename != null) && (filename.endsWith(Global.reportExt)));
- }
- });
-
- if ((fileNames != null) && (fileNames.length > 0)) {
- fileNamesPlusReset.addAll(Arrays.asList(fileNames));
- }
- Dialogs dlg = new Dialogs() {
- @Override protected boolean onContextMenuItemClick(int menuItemId, int itemIndex, String[] items) {
- return onBookmarkMenuItemClick(menuItemId, itemIndex, items);
- }
-
- @Override protected void onDialogResult(String fileName, Object[] parameters) {onLoadFromAnswer(fileName, consumer);}
- };
- dlg.pickFromStrings(mContext, mContext.getString(R.string.bookmark_load_from_menu_title), R.menu.menu_bookmark_context, fileNamesPlusReset);
- }
-
- public void onLoadFromAnswer(final String fileName, final IQueryConsumer consumer) {
- if (Global.debugEnabled) {
- Log.d(Global.LOG_CONTEXT, "onLoadFromAnswer(" + fileName + ")");
- }
-
- // #76: used as default for save-as
- mLastBookmarkFileName = fileName;
- if (fileName != null) {
- if (isReset(fileName)) {
- consumer.setQuery(fileName, new QueryParameter(FotoSql.queryDetail));
- } else {
- File inFile = getFile(fileName);
-
- String sql;
- try {
- sql = FileUtils.readFile(inFile);
- QueryParameter query = QueryParameter.parse(sql);
- consumer.setQuery(fileName, query);
- } catch (Exception e) {
- Toast.makeText(mContext,
- e.getMessage(),
- Toast.LENGTH_LONG).show();
- Log.e(Global.LOG_CONTEXT, "Error load query file '" + inFile.getAbsolutePath() + "'", e);
- e.printStackTrace();
- }
- }
- }
- }
-
- protected boolean onBookmarkMenuItemClick(int menuItemId, int itemIndex, String[] items) {
- if ((itemIndex > 0) && (itemIndex < items.length)) {
- switch (menuItemId) {
- case R.id.action_save_as:
- onSaveAsQuestion(mLastBookmarkFileName, mCurrentFilter); return true;
- case R.id.action_edit:
- return onEdit(getFile(items[itemIndex]));
- case R.id.menu_item_rename:
- return onRenameQuestion(items[itemIndex], items[itemIndex]);
- case R.id.cmd_delete:
- return onDeleteQuestion(itemIndex, items);
- default:break;
- }
- } // ignore index 0 = reset.
- return false;
- }
-
- private boolean onEdit(File file) {
- Intent sendIntent = new Intent();
- IntentUtil.setDataAndTypeAndNormalize(sendIntent, Uri.fromFile(file), "text/plain");
- sendIntent.setAction(Intent.ACTION_EDIT);
- mContext.startActivity(sendIntent);
- return true;
- }
-
- private boolean onRenameQuestion(String _newName, final String oldName) {
- if (_newName == null) return false;
-
- String newName = (_newName.endsWith(Global.reportExt))
- ? _newName.substring(0, _newName.length() - Global.reportExt.length())
- : _newName;
-
- Dialogs dialog = new Dialogs() {
- @Override
- protected void onDialogResult(String newFileName, Object... parameters) {
- if (newFileName != null) {
- onRenameAnswer(newFileName, oldName);
- }
- }
- };
- dialog.editFileName(mContext, mContext.getString(R.string.rename_menu_title), newName);
- return true;
- }
-
- private void onRenameAnswer(String newFileName, String oldName) {
- if ((newFileName != null) || (newFileName.length() > 0)) { // || (oldName.compareToIgnoreCase(newFileName) == 0)) {
- File from = getFile(oldName);
- File to = getFile(newFileName);
-
- if ((from.getAbsolutePath().compareToIgnoreCase(to.getAbsolutePath()) != 0) && !to.exists()) {
- from.renameTo(to);
- }
- }
- }
-
- private boolean onDeleteQuestion(final int itemIndex, final String[] items) {
- Dialogs dlg = new Dialogs() {
- @Override protected void onDialogResult(String result, Object[] parameters) {
- if (result != null) {
- onDeleteAnswer(getFile(items[itemIndex]), itemIndex, items);
- }
- }
- };
-
- dlg.yesNoQuestion(mContext, items[itemIndex], mContext.getString(R.string.bookmark_delete_question));
- return true;
- }
-
- private void onDeleteAnswer(File file, int itemIndex, String[] items) {
- if (file.exists() && file.delete()) {
- String message = mContext.getString(R.string.bookmark_delete_answer_format, file.getAbsoluteFile() );
- Toast.makeText(mContext,
- message,
- Toast.LENGTH_LONG).show();
- Log.d(Global.LOG_CONTEXT, message);
- items[itemIndex] = null;
- } else {
- String message = mContext.getString(R.string.bookmark_delete_error_format, file.getAbsoluteFile() );
- Toast.makeText(mContext,
- message,
- Toast.LENGTH_LONG).show();
- Log.d(Global.LOG_CONTEXT, message);
-
- }
- }
-
-}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/Common.java b/app/src/main/java/de/k3b/android/androFotoFinder/Common.java
index eaf4c37c..5165bc58 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/Common.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/Common.java
@@ -28,10 +28,10 @@
public interface Common {
/**
* gallery,filter:
- * Format:GalleryFilterParameter.toString/parseMultiple.
+ * Format:GalleryFilterParameter.toString/parse as a "," seperated list of values.
* See https://github.com/k3b/AndroFotoFinder/wiki/intentapi#filter
*/
- static final String EXTRA_FILTER = "de.k3b.extra.FILTER";
+ public static final String EXTRA_FILTER = "de.k3b.extra.FILTER";
/** detail,gallery: sql where ... order by ... group by ... */
public static final String EXTRA_QUERY = "de.k3b.extra.SQL";
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/FotoGalleryActivity.java b/app/src/main/java/de/k3b/android/androFotoFinder/FotoGalleryActivity.java
index b6382f67..8b1a3c67 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/FotoGalleryActivity.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/FotoGalleryActivity.java
@@ -20,410 +20,70 @@
package de.k3b.android.androFotoFinder;
import android.app.Activity;
-import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
-import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-import android.widget.Toast;
-
-// import com.squareup.leakcanary.RefWatcher;
-
-import java.util.ArrayList;
-import java.util.List;
import de.k3b.FotoLibGlobal;
import de.k3b.android.androFotoFinder.directory.DirectoryGui;
-import de.k3b.android.androFotoFinder.directory.DirectoryLoaderTask;
import de.k3b.android.androFotoFinder.gallery.cursor.GalleryCursorFragment;
import de.k3b.android.androFotoFinder.imagedetail.ImageDetailActivityViewPager;
import de.k3b.android.androFotoFinder.locationmap.GeoEditActivity;
-import de.k3b.android.androFotoFinder.locationmap.LocationMapFragment;
-import de.k3b.android.androFotoFinder.directory.DirectoryPickerFragment;
+import de.k3b.android.androFotoFinder.queries.AndroidAlbumUtils;
import de.k3b.android.androFotoFinder.queries.FotoSql;
import de.k3b.android.androFotoFinder.queries.FotoViewerParameter;
import de.k3b.android.androFotoFinder.queries.Queryable;
-import de.k3b.android.androFotoFinder.tagDB.TagSql;
-import de.k3b.android.androFotoFinder.tagDB.TagsPickerFragment;
-import de.k3b.android.osmdroid.OsmdroidUtil;
import de.k3b.android.util.GarbageCollector;
-import de.k3b.android.util.IntentUtil;
-import de.k3b.android.util.MediaScanner;
import de.k3b.android.widget.AboutDialogPreference;
-import de.k3b.android.widget.LocalizedActivity;
+import de.k3b.android.widget.BaseQueryActivity;
import de.k3b.database.QueryParameter;
import de.k3b.io.collections.SelectedItems;
-import de.k3b.io.Directory;
-import de.k3b.io.DirectoryFormatter;
-import de.k3b.io.GalleryFilterParameter;
-import de.k3b.io.GeoRectangle;
import de.k3b.io.IDirectory;
-import de.k3b.io.IGalleryFilter;
-import de.k3b.io.ListUtils;
-import de.k3b.tagDB.Tag;
-
-public class FotoGalleryActivity extends LocalizedActivity implements Common,
- OnGalleryInteractionListener, DirectoryPickerFragment.OnDirectoryInteractionListener,
- LocationMapFragment.OnDirectoryInteractionListener,
- TagsPickerFragment.ITagsPicker
-{
- private static final String mDebugPrefix = "GalleryA-";
-
- private static final String DEFAULT_BOOKMARKNAME_PICK_GEO = "pickGeoFromPhoto";
- /** intent parameters supported by FotoGalleryActivity: EXTRA_... */
-
- private static final String DLG_NAVIGATOR_TAG = "navigator";
-
- /** after media db change cached Directories must be recalculated */
- private final ContentObserver mMediaObserverDirectory = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- invalidateDirectories(mDebugPrefix + "#onChange from mMediaObserverDirectory");
- }
- };
- private final BookmarkController.IQueryConsumer mLoadBookmarkResultConsumer = new BookmarkController.IQueryConsumer() {
- @Override
- public void setQuery(String fileName, QueryParameter newQuery) {
- final IGalleryFilter whereFilter = TagSql.parseQueryEx(newQuery, true);
- mGalleryQueryParameter.mGalleryContentQuery = newQuery;
- mBookmarkController.setlastBookmarkFileName(fileName);
- String why = "#loadedBookmark " + fileName;
- onFilterChanged(whereFilter, why);
- invalidateDirectories(mDebugPrefix + why);
- mGalleryQueryParameter.setHasUserDefinedQuery(true);
- reloadGui(why);
- }
- };
- /** set while dir picker is active */
- private DialogFragment mDirPicker = null;
+/**
+ * Gallery: Show zeoro or more images in a grid optionally filtered by a
+ * * base query plus
+ * * sub query (either path or date or geo or tag or in-any)
+ *
+ * Use cases
+ * * stand alone gallery (i.e. from file manager with intent-uri of image.jpg/virtual-album/directory)
+ * * sub gallery drill down from other activity with intent containing file-uri/query-extra and/or filter-extra
+ * * as picker for
+ * * * ACTION_PICK uri=geo:... pick geo via image
+ * * * ACTION_PICK pick image old android-2.1 api
+ * * * ACTION_GET_CONTENT image new android-4.4 api
+ */
+public class FotoGalleryActivity extends BaseQueryActivity implements
+ OnGalleryInteractionListener {
+ /**
+ * intent parameters supported by FotoGalleryActivity: EXTRA_...
+ */
- private GalleryQueryParameter mGalleryQueryParameter = new GalleryQueryParameter();
// multi selection support
private SelectedItems mSelectedItems = null;
private Queryable mGalleryGui;
- private boolean mHasEmbeddedDirPicker = false;
private DirectoryGui mDirGui;
- private IDirectory mPopUpSelection = null;
-
- private String mTitleResultCount = "";
-
- private IDirectory mDirectoryRoot = null;
-
- /** true if activity should show navigator dialog after loading mDirectoryRoot is complete */
- private boolean mMustShowNavigator = false;
-
- private static class GalleryQueryParameter {
- private static final String STATE_SUB_FILTER_CurrentPath = "CurrentPath";
- private static final String STATE_DirQueryID = "DirQueryID";
- private static final String STATE_SortID = "SortID";
- private static final String STATE_SortAscending = "SortAscending";
- private static final String STATE_Filter = "filter";
- private static final String STATE_SUB_FILTER_LAT_LON = "currentLatLon";
- private static final String STATE_SUB_FILTER_TAGS = "currentTags";
- private static final String STATE_SUB_FILTR_MODE = "currentSubFilterMode";
- private static final String PICK_GEO_SUFFIX = "-pick-geo";
-
- /** true use latLonPicker; false use directoryPicker */
- private static final int SUB_FILTER_MODE_PATH = 0;
- private static final int SUB_FILTER_MODE_GEO = 1;
- private static final int SUB_FILTER_MODE_TAG = 2;
- private int mCurrentSubFilterMode = SUB_FILTER_MODE_PATH;
-
- private GeoRectangle mCurrentLatLonFromGeoAreaPicker = new GeoRectangle();
- private List mCurrentTagsFromPicker = new ArrayList();
-
- /** one of the FotoSql.QUERY_TYPE_xxx values */
- protected int mDirQueryID = FotoSql.QUERY_TYPE_GROUP_DEFAULT;
-
- private boolean mHasUserDefinedQuery = false;
-
- /** current sort order */
- private int mCurrentSortID = FotoSql.SORT_BY_DEFAULT;
- /** current sort order */
- private boolean mCurrentSortAscending = false;
-
- private String mCurrentPathFromFolderPicker = "/";
-
- /** Filter parameter defining current visible items */
- private IGalleryFilter mCurrentFilterSettings;
-
- /** sql defines current visible items with optional sort order */
- protected QueryParameter mGalleryContentQuery = null;
-
- /** true: if activity started without special intent-parameters,
- * the last mCurrentFilterSettings is saved/loaded for next use */
- private boolean mSaveToSharedPrefs = true;
-
- /** view/pick-image/pick-geo have different state persistence.
- * naem=STATE_XXXXX + mStatSuffix
- * ""==view; "-pick-image"; "-pick-geo" */
- private String mStatSuffix = "";
-
- /** one of the FotoSql.QUERY_TYPE_xxx values. if undefined use default */
- private int getDirQueryID() {
- if (this.mDirQueryID == FotoSql.QUERY_TYPE_UNDEFINED)
- return FotoSql.QUERY_TYPE_GROUP_DEFAULT;
-
- return this.mDirQueryID;
- }
-
- public int getSortID() {
- return mCurrentSortID;
- }
- public void setSortID(int sortID) {
- if (sortID == mCurrentSortID) {
- mCurrentSortAscending = !mCurrentSortAscending;
- } else {
- mCurrentSortAscending = true;
- mCurrentSortID = sortID;
- }
- }
-
- public String getSortDisplayName(Context context) {
- return FotoSql.getName(context, this.mCurrentSortID) + " " + ((mCurrentSortAscending) ? IGalleryFilter.SORT_DIRECTION_ASCENDING : IGalleryFilter.SORT_DIRECTION_DESCENDING);
- }
-
- public boolean clearPathIfActive() {
- if ((mCurrentSubFilterMode == SUB_FILTER_MODE_PATH) && (mCurrentPathFromFolderPicker != null)) {
- mCurrentPathFromFolderPicker = null;
- return true;
- }
- return false;
- }
-
- /** combine root-query plus current selected directoryRoot/geo/tags */
- private QueryParameter calculateEffectiveGalleryContentQuery() {
- return calculateEffectiveGalleryContentQuery(mGalleryContentQuery);
- }
-
- /** combine root-query plus current selected directoryRoot */
- private QueryParameter calculateEffectiveGalleryContentQuery(QueryParameter rootQuery) {
- if (rootQuery == null) return null;
-
- // .nomedia folder has no current sql
- if ((this.getCurrentFilterSettings() != null) && MediaScanner.isNoMedia(this.getCurrentFilterSettings().getPath(), MediaScanner.DEFAULT_SCAN_DEPTH)) {
- return null;
- }
-
- QueryParameter result = new QueryParameter(rootQuery);
-
- TagSql.filter2QueryEx(result, this.getCurrentFilterSettings(), !hasUserDefinedQuery());
- if (result == null) return null;
-
- if (mCurrentSubFilterMode == SUB_FILTER_MODE_GEO) {
- FotoSql.addWhereFilterLatLon(result, mCurrentLatLonFromGeoAreaPicker);
- } else if (mCurrentSubFilterMode == SUB_FILTER_MODE_TAG) {
- TagSql.addWhereTagsIncluded(result, mCurrentTagsFromPicker,false);
- } else if (this.mCurrentPathFromFolderPicker != null) {
- FotoSql.addPathWhere(result, this.mCurrentPathFromFolderPicker, this.getDirQueryID());
- }
-
- if (mCurrentSortID != IGalleryFilter.SORT_BY_NONE) {
- FotoSql.setSort(result, mCurrentSortID, mCurrentSortAscending);
- }
- return result;
- }
-
- private void saveInstanceState(Context context, Bundle savedInstanceState) {
- saveSettings(context);
-
- // save InstanceState
- savedInstanceState.putInt(STATE_DirQueryID , this.getDirQueryID());
- if (mStatSuffix.length() == 0) {
- savedInstanceState.putString(STATE_SUB_FILTER_LAT_LON, this.mCurrentLatLonFromGeoAreaPicker.toString());
- savedInstanceState.putString(STATE_SUB_FILTER_CurrentPath, this.mCurrentPathFromFolderPicker);
- savedInstanceState.putString(STATE_SUB_FILTER_TAGS, ListUtils.toString(mCurrentTagsFromPicker));
- }
- savedInstanceState.putInt(STATE_SortID, this.mCurrentSortID);
- savedInstanceState.putBoolean(STATE_SortAscending, this.mCurrentSortAscending);
- if (this.getCurrentFilterSettings() != null) {
- savedInstanceState.putString(STATE_Filter, this.getCurrentFilterSettings().toString());
- }
- savedInstanceState.putInt(STATE_SUB_FILTR_MODE, this.mCurrentSubFilterMode);
- }
-
- private void saveSettings(Context context) {
- if (mSaveToSharedPrefs) {
- // save settings
- SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
- SharedPreferences.Editor edit = sharedPref.edit();
-
- edit.putInt(STATE_DirQueryID + mStatSuffix, this.getDirQueryID());
- edit.putInt(STATE_SortID + mStatSuffix, this.mCurrentSortID);
- edit.putBoolean(STATE_SortAscending + mStatSuffix, this.mCurrentSortAscending);
-
- if (mStatSuffix.length() == 0) {
- edit.putString(STATE_SUB_FILTER_CurrentPath, this.mCurrentPathFromFolderPicker);
- edit.putString(STATE_SUB_FILTER_TAGS, ListUtils.toString(mCurrentTagsFromPicker));
- edit.putString(STATE_SUB_FILTER_LAT_LON, this.mCurrentLatLonFromGeoAreaPicker.toString());
- }
-
- if (getCurrentFilterSettings() != null) {
- edit.putString(STATE_Filter + mStatSuffix, getCurrentFilterSettings().toString());
- }
- edit.apply();
- }
- }
-
- public boolean isGeoPick() {
- return (mStatSuffix != null) && mStatSuffix.equals(PICK_GEO_SUFFIX);
- }
-
- // load from settings/instanceState
- private void loadSettingsAndInstanceState(Activity context, Bundle savedInstanceState) {
-
- Intent intent = context.getIntent();
-
- // for debugging: where does the filter come from
- StringBuilder dbgFilter = (Global.debugEnabled) ? new StringBuilder() : null;
- String filter = null;
- String pathFilter = null;
-
- if (intent != null) {
- filter = intent.getStringExtra(EXTRA_FILTER);
-
- if ((filter != null) && (dbgFilter != null)) dbgFilter.append("filter from ").append(EXTRA_FILTER).append("=").append(filter).append("\n");
-
- Uri uri = IntentUtil.getUri(intent);
- boolean fileUri = IntentUtil.isFileUri(uri);
- if (filter == null) {
-
- if (fileUri) {
- pathFilter = uri.getSchemeSpecificPart();
- if (pathFilter != null) pathFilter = pathFilter.replace('*', '%');
- if (dbgFilter != null) dbgFilter.append("path from uri=").append(pathFilter).append("\n");
- } else {
- String action = (intent != null) ? intent.getAction() : null;
-
- if ((action != null) && ((Intent.ACTION_PICK.compareTo(action) == 0) || (Intent.ACTION_GET_CONTENT.compareTo(action) == 0))) {
- mStatSuffix = "-pick-image";
- String schema = intent.getScheme();
- if ((schema != null) && ("geo".compareTo(schema) == 0)) {
- mStatSuffix = PICK_GEO_SUFFIX;
- }
- }
- }
- }
- this.mSaveToSharedPrefs = ((filter == null) && (pathFilter == null) && (!fileUri) ); // false if controlled via intent
- } else {
- this.mSaveToSharedPrefs = true;
- }
-
- SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
- if (this.mSaveToSharedPrefs) {
- if (mStatSuffix.length() == 0) {
- this.mCurrentLatLonFromGeoAreaPicker.get(DirectoryFormatter.parseLatLon(sharedPref.getString(STATE_SUB_FILTER_LAT_LON, null)));
- this.mCurrentPathFromFolderPicker = sharedPref.getString(STATE_SUB_FILTER_CurrentPath, this.mCurrentPathFromFolderPicker);
- this.mCurrentTagsFromPicker = new ArrayList<>(ListUtils.fromString(sharedPref.getString(STATE_SUB_FILTER_TAGS, ListUtils.toString(mCurrentTagsFromPicker))));
- }
- this.mDirQueryID = sharedPref.getInt(STATE_DirQueryID + mStatSuffix, this.getDirQueryID());
- this.mCurrentSortID = sharedPref.getInt(STATE_SortID + mStatSuffix, this.mCurrentSortID);
- this.mCurrentSortAscending = sharedPref.getBoolean(STATE_SortAscending + mStatSuffix, this.mCurrentSortAscending);
- }
-
- // instance state overrides settings
- if (savedInstanceState != null) {
- if (mStatSuffix.length() == 0) {
- this.mCurrentLatLonFromGeoAreaPicker.get(DirectoryFormatter.parseLatLon(savedInstanceState.getString(STATE_SUB_FILTER_LAT_LON)));
- this.mCurrentPathFromFolderPicker = savedInstanceState.getString(STATE_SUB_FILTER_CurrentPath, this.mCurrentPathFromFolderPicker);
- this.mCurrentTagsFromPicker = new ArrayList<>(ListUtils.fromString(savedInstanceState.getString(STATE_SUB_FILTER_TAGS, ListUtils.toString(mCurrentTagsFromPicker))));
- }
- this.mDirQueryID = savedInstanceState.getInt(STATE_DirQueryID, this.getDirQueryID());
- this.mCurrentSortID = savedInstanceState.getInt(STATE_SortID, this.mCurrentSortID);
- this.mCurrentSortAscending = savedInstanceState.getBoolean(STATE_SortAscending, this.mCurrentSortAscending);
- filter = savedInstanceState.getString(STATE_Filter);
- if ((filter != null) && (dbgFilter != null)) dbgFilter.append("filter from savedInstanceState=").append(filter).append("\n");
-
- this.mCurrentSubFilterMode = savedInstanceState.getInt(STATE_SUB_FILTR_MODE, this.mCurrentSubFilterMode);
- }
-
- if ((pathFilter == null) && (filter == null) && (this.getCurrentFilterSettings() == null)) {
- filter = sharedPref.getString(STATE_Filter + mStatSuffix, null);
- if ((filter != null) && (dbgFilter != null)) dbgFilter.append("filter from sharedPref=").append(filter).append("\n");
- }
-
- if (filter != null) {
- this.setCurrentFilterSettings(GalleryFilterParameter.parse(filter, new GalleryFilterParameter()));
- } else if (pathFilter != null) {
- if (!pathFilter.endsWith("%")) pathFilter += "%";
- this.setCurrentFilterSettings(new GalleryFilterParameter().setPath(pathFilter));
- }
-
- // extra parameter
- final String sqlString = intent.getStringExtra(EXTRA_QUERY);
- if (sqlString != null) {
- if (dbgFilter != null) dbgFilter.append("query from ").append(EXTRA_QUERY).append("\n\t").append(sqlString).append("\n");
- this.mGalleryContentQuery = QueryParameter.parse(sqlString);
- setHasUserDefinedQuery(true);
- }
-
- if (this.mGalleryContentQuery == null) this.mGalleryContentQuery = FotoSql.getQuery(FotoSql.QUERY_TYPE_DEFAULT);
-
- if (dbgFilter != null) {
- Log.i(Global.LOG_CONTEXT, mDebugPrefix + dbgFilter.toString());
- }
- }
-
- public boolean hasUserDefinedQuery() {
- return mHasUserDefinedQuery;
- }
-
- public void setHasUserDefinedQuery(boolean mHasUserDefinedQuery) {
- this.mHasUserDefinedQuery = mHasUserDefinedQuery;
- }
-
- public IGalleryFilter getCurrentFilterSettings() {
- return mCurrentFilterSettings;
- }
-
- public void setCurrentFilterSettings(IGalleryFilter newFilterSettings) {
- if ((newFilterSettings != null) && (PICK_GEO_SUFFIX.compareTo(mStatSuffix) == 0)) {
- // geopick: only photos that have lat/lon
- GalleryFilterParameter parameter = new GalleryFilterParameter().get(newFilterSettings);
- parameter.setNonGeoOnly(false);
- if (parameter.isEmpty()) {
- parameter.setLatitude(-90.0,90.0).setLogitude(-180.0,180.0);
- }
- this.mCurrentFilterSettings = parameter;
- } else {
- this.mCurrentFilterSettings = newFilterSettings;
- }
- }
- }
-
- private BookmarkController mBookmarkController = null;
/**
* shows a new instance of FotoGalleryActivity.
- *
- * @param context calling activity
- * @param filter if != null set initial filter to new FotoGalleryActivity
- * @param query if != null set initial filter to new FotoGalleryActivity
+ * @param context calling activity
+ * @param query if != null set initial filter to new FotoGalleryActivity
* @param requestCode if != 0 start for result. else start without result
*/
- public static void showActivity(Activity context, GalleryFilterParameter filter, QueryParameter query, int requestCode) {
+ public static void showActivity(Activity context, QueryParameter query, int requestCode) {
Intent intent = new Intent(context, FotoGalleryActivity.class);
- if (filter != null) {
- intent.putExtra(EXTRA_FILTER, filter.toString());
- }
-
- if (query != null) {
- intent.putExtra(EXTRA_QUERY, query.toReParseableString());
- }
+ AndroidAlbumUtils.saveFilterAndQuery(context, null, intent, null, null, query);
if (requestCode != 0) {
context.startActivityForResult(intent, requestCode);
@@ -433,14 +93,7 @@ public static void showActivity(Activity context, GalleryFilterParameter filter,
}
@Override
- public void onSaveInstanceState(Bundle savedInstanceState) {
- this.mGalleryQueryParameter.saveInstanceState(this, savedInstanceState);
- mBookmarkController.saveState(null, savedInstanceState);
- super.onSaveInstanceState(savedInstanceState);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState){
+ protected void onCreate(Bundle savedInstanceState) {
Global.debugMemory(mDebugPrefix, "onCreate");
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
@@ -449,23 +102,13 @@ protected void onCreate(Bundle savedInstanceState){
// not implemented yet
FotoLibGlobal.itpcWriteSupport = false;
}
- if (Global.debugEnabled && (intent != null)){
+ if (Global.debugEnabled && (intent != null)) {
Log.d(Global.LOG_CONTEXT, mDebugPrefix + "onCreate " + intent.toUri(Intent.URI_INTENT_SCHEME));
}
- this.getContentResolver().registerContentObserver(FotoSql.SQL_TABLE_EXTERNAL_CONTENT_URI, true, mMediaObserverDirectory);
- this.getContentResolver().registerContentObserver(FotoSql.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, true, mMediaObserverDirectory);
setContentView(R.layout.activity_gallery); // .gallery_activity);
- this.mGalleryQueryParameter.loadSettingsAndInstanceState(this, savedInstanceState);
-
- mBookmarkController = new BookmarkController(this);
- mBookmarkController.loadState(intent,savedInstanceState);
-
- if (this.mGalleryQueryParameter.isGeoPick()) {
- // #76: load predefined bookmark file
- this.mBookmarkController.onLoadFromAnswer(DEFAULT_BOOKMARKNAME_PICK_GEO, this.mLoadBookmarkResultConsumer);
- }
+ onCreateData(savedInstanceState);
FragmentManager fragmentManager = getFragmentManager();
mGalleryGui = (Queryable) fragmentManager.findFragmentById(R.id.galleryCursor);
@@ -496,36 +139,22 @@ protected void onCreate(Bundle savedInstanceState){
}
@Override
- protected void onPause () {
- Global.debugMemory(mDebugPrefix, "onPause");
- this.mGalleryQueryParameter.saveSettings(this);
- super.onPause();
- }
-
- @Override
- protected void onResume () {
- Global.debugMemory(mDebugPrefix, "onResume");
+ public void onResume() {
super.onResume();
- }
-
- @Override public void onLowMemory() {
- super.onLowMemory();
- invalidateDirectories(mDebugPrefix + "#onLowMemory");
+ if (mGalleryGui instanceof GalleryCursorFragment) {
+ this.mSelectedItems = ((GalleryCursorFragment) mGalleryGui).getSelectedItems();
+ }
}
@Override
protected void onDestroy() {
Global.debugMemory(mDebugPrefix, "onDestroy start");
super.onDestroy();
- this.getContentResolver().unregisterContentObserver(mMediaObserverDirectory);
- mPopUpSelection = null;
+
// to avoid memory leaks
GarbageCollector.freeMemory(findViewById(R.id.root_view));
-
- this.mGalleryQueryParameter.mGalleryContentQuery = null;
mGalleryGui = null;
mDirGui = null;
- invalidateDirectories(mDebugPrefix + "#onDestroy");
System.gc();
Global.debugMemory(mDebugPrefix, "onDestroy end");
@@ -550,7 +179,8 @@ public boolean onCreateOptionsMenu(Menu menu) {
Global.fixMenu(this, menu);
}
- return super.onCreateOptionsMenu(menu);
+ final boolean result = super.onCreateOptionsMenu(menu);
+ return result;
}
@Override
@@ -569,62 +199,7 @@ public boolean onPrepareOptionsMenu(Menu menu) {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- // Handle presses on the action bar items
switch (item.getItemId()) {
- case R.id.cmd_select_folder:
- openFolderPicker();
- return true;
-
- case R.id.cmd_select_lat_lon:
- openLatLonPicker();
- return true;
- case R.id.cmd_select_tag:
- openTagPicker();
- return true;
- case R.id.cmd_filter:
- openFilter();
- return true;
- case R.id.cmd_load_bookmark:
- loadBookmark();
- return true;
- case R.id.cmd_sort_date:
- this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_DATE);
- reloadGui("sort date");
- return true;
- case R.id.cmd_sort_directory:
- this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_NAME);
- reloadGui("sort dir");
- return true;
- case R.id.cmd_sort_path_len:
- this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_NAME_LEN);
- reloadGui("sort len");
- return true;
- case R.id.cmd_sort_file_len:
- this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_FILE_LEN);
- reloadGui("sort size");
- return true;
-
- case R.id.cmd_sort_width:
- this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_WIDTH);
- reloadGui("sort width");
- return true;
-
- case R.id.cmd_sort_location:
- this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_LOCATION);
- reloadGui("sort geo");
- return true;
-
- case R.id.cmd_sort_rating:
- this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_RATING);
- reloadGui("sort rating");
- return true;
-
- case R.id.cmd_sort_modification:
- this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_MODIFICATION);
- reloadGui("sort modification");
- return true;
-
-
case R.id.cmd_settings:
SettingsActivity.show(this);
return true;
@@ -640,14 +215,11 @@ public void run() {
}, 200);
return true;
default:
- return super.onOptionsItemSelected(item);
+ return onOptionsItemSelected(item, this.mSelectedItems);
}
}
- private void loadBookmark() {
- mBookmarkController.onLoadFromQuestion(mLoadBookmarkResultConsumer, this.mGalleryQueryParameter.calculateEffectiveGalleryContentQuery());
- }
/**
* Call back from sub-activities.
* Process Change StartTime (longpress start), Select StopTime before stop
@@ -661,20 +233,7 @@ protected void onActivityResult(final int requestCode,
((Fragment) mGalleryGui).onActivityResult(requestCode, resultCode, intent);
}
- if (mDirPicker instanceof Fragment) {
- ((Fragment) mDirPicker).onActivityResult(requestCode, resultCode, intent);
- }
-
- if (mPopUpSelection != null) mPopUpSelection.refresh();
-
switch (requestCode) {
- case GalleryFilterActivity.resultID :
- if (BookmarkController.isReset(intent)) {
- mGalleryQueryParameter.mGalleryContentQuery = new QueryParameter(FotoSql.queryDetail);
- }
- mBookmarkController.loadState(intent, null);
- onFilterChanged(GalleryFilterActivity.getFilter(intent), mDebugPrefix + "#onActivityResult from GalleryFilterActivity");
- break;
case ImageDetailActivityViewPager.ACTIVITY_ID:
if (resultCode == ImageDetailActivityViewPager.RESULT_CHANGE) {
invalidateDirectories(mDebugPrefix + "#onActivityResult from ImageDetailActivityViewPager");
@@ -685,124 +244,11 @@ protected void onActivityResult(final int requestCode,
invalidateDirectories(mDebugPrefix + "#onActivityResult from GeoEditActivity");
}
break;
- default:break;
- }
- }
-
- private void onFilterChanged(IGalleryFilter filter, String why) {
- if (filter != null) {
- this.mGalleryQueryParameter.setCurrentFilterSettings(filter);
- this.mGalleryQueryParameter.setHasUserDefinedQuery(false);
-
- invalidateDirectories(mDebugPrefix + "#filter changed " + why);
-
- reloadGui("filter changed");
- }
- }
-
- private void openLatLonPicker() {
- mGalleryQueryParameter.mCurrentSubFilterMode = GalleryQueryParameter.SUB_FILTER_MODE_GEO;
-
- final FragmentManager manager = getFragmentManager();
- LocationMapFragment dialog = new LocationMapFragment();
- dialog.defineNavigation(this.mGalleryQueryParameter.getCurrentFilterSettings(),
- this.mGalleryQueryParameter.mCurrentLatLonFromGeoAreaPicker, OsmdroidUtil.NO_ZOOM, mSelectedItems, null);
-
- dialog.show(manager, DLG_NAVIGATOR_TAG);
- }
-
- private void openTagPicker() {
- mGalleryQueryParameter.mCurrentSubFilterMode = GalleryQueryParameter.SUB_FILTER_MODE_TAG;
-
- final FragmentManager manager = getFragmentManager();
- TagsPickerFragment dlg = new TagsPickerFragment();
- dlg.setFragmentOnwner(this);
- dlg.setTitleId(R.string.tags_activity_title);
- dlg.setAddNames(mGalleryQueryParameter.mCurrentTagsFromPicker);
- dlg.show(manager, DLG_NAVIGATOR_TAG);
- }
-
- /** called by {@link TagsPickerFragment} */
- @Override
- public boolean onCancel(String msg) {
- return true;
- }
-
- /** called by {@link TagsPickerFragment} */
- @Override
- public boolean onOk(List addNames, List removeNames) {
- Log.d(Global.LOG_CONTEXT, "FotoGalleryActivity.navigateTo " + ListUtils.toString(addNames) + " from "
- + ListUtils.toString(mGalleryQueryParameter.mCurrentTagsFromPicker));
- mGalleryQueryParameter.mCurrentTagsFromPicker = new ArrayList(addNames);
- reloadGui("navigate to tags");
- return true;
- }
-
- /** called by {@link TagsPickerFragment} */
- @Override
- public boolean onTagPopUpClick(int menuItemItemId, Tag selectedTag) {
- return TagsPickerFragment.handleMenuShow(menuItemItemId, selectedTag, this, this.mGalleryQueryParameter.getCurrentFilterSettings());
- }
-
- private void openFolderPicker() {
- mGalleryQueryParameter.mCurrentSubFilterMode = GalleryQueryParameter.SUB_FILTER_MODE_PATH;
-
- int dirQueryID = this.mGalleryQueryParameter.getDirQueryID();
-
- /** if wrong datatype was saved: gallery is not allowed for dirPicker */
- if (FotoSql.QUERY_TYPE_GALLERY == dirQueryID) {
- dirQueryID = FotoSql.QUERY_TYPE_GROUP_ALBUM;
- }
-
- if (mDirectoryRoot == null) {
- // not loaded yet. load directoryRoot in background
- final QueryParameter currentDirContentQuery = new QueryParameter(FotoSql.getQuery(dirQueryID));
- TagSql.filter2QueryEx(currentDirContentQuery, this.mGalleryQueryParameter.getCurrentFilterSettings(),
- this.mGalleryQueryParameter.getSortID() != IGalleryFilter.SORT_BY_NONE);
-
- this.mGalleryQueryParameter.mDirQueryID = (currentDirContentQuery != null) ? currentDirContentQuery.getID() : FotoSql.QUERY_TYPE_UNDEFINED;
-
- if (currentDirContentQuery != null) {
- this.mMustShowNavigator = true;
- DirectoryLoaderTask loader = new DirectoryLoaderTask(this, mDebugPrefix) {
- @Override
- protected void onPostExecute(IDirectory directoryRoot) {
- onDirectoryDataLoadComplete(directoryRoot);
- }
- };
- loader.execute(currentDirContentQuery);
- } else {
- Log.e(Global.LOG_CONTEXT, mDebugPrefix + " this.mDirQueryID undefined "
- + this.mGalleryQueryParameter.mDirQueryID);
- }
- } else {
- this.mMustShowNavigator = false;
- final FragmentManager manager = getFragmentManager();
- DirectoryPickerFragment dirDialog =new DirectoryPickerFragment() {
- protected boolean onPopUpClick(MenuItem menuItem, IDirectory popUpSelection) {
- mPopUpSelection = popUpSelection;
- return super.onPopUpClick(menuItem, popUpSelection);
- }
- };
-
- // (DirectoryPickerFragment) manager.findFragmentByTag(DLG_NAVIGATOR_TAG);
- dirDialog.setContextMenuId(LockScreen.isLocked(this) ? 0 : R.menu.menu_context_dirpicker);
-
- dirDialog.defineDirectoryNavigation(mDirectoryRoot, dirQueryID,
- this.mGalleryQueryParameter.mCurrentPathFromFolderPicker);
-
- mDirPicker = dirDialog;
- dirDialog.show(manager, DLG_NAVIGATOR_TAG);
+ default:
+ break;
}
}
- private void openFilter() {
- GalleryFilterActivity.showActivity(this,
- this.mGalleryQueryParameter.getCurrentFilterSettings(),
- this.mGalleryQueryParameter.mGalleryContentQuery,
- mBookmarkController.getlastBookmarkFileName(), GalleryFilterActivity.resultID);
- }
-
/** called by Fragment: a fragment Item was clicked */
@Override
public void onGalleryImageClick(long imageId, Uri imageUri, int position) {
@@ -811,88 +257,16 @@ public void onGalleryImageClick(long imageId, Uri imageUri, int position) {
ImageDetailActivityViewPager.showActivity(this, imageUri, position, imageDetailQuery, ImageDetailActivityViewPager.ACTIVITY_ID);
}
- /** GalleryFragment tells the Owning Activity that querying data has finisched */
- @Override
- public void setResultCount(int count) {
- this.mTitleResultCount = (count > 0) ? ("(" + count + ")") : "";
- setTitle();
-
- // current path does not contain photo => refreshLocal witout current path
- if ((count == 0) &&(mGalleryQueryParameter.clearPathIfActive())) {
- setTitle();
- reloadGui("query changed");
- }
- }
-
- /**
- * called when user selects a new directoryRoot
- */
- @Override
- public void onDirectoryPick(String selectedAbsolutePath, int queryTypeId) {
- if (!this.mHasEmbeddedDirPicker) {
- navigateTo(selectedAbsolutePath, queryTypeId);
- }
- mDirPicker = null;
- }
-
@Override
- public void invalidateDirectories(String why) {
-
- if (mDirectoryRoot != null) {
- if (Global.debugEnabled) {
- StringBuilder name = new StringBuilder(mDirectoryRoot.getAbsolute());
- Directory.appendCount(name, mDirectoryRoot, Directory.OPT_DIR | Directory.OPT_SUB_DIR);
- Log.i(Global.LOG_CONTEXT, mDebugPrefix + "invalidateDirectories(" + name + ") because of " + why);
- }
- if (mDirPicker == null) {
- mDirectoryRoot.destroy();
- mDirectoryRoot = null; // must refreshLocal next time
- }
+ protected void defineDirectoryNavigation(IDirectory directoryRoot) {
+ if (mDirGui != null) {
+ mDirGui.defineDirectoryNavigation(directoryRoot, mGalleryQueryParameter.getDirQueryID(),
+ mGalleryQueryParameter.getCurrentSubFilterSettings().getPath());
}
}
- /**
- * called when user cancels selection of a new directoryRoot
- */
- @Override
- public void onDirectoryCancel(int queryTypeId) {
- mDirPicker = null;
- }
-
- /** called after the selection in tree has changed */
@Override
- public void onDirectorySelectionChanged(String selectedAbsolutePath, int queryTypeId) {
- if (this.mHasEmbeddedDirPicker) {
- navigateTo(selectedAbsolutePath, queryTypeId);
- }
- }
-
- private void navigateTo(String selectedAbsolutePath, int queryTypeId) {
-
- if (selectedAbsolutePath != null) {
- if (mGalleryQueryParameter.mCurrentSubFilterMode == GalleryQueryParameter.SUB_FILTER_MODE_GEO) {
- Log.d(Global.LOG_CONTEXT, "FotoGalleryActivity.navigateTo " + selectedAbsolutePath + " from " + mGalleryQueryParameter.mCurrentLatLonFromGeoAreaPicker);
- this.mGalleryQueryParameter.mCurrentLatLonFromGeoAreaPicker.get(DirectoryFormatter.parseLatLon(selectedAbsolutePath));
-
- reloadGui("navigate to geo");
-
- } else if (mGalleryQueryParameter.mCurrentSubFilterMode == GalleryQueryParameter.SUB_FILTER_MODE_TAG) {
- Log.d(Global.LOG_CONTEXT, "FotoGalleryActivity.navigateTo " + selectedAbsolutePath + " from "
- + ListUtils.toString(this.mGalleryQueryParameter.mCurrentTagsFromPicker));
- this.mGalleryQueryParameter.mCurrentTagsFromPicker = new ArrayList<>(ListUtils.fromString(selectedAbsolutePath));
- reloadGui("navigate to tags");
- } else if (mGalleryQueryParameter.mCurrentSubFilterMode == GalleryQueryParameter.SUB_FILTER_MODE_PATH) {
- Log.d(Global.LOG_CONTEXT, "FotoGalleryActivity.navigateTo " + selectedAbsolutePath + " from " + this.mGalleryQueryParameter.mCurrentPathFromFolderPicker);
- this.mGalleryQueryParameter.mCurrentPathFromFolderPicker = selectedAbsolutePath;
- this.mGalleryQueryParameter.mDirQueryID = queryTypeId;
- setTitle();
-
- reloadGui("navigate to dir");
- }
- }
- }
-
- private void reloadGui(String why) {
+ protected void reloadGui(String why) {
if (mGalleryGui != null) {
QueryParameter query = this.mGalleryQueryParameter.calculateEffectiveGalleryContentQuery();
if (query != null) {
@@ -901,63 +275,17 @@ private void reloadGui(String why) {
}
if (mDirGui != null) {
- String currentPath = this.mGalleryQueryParameter.mCurrentPathFromFolderPicker;
+ String currentPath = this.mGalleryQueryParameter.getCurrentSubFilterSettings().getPath();
if (currentPath != null) {
mDirGui.navigateTo(currentPath);
}
}
}
- private void onDirectoryDataLoadComplete(IDirectory directoryRoot) {
- if (directoryRoot == null) {
- final String message = getString(R.string.folder_err_load_failed_format, FotoSql.getName(this, this.mGalleryQueryParameter.getDirQueryID()));
- Toast.makeText(this, message,Toast.LENGTH_LONG).show();
- } else {
- mDirectoryRoot = directoryRoot;
- final boolean mustDefineNavigation = (mDirGui != null) && (this.mGalleryQueryParameter.mCurrentPathFromFolderPicker != null);
- final boolean mustShowFolderPicker = (mDirectoryRoot != null) && (this.mMustShowNavigator);
-
- if (Global.debugEnabled) {
- StringBuilder name = new StringBuilder(mDirectoryRoot.getAbsolute());
- Directory.appendCount(name, mDirectoryRoot, Directory.OPT_DIR | Directory.OPT_SUB_DIR);
- Log.i(Global.LOG_CONTEXT, mDebugPrefix + "onDirectoryDataLoadComplete(" +
- "mustDefineNavigation=" + mustDefineNavigation +
- ", mustShowFolderPicker=" + mustShowFolderPicker +
- ", content=" + name + ")");
- }
-
- if (mustDefineNavigation) {
- mDirGui.defineDirectoryNavigation(directoryRoot, this.mGalleryQueryParameter.getDirQueryID(), this.mGalleryQueryParameter.mCurrentPathFromFolderPicker);
- }
- Global.debugMemory(mDebugPrefix, "onDirectoryDataLoadComplete");
-
- if (mustShowFolderPicker) {
- openFolderPicker();
- }
- }
- }
-
- private void setTitle() {
- Intent intent = getIntent();
- String title = (intent == null) ? null : intent.getStringExtra(EXTRA_TITLE);
-
- if (title == null) {
- if (mGalleryQueryParameter.mCurrentSubFilterMode == GalleryQueryParameter.SUB_FILTER_MODE_GEO) {
- title = getString(R.string.gallery_title);
- } else if (this.mGalleryQueryParameter.mCurrentPathFromFolderPicker != null) {
- title = FotoSql.getName(this, this.mGalleryQueryParameter.getDirQueryID())
- + " - " + this.mGalleryQueryParameter.mCurrentPathFromFolderPicker;
- } else {
- title = FotoSql.getName(this, this.mGalleryQueryParameter.getDirQueryID());
- }
- }
- if (title != null) {
- this.setTitle(title + mTitleResultCount);
- }
- }
-
@Override
public String toString() {
return mDebugPrefix + "->" + this.mGalleryGui;
}
+
+
}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/GalleryFilterActivity.java b/app/src/main/java/de/k3b/android/androFotoFinder/GalleryFilterActivity.java
index f7cb14fe..e3f7420a 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/GalleryFilterActivity.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/GalleryFilterActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder / #APhotoManager.
*
@@ -20,9 +20,11 @@
package de.k3b.android.androFotoFinder;
import android.app.Activity;
+import android.app.DialogFragment;
import android.app.FragmentManager;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
@@ -35,6 +37,7 @@
import android.widget.RatingBar;
import android.widget.Toast;
+import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
@@ -44,25 +47,33 @@
import de.k3b.FotoLibGlobal;
import de.k3b.android.androFotoFinder.directory.DirectoryLoaderTask;
import de.k3b.android.androFotoFinder.directory.DirectoryPickerFragment;
+import de.k3b.android.androFotoFinder.imagedetail.ImageDetailMetaDialogBuilder;
import de.k3b.android.androFotoFinder.locationmap.LocationMapFragment;
import de.k3b.android.androFotoFinder.locationmap.MapGeoPickerActivity;
+import de.k3b.android.androFotoFinder.queries.AndroidAlbumUtils;
import de.k3b.android.androFotoFinder.queries.FotoSql;
import de.k3b.android.androFotoFinder.tagDB.TagSql;
import de.k3b.android.androFotoFinder.tagDB.TagsPickerFragment;
import de.k3b.android.osmdroid.OsmdroidUtil;
+import de.k3b.android.util.OsUtils;
import de.k3b.android.widget.AboutDialogPreference;
import de.k3b.android.widget.ActivityWithAutoCloseDialogs;
+import de.k3b.android.widget.BaseQueryActivity;
import de.k3b.android.widget.HistoryEditText;
import de.k3b.database.QueryParameter;
+import de.k3b.io.AlbumFile;
import de.k3b.io.DirectoryFormatter;
import de.k3b.io.GalleryFilterParameter;
import de.k3b.io.IDirectory;
import de.k3b.io.IGalleryFilter;
import de.k3b.io.IGeoRectangle;
+import de.k3b.io.StringUtils;
import de.k3b.io.VISIBILITY;
import de.k3b.tagDB.Tag;
/**
+ * Editor for GalleryFilterParameter and for parsed *.query and *.album QueryParameter-files.
+ *
* Defines a gui for global foto filter: only fotos from certain filepath, date and/or lat/lon will be visible.
*/
public class GalleryFilterActivity extends ActivityWithAutoCloseDialogs
@@ -72,23 +83,34 @@ public class GalleryFilterActivity extends ActivityWithAutoCloseDialogs
{
private static final String mDebugPrefix = "GalF-";
- public static final int resultID = 522;
private static final String DLG_NAVIGATOR_TAG = "GalleryFilterActivity";
+ private static final String DLG_SAVE_AS_TAG = "GalleryFilterActivitySaveAs";
private static final String SETTINGS_KEY = "GalleryFilterActivity-";
- private static final String FILTER_VALUE = "CURRENT_FILTER";
+ private static final String LAST_SELECTED_DIR_KEY = "mLastSelectedAlbumDir";
+
private static final String WILDCARD = "%";
- private static QueryParameter mRootQuery;
+ private static final int SAVE_AS_VALBUM_PICK = 9921;
private GalleryFilterParameter mFilter = new GalleryFilterParameter();
+ // where to navigate to when folder picker is opend: last path or last picked album file
+ private String mLastSelectedAlbumDir = null;
+
+ // parsed filter part of query
private FilterValue mFilterValue = null;
+ // contains the non parsebale part of query
+ private QueryParameter mQueryWithoutFilter;
+
private HistoryEditText mHistory;
- private BookmarkController mBookmarkController = null;
- private IDirectory mPopUpSelection = null;
- public static void showActivity(Activity context, IGalleryFilter filter, QueryParameter rootQuery,
+ private GalleryFilterPathState mGalleryFilterPathState = null;
+ private VirtualAlbumController mBookmarkController = null;
+
+ /** set while dir picker is active */
+ private DirectoryPickerFragment mDirPicker = null;
+
+ public static void showActivity(Activity context, IGalleryFilter filter, QueryParameter query,
String lastBookmarkFileName, int requestCode) {
- mRootQuery = rootQuery;
if (Global.debugEnabled) {
Log.d(Global.LOG_CONTEXT, context.getClass().getSimpleName()
+ " > GalleryFilterActivity.showActivity");
@@ -97,11 +119,7 @@ public static void showActivity(Activity context, IGalleryFilter filter, QueryPa
final Intent intent = new Intent().setClass(context,
GalleryFilterActivity.class);
- if ((intent != null) && (filter != null)) {
- intent.putExtra(EXTRA_FILTER, filter.toString());
- }
-
- BookmarkController.saveState(lastBookmarkFileName, intent, null);
+ AndroidAlbumUtils.saveFilterAndQuery(context, null, intent, null, filter, query);
if (requestCode != 0) {
context.startActivityForResult(intent, requestCode);
} else {
@@ -109,18 +127,14 @@ public static void showActivity(Activity context, IGalleryFilter filter, QueryPa
}
}
- public static GalleryFilterParameter getFilter(Intent intent) {
- if (intent == null) return null;
- String filter = intent.getStringExtra(EXTRA_FILTER);
- if (filter == null) return null;
- return GalleryFilterParameter.parse(filter, new GalleryFilterParameter());
- }
-
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
fromGui(mFilter);
- savedInstanceState.putString(FILTER_VALUE, mFilter.toString());
- mBookmarkController.saveState(null, savedInstanceState);
+ AndroidAlbumUtils.saveFilterAndQuery(this, null, null, savedInstanceState, mFilter, mQueryWithoutFilter);
+
+ savedInstanceState.putString(LAST_SELECTED_DIR_KEY, mLastSelectedAlbumDir);
+
+ mGalleryFilterPathState.save(this, savedInstanceState);
super.onSaveInstanceState(savedInstanceState);
}
@@ -130,25 +144,46 @@ protected void onCreate(Bundle savedInstanceState) {
Global.debugMemory(mDebugPrefix, "onCreate");
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
- if (Global.debugEnabled && (intent != null)){
- Log.d(Global.LOG_CONTEXT, mDebugPrefix + "onCreate " + intent.toUri(Intent.URI_INTENT_SCHEME));
+
+ mGalleryFilterPathState = new GalleryFilterPathState().load(this, intent,
+ savedInstanceState);
+ // for debugging: where does the filter come from
+ StringBuilder dbgMessageResult = (Global.debugEnabled) ? new StringBuilder() : null;
+
+ if ((dbgMessageResult != null) && (intent != null)){
+ dbgMessageResult.append(StringUtils.appendMessage(dbgMessageResult,
+ mDebugPrefix , "onCreate", intent.toUri(Intent.URI_INTENT_SCHEME)));
}
setContentView(R.layout.activity_gallery_filter);
this.mFilterValue = new FilterValue();
onCreateButtos();
- GalleryFilterParameter filter = (savedInstanceState == null)
- ? getFilter(intent)
- : GalleryFilterParameter.parse(savedInstanceState.getString(FILTER_VALUE, ""), new GalleryFilterParameter()) ;
+ final QueryParameter query = AndroidAlbumUtils.getQuery(this, "",
+ savedInstanceState, intent, null, null, dbgMessageResult);
+ setQueryAndFilter(query);
- if (filter != null) {
- mFilter = filter;
- toGui(mFilter);
- mFilterValue.showLatLon(filter.isNonGeoOnly());
+ if (dbgMessageResult != null) {
+ Log.i(Global.LOG_CONTEXT, dbgMessageResult.toString());
}
- mBookmarkController = new BookmarkController(this);
- mBookmarkController.loadState(intent,savedInstanceState);
+ mBookmarkController = new VirtualAlbumController(this);
+
+ if (savedInstanceState != null) {
+ mLastSelectedAlbumDir = savedInstanceState.getString(LAST_SELECTED_DIR_KEY);
+ }
+ }
+
+ private void setQueryAndFilter(QueryParameter query) {
+ this.mQueryWithoutFilter = query;
+ if (this.mQueryWithoutFilter == null) {
+ this.mQueryWithoutFilter = new QueryParameter();
+ this.mFilter = new GalleryFilterParameter();
+ } else {
+ this.mFilter.get(TagSql.parseQueryEx(this.mQueryWithoutFilter, true));
+ }
+
+ mFilterValue.showLatLon(this.mFilter.isNonGeoOnly());
+ toGui(this.mFilter);
}
private void onCreateButtos() {
@@ -156,14 +191,30 @@ private void onCreateButtos() {
cmd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- showDirectoryPicker(FotoSql.queryGroupByDir);
+ String path = (!StringUtils.isNullOrEmpty(mLastSelectedAlbumDir)) ? mLastSelectedAlbumDir : getAsGalleryFilter().getPath();
+
+ if (StringUtils.isNullOrEmpty(path)) {
+ path = mGalleryFilterPathState.load(GalleryFilterActivity.this,
+ null, null)
+ .getPathDefault(null);
+ }
+ if (path != null) path = path.replaceAll("%","");
+ showDirectoryPickerForFilterParamValue(
+ mDebugPrefix + " path picker " + path,
+ FotoSql.queryGroupByDir,
+ true,
+ false, path);
}
});
cmd = (Button) findViewById(R.id.cmd_date);
cmd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- showDirectoryPicker(FotoSql.queryGroupByDate);
+ String path = getAsGalleryFilter().getDatePath();
+ showDirectoryPickerForFilterParamValue(
+ mDebugPrefix + " date picker " + path,
+ FotoSql.queryGroupByDate, false,
+ FotoLibGlobal.datePickerUseDecade, path);
}
});
cmd = (Button) findViewById(R.id.cmd_select_lat_lon);
@@ -219,25 +270,26 @@ public boolean onOptionsItemSelected(MenuItem item) {
return true;
case R.id.cmd_gallery:
- FotoGalleryActivity.showActivity(this, getAsGalleryFilter(), null, 0);
+ FotoGalleryActivity.showActivity(this, getAsMergedQuery(), 0);
+ // TagSql.filter2NewQuery(getAsGalleryFilter()), 0);
return true;
case R.id.cmd_show_geo: {
- MapGeoPickerActivity.showActivity(this, null, getAsGalleryFilter());
+ MapGeoPickerActivity.showActivity(this, null, getAsMergedQuery(), 0);
return true;
}
+ case R.id.action_details:
+ cmdShowDetails();
+ return true;
case R.id.action_save_as:
- mBookmarkController.onSaveAsQuestion(mBookmarkController.getlastBookmarkFileName(), getAsQuery());
- return true;
- case R.id.action_load_from:
- mBookmarkController.onLoadFromQuestion(new BookmarkController.IQueryConsumer() {
- @Override
- public void setQuery(String fileName, QueryParameter newQuery) {
- IGalleryFilter filter = TagSql.parseQueryEx(newQuery, false);
- toGui(filter);
- }
- }, getAsQuery());
+ mGalleryFilterPathState.load(this,null,null);
+ // mBookmarkController.onSaveAsQuestion(mBookmarkController.getlastBookmarkFileName(), getAsQuery());
+ File valbum = mGalleryFilterPathState.getSaveAlbumAs(getString(R.string.mk_dir_default), AlbumFile.SUFFIX_VALBUM);
+ DialogFragment dlg = mBookmarkController.onSaveAsVirutalAlbumQuestion(valbum, getAsMergedQuery());
+ setAutoClose(dlg, null, null);
+ invalidatePathData();
+
return true;
default:
return super.onOptionsItemSelected(item);
@@ -254,7 +306,8 @@ protected void onActivityResult(final int requestCode,
final int resultCode, final Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
- if (mPopUpSelection != null) mPopUpSelection.refresh();
+ IDirectory lastPopUpSelection = (mDirPicker == null) ? null : mDirPicker.getLastPopUpSelection();
+ if (lastPopUpSelection != null) lastPopUpSelection.refresh();
}
private GalleryFilterParameter getAsGalleryFilter() {
@@ -263,12 +316,8 @@ private GalleryFilterParameter getAsGalleryFilter() {
return filter;
}
- private QueryParameter getAsQuery() {
- IGalleryFilter filter = new GalleryFilterParameter();
- fromGui(filter);
- QueryParameter query = new QueryParameter(mRootQuery);
- TagSql.filter2QueryEx(query, filter, true);
- return query;
+ private QueryParameter getAsMergedQuery() {
+ return AndroidAlbumUtils.getAsMergedNewQueryParameter(mQueryWithoutFilter, getAsGalleryFilter());
}
@Override
@@ -283,12 +332,18 @@ protected void onResume () {
Global.debugMemory(mDebugPrefix, "onResume");
loadLastFilter();
super.onResume();
+ if (LockScreen.isLocked(this)) {
+ // filter is forbidden when locked. Might occur via
+ // gallery -> filter -> dir-pick -> openInNew Gallery -> exit
+ finish();
+ }
}
private void loadLastFilter() {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
loadLastFilter(sharedPref, FotoSql.QUERY_TYPE_GROUP_ALBUM);
+ loadLastFilter(sharedPref, FotoSql.QUERY_TYPE_GALLERY);
loadLastFilter(sharedPref, FotoSql.QUERY_TYPE_GROUP_DATE);
loadLastFilter(sharedPref, FotoSql.QUERY_TYPE_GROUP_PLACE);
}
@@ -298,13 +353,13 @@ private void loadLastFilter(SharedPreferences sharedPref, int queryTypeID) {
}
private void saveLastFilter() {
- if (dirInfos != null)
+ if (mDirInfos != null)
{
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor edit = sharedPref.edit();
-
- for(Integer id : dirInfos.keySet()) {
- DirInfo dir = dirInfos.get(id);
+
+ for(Integer id : mDirInfos.keySet()) {
+ DirInfo dir = mDirInfos.get(id);
if ((dir != null) && (dir.currentPath != null) && (dir.currentPath.length() > 0)) {
edit.putString(SETTINGS_KEY + id, dir.currentPath);
}
@@ -318,16 +373,15 @@ protected void onDestroy() {
Global.debugMemory(mDebugPrefix, "onDestroy start");
super.onDestroy();
- mPopUpSelection = null;
- if (dirInfos != null)
+ if (mDirInfos != null)
{
- for(Integer id : dirInfos.keySet()) {
- DirInfo dir = dirInfos.get(id);
+ for(Integer id : mDirInfos.keySet()) {
+ DirInfo dir = mDirInfos.get(id);
if (dir.directoryRoot != null) {
dir.directoryRoot.destroy();
}
}
- dirInfos = null;
+ mDirInfos = null;
}
Global.debugMemory(mDebugPrefix, "onDestroy end");
@@ -619,6 +673,10 @@ private long convertDate(String string) throws RuntimeException {
}
}
+ @Override public String toString() {
+ return new GalleryFilterParameter().get(this).toString();
+ }
+
}
private void toGui(IGalleryFilter gf) {
@@ -638,6 +696,19 @@ private boolean fromGui(IGalleryFilter dest) {
}
}
+ private void cmdShowDetails() {
+ final QueryParameter asMergedQuery = getAsMergedQuery();
+
+ ImageDetailMetaDialogBuilder.createImageDetailDialog(
+ this,
+ getTitle().toString(),
+ asMergedQuery.toSqlString(),
+ StringUtils.appendMessage(null,
+ getString(R.string.show_photo),
+ TagSql.getCount(this, asMergedQuery))
+ ).show();
+ }
+
private void clearFilter() {
GalleryFilterParameter filter = new GalleryFilterParameter();
@@ -645,7 +716,8 @@ private void clearFilter() {
filter.setSort(mFilter.getSortID(), mFilter.isSortAscending());
}
- this.mFilter = filter;
+ mFilter = filter;
+ mQueryWithoutFilter = new QueryParameter();
toGui(mFilter);
}
@@ -653,19 +725,20 @@ private void onOk() {
if (fromGui(mFilter)) {
mHistory.saveHistory();
- final Intent intent = new Intent();
- if (this.mFilter != null) {
- intent.putExtra(EXTRA_FILTER, this.mFilter.toString());
- }
+ final Intent resultIntent = new Intent();
+ final Intent originalIntent = getIntent();
+
+ // if result must be saved to file
+ Uri originalUri = (originalIntent == null) ? null : originalIntent.getData();
- mBookmarkController.saveState(intent, null);
+ AndroidAlbumUtils.saveFilterAndQuery(this, originalUri, resultIntent, null, mFilter, mQueryWithoutFilter);
+
+ this.setResult(BaseQueryActivity.resultID, resultIntent);
- this.setResult(resultID, intent);
finish();
}
}
-
/**************** DirectoryPicker *****************/
private static class DirInfo {
public int queryId = 0;
@@ -673,13 +746,14 @@ private static class DirInfo {
public String currentPath = null;
}
- private HashMap dirInfos = new HashMap();
+ /** one DirInfo per possible queyType */
+ private HashMap mDirInfos = new HashMap();
private DirInfo getOrCreateDirInfo(int queryId) {
- DirInfo result = dirInfos.get(queryId);
+ DirInfo result = mDirInfos.get(queryId);
if (result == null) {
result = new DirInfo();
result.queryId = queryId;
- dirInfos.put(queryId, result);
+ mDirInfos.put(queryId, result);
}
return result;
}
@@ -724,43 +798,49 @@ private void showLatLonPicker() {
if (fromGui(mFilter)) {
final FragmentManager manager = getFragmentManager();
LocationMapFragment dlg = new LocationMapFragment();
- dlg.defineNavigation(null, mFilter, OsmdroidUtil.NO_ZOOM, null, null);
+ dlg.defineNavigation(null, null, mFilter, OsmdroidUtil.NO_ZOOM, null, null, false);
dlg.show(manager, DLG_NAVIGATOR_TAG);
setAutoClose(dlg, null, null);
}
}
- private void showDirectoryPicker(final QueryParameter currentDirContentQuery) {
+ private void showDirectoryPickerForFilterParamValue(
+ final String debugContext, final QueryParameter currentDirContentQuery,
+ boolean addVAlbums, boolean datePickerUseDecade,
+ final String initialSelection) {
if (fromGui(mFilter)) {
- IDirectory directoryRoot = getOrCreateDirInfo(currentDirContentQuery.getID()).directoryRoot;
+ final DirInfo dirInfo = getOrCreateDirInfo(currentDirContentQuery.getID());
+ IDirectory directoryRoot = dirInfo.directoryRoot;
+ dirInfo.currentPath = initialSelection;
if (directoryRoot == null) {
- DirectoryLoaderTask loader = new DirectoryLoaderTask(this, mDebugPrefix) {
+ DirectoryLoaderTask loader = new DirectoryLoaderTask(this, datePickerUseDecade, debugContext) {
@Override
protected void onPostExecute(IDirectory directoryRoot) {
- onDirectoryDataLoadComplete(directoryRoot, currentDirContentQuery.getID());
+ onDirectoryDataLoadCompleteForFilterParamValue(directoryRoot, currentDirContentQuery.getID());
}
};
- loader.execute(currentDirContentQuery);
+
+ if (addVAlbums) {
+ // load dir-s + "*.album"
+ loader.execute(currentDirContentQuery, FotoSql.queryVAlbum);
+ } else {
+ loader.execute(currentDirContentQuery);
+ }
} else {
- onDirectoryDataLoadComplete(directoryRoot, currentDirContentQuery.getID());
+ onDirectoryDataLoadCompleteForFilterParamValue(directoryRoot, currentDirContentQuery.getID());
}
}
}
- private void onDirectoryDataLoadComplete(IDirectory directoryRoot, int queryId) {
+ private void onDirectoryDataLoadCompleteForFilterParamValue(IDirectory directoryRoot, int queryId) {
if (directoryRoot != null) {
Global.debugMemory(mDebugPrefix, "onDirectoryDataLoadComplete");
DirInfo dirInfo = getOrCreateDirInfo(queryId);
dirInfo.directoryRoot = directoryRoot;
final FragmentManager manager = getFragmentManager();
- DirectoryPickerFragment dlg = new DirectoryPickerFragment() {
- protected boolean onPopUpClick(MenuItem menuItem, IDirectory popUpSelection) {
- mPopUpSelection = popUpSelection;
- return super.onPopUpClick(menuItem, popUpSelection);
- }
- };
+ DirectoryPickerFragment dlg = new DirectoryPickerFragment();
int menuResId = 0; // no menu in app lock mode
if (!LockScreen.isLocked(this)) {
@@ -769,22 +849,52 @@ protected boolean onPopUpClick(MenuItem menuItem, IDirectory popUpSelection) {
dlg.setContextMenuId(menuResId);
dlg.defineDirectoryNavigation(dirInfo.directoryRoot, dirInfo.queryId, dirInfo.currentPath);
-
dlg.show(manager, DLG_NAVIGATOR_TAG);
+ mDirPicker = dlg;
+
setAutoClose(dlg, null, null);
}
}
/**
- * called when user picks a new directory
+ * called when user picks a new directory.
+ * Sources: Filter-Path, Filter-Date or SaveAs distinguished via queryTypeId
*/
@Override
- public void onDirectoryPick(String selectedAbsolutePath, int queryTypeId) {
- DirInfo dirInfo = getOrCreateDirInfo(queryTypeId);
- dirInfo.currentPath=selectedAbsolutePath;
+ public void onDirectoryPick(final String selectedAbsolutePath, int queryTypeId) {
+ closeDialogIfNeeded();
+ mGalleryFilterPathState.load(this, null, null);
+
+ File selectedAlbumFile = AlbumFile.getExistingQueryFileOrNull(selectedAbsolutePath);
+ if (selectedAlbumFile != null) {
+ // selection was an album file
+ final String selectedAlbumPath = selectedAlbumFile.getPath();
+ setQueryAndFilter(AndroidAlbumUtils.getQueryFromUri(mDebugPrefix + ".onDirectoryPick loading album "
+ + selectedAlbumPath, this, Uri.fromFile(selectedAlbumFile), null));
+ mGalleryFilterPathState.setAlbum(Uri.fromFile(selectedAlbumFile));
+ mGalleryFilterPathState.setLastPath(selectedAlbumFile.getParent());
+ mLastSelectedAlbumDir = selectedAlbumPath; //??electedAlbumFile.getParent();
+ } else if (AlbumFile.isQueryFile(selectedAbsolutePath)) {
+ // album does not exist (any more) rescan
+ AndroidAlbumUtils.albumMediaScan(mDebugPrefix + " onAlbumPick not found in filesystem => ",
+ this, new File(selectedAbsolutePath), 1);
+
+ mGalleryFilterPathState.setLastPath(selectedAlbumFile.getParent());
+ invalidatePathData();
+ } else {
+ // selection was a path (os-dir or date)
+ mLastSelectedAlbumDir = null;
+ FotoSql.set(mFilter, selectedAbsolutePath, queryTypeId);
+ if (queryTypeId == FotoSql.QUERY_TYPE_GROUP_ALBUM) {
+ mGalleryFilterPathState.setLastPath(selectedAbsolutePath);
+ }
+ toGui(mFilter);
+ }
+ }
- FotoSql.set(mFilter,selectedAbsolutePath, queryTypeId);
- toGui(mFilter);
+ private void invalidatePathData() {
+ getOrCreateDirInfo(FotoSql.QUERY_TYPE_GROUP_ALBUM).directoryRoot = null;
+ getOrCreateDirInfo(FotoSql.QUERY_TYPE_GALLERY).directoryRoot = null;
}
/** interface DirectoryPickerFragment.invalidateDirectories not used */
@@ -794,7 +904,13 @@ public void invalidateDirectories(String why) {
/** interface DirectoryPickerFragment.OnDirectoryInteractionListener not used */
@Override
- public void onDirectoryCancel(int queryTypeId) {}
+ public void onDirectoryCancel(int queryTypeId) {closeDialogIfNeeded();}
+
+ @Override
+ protected void closeDialogIfNeeded() {
+ super.closeDialogIfNeeded();
+ mDirPicker = null;
+ }
/** interface DirectoryPickerFragment.OnDirectoryInteractionListener not used */
@Override
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/GalleryFilterPathState.java b/app/src/main/java/de/k3b/android/androFotoFinder/GalleryFilterPathState.java
new file mode 100644
index 00000000..815f4bd5
--- /dev/null
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/GalleryFilterPathState.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2018 by k3b.
+ *
+ * This file is part of AndroFotoFinder / #APhotoManager.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see
+ */
+
+package de.k3b.android.androFotoFinder;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.PreferenceManager;
+
+import java.io.File;
+
+import de.k3b.android.util.IntentUtil;
+import de.k3b.io.AlbumFile;
+import de.k3b.io.FileUtils;
+import de.k3b.io.StringUtils;
+
+/**
+ * Created by k3b on 09.08.2018.
+ */
+public class GalleryFilterPathState {
+ private static final String KEY_LastAlbum = "GalleryFilterActivity-LastAlbum";
+ private static final String KEY_CurrentAlbum = "GalleryFilterActivity-CurrentAlbum";
+ private static final String KEY_LastFilterPath = "GalleryFilterActivity-LastFilterPath";
+
+ /*
+ Concept path currend
+ - (1) settings-lastAlbum = dir for "save album as"
+ loaded-from/saved-to settings
+ default for save album as
+ - (2) bundle-currentAlbum (that will be saved on exit)
+ initially set from intent-uri
+ overwrites (1)
+ modified by "save as"
+ saved/loaded from bundle
+ - (3) settings-last used "filtered path-directory"
+ loaded-from/saved-to settings
+ default for get filter path
+ - (4) data-current filtered path-directory
+ overwrites (3) (or 1 if album)
+ */
+ private Uri mLastAlbum = null;
+ private Uri mCurrentAlbum = null;
+ private String mLastFilterPath = null;
+
+ public GalleryFilterPathState load(Context context, Intent intent, Bundle savedInstanceState) {
+ if (context != null) {
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
+ mLastAlbum = get(sharedPref.getString(KEY_LastAlbum, null), mLastAlbum);
+ mLastFilterPath = sharedPref.getString(KEY_LastFilterPath, mLastFilterPath);
+
+ }
+ if (savedInstanceState != null) {
+ mCurrentAlbum = get(savedInstanceState.getString(KEY_CurrentAlbum), mCurrentAlbum);
+ }
+ if (intent != null) {
+ if (mCurrentAlbum == null) {
+ Uri uri = IntentUtil.getUri(intent);
+ if ((uri != null) && AlbumFile.isQueryFile(uri.getPath())) {
+ mCurrentAlbum = uri;
+ mLastAlbum = uri;
+
+ }
+ }
+ }
+ return this;
+ }
+
+ public GalleryFilterPathState save(Context context, Bundle savedInstanceState) {
+ saveAsPreference(context, this.mLastAlbum, this.mLastFilterPath);
+
+ if (savedInstanceState != null) {
+ if (mCurrentAlbum != null)
+ savedInstanceState.putString(KEY_CurrentAlbum, mCurrentAlbum.toString());
+ }
+ return this;
+ }
+
+ public static void saveAsPreference(Context context, Uri lastAlbum, String lastFilterPath) {
+ if (context != null) {
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sharedPref.edit();
+ if (lastFilterPath != null) editor.putString(KEY_LastFilterPath, lastFilterPath);
+ if (lastAlbum != null) editor.putString(KEY_LastAlbum, lastAlbum.toString());
+ editor.commit();
+ }
+ }
+
+ private Uri get(String str, Uri defaultValue) {
+ Uri uri = null;
+ if (str != null) uri = Uri.parse(str);
+ if (uri != null) return uri;
+ return defaultValue;
+ }
+
+ public Uri getAlbumDefault() {
+ return mLastAlbum;
+ }
+
+ public File getSaveAlbumAs(String newFilePrefix, String newFileSuffix) {
+ if (mCurrentAlbum != null) return getFile(mCurrentAlbum);
+ File parentDir = getExistingParentDirFile(this.mLastAlbum);
+ if (parentDir == null) {
+ parentDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
+ }
+ return FileUtils.getFirstNonExistingFile(parentDir, newFilePrefix, 0, newFileSuffix);
+ }
+
+ public static File getFile(Uri uri) {
+ if (uri != null) {
+ String path = FileUtils.fixPath(uri.getPath());
+ if (path != null) {
+ return new File(path);
+ }
+ }
+ return null;
+ }
+
+ public static File getExistingParentDirFile(Uri uri) {
+ return FileUtils.getFirstExistingDir(getFile(uri));
+ }
+
+ public void setAlbum(Uri album) {
+ this.mLastAlbum = album;
+ this.mCurrentAlbum = album;
+ }
+
+ public Uri getCurrentAlbum() {
+ return mCurrentAlbum;
+ }
+
+ public String getPathDefault(String currentPath) {
+ if (!StringUtils.isNullOrEmpty(currentPath)) return currentPath;
+ return mLastFilterPath;
+ }
+
+ public void setLastPath(String mLastFilterPath) {
+ this.mLastFilterPath = mLastFilterPath;
+ }
+}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/LockScreen.java b/app/src/main/java/de/k3b/android/androFotoFinder/LockScreen.java
index 34efc206..7106517b 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/LockScreen.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/LockScreen.java
@@ -27,6 +27,8 @@
import android.view.Menu;
import android.view.MenuItem;
+import de.k3b.android.util.MenuUtils;
+
/**
* #105: Management of app locking (aka Android "Screen"-pinning, "Kiosk Mode", "LockTask")
* https://developer.android.com/about/versions/android-5.0.html#ScreenPinning.
@@ -75,13 +77,12 @@ public static boolean isLocked(Context ctx) {
return Global.locked;
}
- public static void fixMenu(Menu menu) {
- if ((menu != null) && OS_APPLOCK_ENABLED) {
- MenuItem unlock = menu.findItem(R.id.cmd_app_unpin2);
- if (unlock != null) menu.removeItem(R.id.cmd_app_unpin2);
-
- menu.removeItem(R.id.cmd_show_geo);
- menu.removeItem(R.id.cmd_gallery);
+ public static void removeDangerousCommandsFromMenu(Menu menu) {
+ if (OS_APPLOCK_ENABLED) {
+ // use os-unlock method instead
+ MenuUtils.removeItems(menu,R.id.cmd_app_unpin2);
}
+ MenuUtils.removeItems(menu,R.id.cmd_show_geo,
+ R.id.cmd_gallery, R.id.cmd_filter);
}
}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/SettingsActivity.java b/app/src/main/java/de/k3b/android/androFotoFinder/SettingsActivity.java
index 4f8584ce..3a671c02 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/SettingsActivity.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/SettingsActivity.java
@@ -150,6 +150,8 @@ public static void global2Prefs(Context context) {
prefs.putBoolean("debugEnabledMemory", Global.debugEnabledMemory);
+ prefs.putBoolean("datePickerUseDecade", FotoLibGlobal.datePickerUseDecade);
+
prefs.putBoolean("debugEnabledJpgMetaIo", FotoLibGlobal.debugEnabledJpgMetaIo);
prefs.putBoolean("debugEnabledJpg", FotoLibGlobal.debugEnabledJpg);
@@ -204,6 +206,8 @@ public static void prefs2Global(Context context) {
Global.debugEnabledMemory = getPref(prefs, "debugEnabledMemory", Global.debugEnabledMemory);
+ FotoLibGlobal.datePickerUseDecade = getPref(prefs, "datePickerUseDecade", FotoLibGlobal.datePickerUseDecade);
+
Global.locked = getPref(prefs, "locked", Global.locked);
Global.passwordHash = getPref(prefs, "passwordHash", Global.passwordHash);
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/VirtualAlbumController.java b/app/src/main/java/de/k3b/android/androFotoFinder/VirtualAlbumController.java
new file mode 100644
index 00000000..342a5404
--- /dev/null
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/VirtualAlbumController.java
@@ -0,0 +1,87 @@
+package de.k3b.android.androFotoFinder;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+
+import java.io.File;
+
+import de.k3b.android.androFotoFinder.directory.SaveAsPickerFragment;
+import de.k3b.android.androFotoFinder.queries.AndroidAlbumUtils;
+import de.k3b.android.widget.Dialogs;
+import de.k3b.database.QueryParameter;
+
+/**
+ * Encapsulates logic to handle virtual albums (former known as Bookmarks)
+ *
+ * Created by k3b on 21.05.2018.
+ */
+
+public class VirtualAlbumController {
+ public static final String DLG_SAVE_ALBUM_AS = "GalleryFilterActivitySaveAs";
+ private final Activity mContext;
+
+ public VirtualAlbumController(Activity context) {
+ super();
+ mContext = context;
+ }
+
+ public static class SaveAs extends SaveAsPickerFragment {
+ private final VirtualAlbumController mVirtualAlbumController;
+ private final QueryParameter mCurrentFilter;
+
+ // only needed to prevent crash on rotation
+ public SaveAs() {this(null, null, null);}
+ public SaveAs(VirtualAlbumController virtualAlbumController, final File valbum, final QueryParameter currentFilter) {
+ super(valbum);
+ this.mVirtualAlbumController = virtualAlbumController;
+ this.mCurrentFilter = currentFilter;
+ }
+ @Override
+ protected void onFilePick(File pickedOrCreatedFile) {
+ if (mVirtualAlbumController != null) mVirtualAlbumController.onSaveAsVirutalAlbumAnswer(pickedOrCreatedFile, mCurrentFilter);
+ }
+ }
+ // workflow onSaveAsVirutalAlbumQuestion-onSaveAsVirutalAlbumAnswer-onSaveAsVirutalAlbumAllowOverwriteAnswer
+ public DialogFragment onSaveAsVirutalAlbumQuestion(final File valbum, final QueryParameter currentFilter) {
+ SaveAs dirDialog = new SaveAs(this, valbum, currentFilter);
+
+ final FragmentManager manager = this.mContext.getFragmentManager();
+ dirDialog.show(manager, DLG_SAVE_ALBUM_AS);
+ return dirDialog;
+ }
+
+ // workflow onSaveAsVirutalAlbumQuestion-onSaveAsVirutalAlbumAnswer-onSaveAsVirutalAlbumAllowOverwriteAnswer
+ private void onSaveAsVirutalAlbumAnswer(final File valbum, final QueryParameter currentFilter) {
+ if (mustAskOverwrite(valbum)) {
+ Dialogs dialog = new Dialogs() {
+ @Override
+ protected void onDialogResult(String result, Object... parameters) {
+ if (result != null) {
+ // yes, overwrite
+ onSaveAsVirutalAlbumAllowOverwriteAnswer(valbum, currentFilter);
+ } else {
+ // no, do not overwrite, ask again
+ onSaveAsVirutalAlbumQuestion(valbum, currentFilter);
+ }
+ }
+ };
+ dialog.yesNoQuestion(this.mContext, this.mContext.getString(R.string.overwrite_question_title) ,
+ this.mContext.getString(R.string.image_err_file_exists_format, valbum.getAbsolutePath()));
+
+ } else {
+ // does not exist yet
+ onSaveAsVirutalAlbumAllowOverwriteAnswer(valbum, currentFilter);
+ }
+ }
+
+ private boolean mustAskOverwrite(File valbum) {
+ if (valbum.exists()) return true;
+ return false;
+ }
+
+ // workflow onSaveAsVirutalAlbumQuestion-onSaveAsVirutalAlbumAnswer-onSaveAsVirutalAlbumAllowOverwriteAnswer
+ private void onSaveAsVirutalAlbumAllowOverwriteAnswer(File valbum, final QueryParameter currentFilter) {
+ AndroidAlbumUtils.saveAs(mContext, valbum, currentFilter);
+ }
+}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryListAdapter.java b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryListAdapter.java
index 21c05b24..2448ac65 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryListAdapter.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryListAdapter.java
@@ -32,7 +32,9 @@
import de.k3b.android.androFotoFinder.Global;
import de.k3b.android.androFotoFinder.R;
+import de.k3b.io.AlbumFile;
import de.k3b.io.Directory;
+import de.k3b.io.FileUtils;
import de.k3b.io.IDirectory;
import de.k3b.io.IExpandableListViewNavigation;
@@ -186,6 +188,15 @@ public static Spanned getDirectoryDisplayText(String prefix, IDirectory director
case IDirectory.DIR_FLAG_APM_DIR:
formatPrefix = IDirectory.APM_DIR_PREFIX;
break;
+ case IDirectory.DIR_FLAG_VIRTUAL_DIR:
+ if ((options & Directory.OPT_AS_HTML) != 0) {
+ formatPrefix = "{";
+ formatSuffix = "}";
+ } else {
+ formatPrefix = "{";
+ formatSuffix = "}";
+ }
+ break;
case IDirectory.DIR_FLAG_NONE:
if ((options & Directory.OPT_AS_HTML) != 0) {
formatPrefix = "";
@@ -198,7 +209,11 @@ public static Spanned getDirectoryDisplayText(String prefix, IDirectory director
if (prefix != null) result.append(prefix);
result.append(formatPrefix);
- result.append(directory.getRelPath()).append(" ");
+ String dirName = directory.getRelPath();
+ if (dirName.endsWith(AlbumFile.SUFFIX_VALBUM)) {
+ dirName = dirName.substring(0, dirName.length() - AlbumFile.SUFFIX_VALBUM.length());
+ }
+ result.append(dirName).append(" ");
result.append(formatSuffix);
Directory.appendCount(result, directory, options);
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryLoaderTask.java b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryLoaderTask.java
index 180804cb..8933b1cd 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryLoaderTask.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryLoaderTask.java
@@ -61,14 +61,16 @@ public class DirectoryLoaderTask extends AsyncTask= 0) ? cursor.getString(colText) : getLatLonPath(cursor.getDouble(colLat), cursor.getDouble(colLon));
- if (path != null) {
- if (colCount != -1) markerItemCount = cursor.getInt(colCount);
- builder.add(path, markerItemCount, cursor.getInt(colIconID));
- itemCount++;
- if ((--increment) <= 0) {
- publishProgress(itemCount, expectedCount);
- increment = PROGRESS_INCREMENT;
-
- // Escape early if cancel() is called
- if (isCancelled()) break;
- }
+ if ((colText == -1) && ((colLat == -1) || (colLon == -1))) {
+ throw new IllegalArgumentException("Missing SQL Column. Need either " +
+ FotoSql.SQL_COL_DISPLAY_TEXT +
+ " or " + FotoSql.SQL_COL_LAT +
+ " + " + FotoSql.SQL_COL_LON);
}
- }
- if (mStatus != null) {
- mStatus.append("\n\tfound ").append(itemCount).append(" db rows");
- }
- IDirectory result = builder.getRoot();
- if (colText < 0) {
- compressLatLon(result);
- }
- return result;
- } catch (Exception ex) {
- mException = ex;
- if (mStatus != null) {
- mStatus.append("\n\t").append(ex.getMessage());
- }
- return null;
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- if (mStatus != null) {
- if (Global.debugEnabledSql) {
- Log.w(Global.LOG_CONTEXT, mStatus.toString());
- } else if (Global.debugEnabled) {
- Log.i(Global.LOG_CONTEXT, mStatus.toString());
+ while (cursor.moveToNext()) {
+ String path = (colText >= 0) ? cursor.getString(colText) : getLatLonPath(cursor.getDouble(colLat), cursor.getDouble(colLon));
+ if (path != null) {
+ if (colCount != -1) markerItemCount = cursor.getInt(colCount);
+ final int iconID = cursor.getInt(colIconID);
+ addItem(builder, path, markerItemCount, iconID);
+ itemCount++;
+ if ((--increment) <= 0) {
+ publishProgress(itemCount, expectedCount);
+ increment = PROGRESS_INCREMENT;
+
+ // Escape early if cancel() is called
+ if (isCancelled()) break;
+ }
+ }
+ }
+ if (mStatus != null) {
+ mStatus.append("\n\tfound ").append(itemCount).append(" db rows");
+ }
+ } catch (Exception ex) {
+ mException = ex;
+ if (mStatus != null) {
+ mStatus.append("\n\t").append(ex.getMessage());
+ }
+ return null;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ cursor = null;
+ }
+ if (mStatus != null) {
+ if (Global.debugEnabledSql) {
+ Log.w(Global.LOG_CONTEXT, mStatus.toString());
+ } else if (Global.debugEnabled) {
+ Log.i(Global.LOG_CONTEXT, mStatus.toString());
+ }
}
+
}
+ }
+ IDirectory result = builder.getRoot();
+ if (colText < 0) {
+ compressLatLon(result);
+ }
+ return result;
+ }
+
+ protected void addItem(DirectoryBuilder builder, String path, int markerItemCount, int iconID) {
+ if (path != null) {
+ String decade = (datePickerUseDecade) ? DirectoryFormatter.getDecade(path,1) : null;
+ String newPath = (decade != null)
+ ? ("/" + decade + path)
+ : path;
+ builder.add(newPath, markerItemCount, iconID);
}
}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryPickerFragment.java b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryPickerFragment.java
index 6e0a5449..506892cc 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryPickerFragment.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryPickerFragment.java
@@ -26,11 +26,13 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
@@ -51,6 +53,7 @@
import de.k3b.android.androFotoFinder.ThumbNailUtils;
import de.k3b.android.androFotoFinder.imagedetail.ImageDetailActivityViewPager;
import de.k3b.android.androFotoFinder.imagedetail.ImageDetailMetaDialogBuilder;
+import de.k3b.android.androFotoFinder.queries.AndroidAlbumUtils;
import de.k3b.android.androFotoFinder.queries.FotoSql;
import de.k3b.android.androFotoFinder.queries.FotoThumbSql;
import de.k3b.android.androFotoFinder.queries.FotoViewerParameter;
@@ -60,9 +63,13 @@
import de.k3b.android.util.AndroidFileCommands;
import de.k3b.android.util.ClipboardUtil;
import de.k3b.android.util.FileManagerUtil;
+import de.k3b.android.util.IntentUtil;
import de.k3b.android.util.MediaScanner;
import de.k3b.android.widget.Dialogs;
import de.k3b.database.QueryParameter;
+import de.k3b.io.AlbumFile;
+import de.k3b.io.ListUtils;
+import de.k3b.io.VISIBILITY;
import de.k3b.io.collections.SelectedFiles;
import de.k3b.io.Directory;
import de.k3b.io.DirectoryNavigator;
@@ -75,7 +82,6 @@
import java.io.File;
import java.util.HashMap;
import java.util.List;
-// import static android.view.MenuItem.SHOW_AS_ACTION_NEVER;
/**
* A fragment with a Listing of Directories to be picked.
@@ -130,7 +136,9 @@ protected void onProgressUpdate(Integer... values) {
private static final java.lang.String INSTANCE_STATE_CONTEXT_MENU = "contextmenu";
// public state
- private IDirectory mCurrentSelection = null;
+ protected IDirectory mCurrentSelection = null;
+
+ private IDirectory mLastPopUpSelection = null;
// Layout
private HorizontalScrollView mParentPathBarScroller;
@@ -145,12 +153,13 @@ protected void onProgressUpdate(Integer... values) {
// local data
protected Activity mContext;
private DirectoryListAdapter mAdapter;
- private DirectoryNavigator mNavigation;
+ protected DirectoryNavigator mNavigation;
private int mDirTypId = 0;
protected int mTitleId = 0;
// api to fragment owner or null
private OnDirectoryInteractionListener mDirectoryListener = null;
+ private OnDirectoryPickListener mDirectoryPickListener = null;
// for debugging
private static int id = 1;
@@ -186,6 +195,10 @@ public DirectoryPickerFragment setContextMenuId(int contextMenuId) {
return this;
}
+ public IDirectory getLastPopUpSelection() {
+ return mLastPopUpSelection;
+ }
+
/****** live cycle ********/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -329,36 +342,36 @@ private PopupMenu onCreatePopupMenu(View anchor, IDirectory selection) {
PopupMenu popup = new PopupMenu(getActivity(), anchor);
popup.setOnMenuItemClickListener(popUpListener);
MenuInflater inflater = popup.getMenuInflater();
- inflater.inflate(this.mContextMenue, popup.getMenu());
+ Menu menu = popup.getMenu();
+ inflater.inflate(this.mContextMenue, menu);
if (selection != null) {
- mPopUpSelection = selection;
- MenuItem menuItem = popup.getMenu().findItem(R.id.cmd_fix_link);
String absoluteSelectedPath = selection.getAbsolute();
- if ((menuItem != null) && (FileUtils.isSymlinkDir(new File(absoluteSelectedPath), false))) {
- menuItem.setVisible(true);
- }
+ boolean isAlbumFile = AlbumFile.isQueryFile(absoluteSelectedPath);
+ mPopUpSelection = selection;
- menuItem = popup.getMenu().findItem(R.id.cmd_folder_hide_images);
- if ((menuItem != null) && MediaScanner.canHideFolderMedia(absoluteSelectedPath)) {
- menuItem.setVisible(true);
- }
+ setMenuVisibility(menu, R.id.cmd_fix_link, FileUtils.isSymlinkDir(new File(absoluteSelectedPath), false));
+ setMenuVisibility(menu, R.id.cmd_folder_hide_images, !isAlbumFile && MediaScanner.canHideFolderMedia(absoluteSelectedPath));
- if (!FotoLibGlobal.apmEnabled) {
- menuItem = popup.getMenu().findItem(R.id.cmd_apm_edit);
- if (menuItem != null) {
- menuItem.setVisible(false);
- }
- }
- menuItem = popup.getMenu().findItem(R.id.cmd_filemanager);
- if ((menuItem != null) && !FileManagerUtil.hasShowInFilemanager(getActivity(), absoluteSelectedPath)) {
- // no filemanager installed
- menuItem.setVisible(false);
- }
+ setMenuVisibility(menu, R.id.cmd_apm_edit, !isAlbumFile && FotoLibGlobal.apmEnabled);
+
+ setMenuVisibility(menu, R.id.cmd_filemanager, !isAlbumFile && FileManagerUtil.hasShowInFilemanager(getActivity(), absoluteSelectedPath));
+ setMenuVisibility(menu, R.id.cmd_delete, isAlbumFile);
+ setMenuVisibility(menu, R.id.action_edit, isAlbumFile);
+
+ setMenuVisibility(menu, android.R.id.copy, !isAlbumFile);
}
return popup;
}
+ private void setMenuVisibility(Menu menu, int menuId, boolean visibility) {
+ MenuItem menuItem = menu.findItem(menuId);
+ if (menuItem != null) {
+ menuItem.setVisible(visibility);
+ }
+
+ }
+
private IDirectory mPopUpSelection = null;
private final PopupMenu.OnMenuItemClickListener popUpListener = new PopupMenu.OnMenuItemClickListener() {
@Override
@@ -368,6 +381,7 @@ public boolean onMenuItemClick(MenuItem menuItem) {
};
protected boolean onPopUpClick(MenuItem menuItem, IDirectory popUpSelection) {
+ this.mLastPopUpSelection = popUpSelection;
switch (menuItem.getItemId()) {
case R.id.cmd_mk_dir:
return onCreateSubDirQuestion(popUpSelection);
@@ -378,6 +392,11 @@ protected boolean onPopUpClick(MenuItem menuItem, IDirectory popUpSelection) {
case R.id.menu_item_rename:
return onRenameDirQuestion(popUpSelection);
+ case R.id.cmd_delete:
+ return onDeleteDirQuestion(popUpSelection);
+
+ case R.id.action_edit:
+ return onEdit(popUpSelection);
case R.id.cmd_photo:
return showPhoto(popUpSelection);
@@ -402,10 +421,16 @@ protected boolean onPopUpClick(MenuItem menuItem, IDirectory popUpSelection) {
if ((requestCode == R.id.cmd_apm_edit) && (resultCode == Activity.RESULT_OK) && (mPopUpSelection != null)) {
// autoprocessing status may have changed: refresh data and gui
mPopUpSelection.refresh();
- this.mAdapter.notifyDataSetChanged();
+ // notifyDataSetChanged();
+ mAdapter = null;
+ reloadTreeViewIfAvailable();
}
}
+ public void notifyDataSetChanged() {
+ if (this.mAdapter != null) this.mAdapter.notifyDataSetChanged();
+ }
+
private boolean onCopy(IDirectory selection) {
String path = (selection == null) ? null : selection.getAbsolute();
return ClipboardUtil.addDirToClipboard(this.getActivity(), path);
@@ -414,7 +439,7 @@ private boolean onCopy(IDirectory selection) {
private boolean onEditApm(IDirectory selection) {
String path = (selection == null) ? null : selection.getAbsolute();
if (!StringUtils.isNullOrEmpty(path)) {
- PhotoAutoprocessingEditActivity.showActivity(getActivity(), null, path, this.getSrcFotos(), R.id.cmd_apm_edit);
+ PhotoAutoprocessingEditActivity.showActivity(getActivity(), null, path, getSrcFotos(), R.id.cmd_apm_edit);
return true;
}
return false;
@@ -439,6 +464,69 @@ protected void onDialogResult(String result, Object[] parameters) {
return true;
}
+ private boolean onEdit(IDirectory dir) {
+ if (dir != null) {
+ File file = FileUtils.tryGetCanonicalFile(dir.getAbsolute());
+ Intent sendIntent = new Intent();
+ IntentUtil.setDataAndTypeAndNormalize(sendIntent, Uri.fromFile(file), "text/plain");
+ sendIntent.setAction(Intent.ACTION_EDIT);
+ mContext.startActivity(sendIntent);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean onDeleteDirQuestion(final IDirectory parentDir) {
+ Dialogs dlg = new Dialogs() {
+ @Override protected void onDialogResult(String result, Object[] parameters) {
+ if (result != null) {
+ if (parentDir != null) {
+ File parentFile = FileUtils.tryGetCanonicalFile(parentDir.getAbsolute());
+ if (parentFile != null) {
+ onDeleteAnswer(parentFile, parentDir);
+ }
+ }
+ }
+ }
+ };
+
+ dlg.yesNoQuestion(mContext, parentDir.getRelPath(), mContext.getString(R.string.bookmark_delete_question));
+ return true;
+ }
+
+ private void onDeleteAnswer(File file, IDirectory dir) {
+ boolean deleteSuccess = false;
+
+ // delete from filesystem
+ if (file.exists() && file.delete()) {
+ deleteSuccess = true;
+ }
+
+ // delete from database
+ if (FotoSql.deleteMedia("delete album", getActivity(),
+ ListUtils.toStringList(file.getAbsolutePath()),false) > 0) {
+ deleteSuccess = true;
+ }
+
+ // delete from dir tree
+ IDirectory parent = (dir != null) ? dir.getParent() :null;
+ if (parent != null) {
+ parent.getChildren().remove(dir);
+ dir.destroy();
+ }
+
+ String message = (deleteSuccess)
+ ? mContext.getString(R.string.bookmark_delete_answer_format, file.getAbsoluteFile() )
+ : mContext.getString(R.string.bookmark_delete_error_format, file.getAbsoluteFile() );
+ Toast.makeText(mContext,
+ message,
+ Toast.LENGTH_LONG).show();
+ Log.d(Global.LOG_CONTEXT, message);
+
+ // notifyDataSetChanged();
+ mAdapter = null;
+ reloadTreeViewIfAvailable();
+ }
private boolean onRenameDirQuestion(final IDirectory parentDir) {
if (parentDir != null) {
File parentFile = FileUtils.tryGetCanonicalFile(parentDir.getAbsolute());
@@ -478,7 +566,7 @@ private void onRenameDirAnswer(final IDirectory srcDir, String newFolderName) {
} else {
// update dirpicker
srcDir.rename(srcDirFile.getName(), newFolderName);
- this.mAdapter.notifyDataSetChanged();
+ notifyDataSetChanged();
}
}
@@ -534,11 +622,11 @@ private boolean fixLinks(IDirectory linkDir) {
if (!canonicalPath.endsWith("/")) canonicalPath+="/";
String sqlWhereLink = FotoSql.SQL_COL_PATH + " like '" + linkPath + "%'";
- SelectedFiles linkFiles = FotoSql.getSelectedfiles(context, sqlWhereLink);
+ SelectedFiles linkFiles = FotoSql.getSelectedfiles(context, sqlWhereLink, VISIBILITY.PRIVATE_PUBLIC);
String sqlWhereCanonical = FotoSql.SQL_COL_PATH + " in (" + linkFiles.toString() + ")";
sqlWhereCanonical = sqlWhereCanonical.replace(linkPath,canonicalPath);
- SelectedFiles canonicalFiles = FotoSql.getSelectedfiles(context, sqlWhereCanonical);
+ SelectedFiles canonicalFiles = FotoSql.getSelectedfiles(context, sqlWhereCanonical, VISIBILITY.PRIVATE_PUBLIC);
HashMap link2canonical = new HashMap();
for(String cann : canonicalFiles.getFileNames()) {
link2canonical.put(linkPath + cann.substring(canonicalPath.length()), cann);
@@ -592,17 +680,27 @@ private boolean showDirInfo(IDirectory selectedDir) {
return false;
}
- private boolean showPhoto(IDirectory selectedDir) {
+ private QueryParameter getSelectionQuery(String dbgContext, IDirectory selectedDir) {
String pathFilter = (selectedDir != null) ? selectedDir.getAbsolute() : null;
+ QueryParameter query = null;
if (pathFilter != null) {
- GalleryFilterParameter filter = new GalleryFilterParameter(); //.setPath(pathFilter);
- if (!FotoSql.set(filter, pathFilter, mDirTypId))
- {
- filter.setPath(pathFilter + "/%");
+ query = AndroidAlbumUtils.getQueryFromUri(dbgContext, getActivity(), Uri.fromFile(new File(pathFilter)), null);
+
+ if (query == null) {
+ GalleryFilterParameter filter = new GalleryFilterParameter(); //.setPath(pathFilter);
+ if (!FotoSql.set(filter, pathFilter, mDirTypId)) {
+ filter.setPath(pathFilter + "/%");
+ }
+
+ query = TagSql.filter2NewQuery(filter);
}
+ }
+ return query;
- QueryParameter query = new QueryParameter();
- TagSql.filter2QueryEx(query, filter, true);
+ }
+ private boolean showPhoto(IDirectory selectedDir) {
+ QueryParameter query = getSelectionQuery("showPhoto", selectedDir);
+ if (query != null) {
FotoSql.setSort(query, FotoSql.SORT_BY_DATE, false);
ImageDetailActivityViewPager.showActivity(this.getActivity(), null, 0, query, 0);
return true;
@@ -611,15 +709,9 @@ private boolean showPhoto(IDirectory selectedDir) {
}
private boolean showGallery(IDirectory selectedDir) {
- String pathFilter = (selectedDir != null) ? selectedDir.getAbsolute() : null;
- if (pathFilter != null) {
- GalleryFilterParameter filter = new GalleryFilterParameter(); //.setPath(pathFilter);
- if (!FotoSql.set(filter, pathFilter, mDirTypId))
- {
- filter.setPath(pathFilter + "/%");
- }
-
- FotoGalleryActivity.showActivity(this.getActivity(), filter, null, 0);
+ QueryParameter query = getSelectionQuery("showGallery", selectedDir);
+ if (query != null) {
+ FotoGalleryActivity.showActivity(this.getActivity(), query, 0);
return true;
}
return false;
@@ -641,8 +733,8 @@ private void onDirectoryCancel() {
protected void onDirectoryPick(IDirectory selection) {
closeAll();
Log.d(Global.LOG_CONTEXT, debugPrefix + "onDirectoryPick: " + selection);
- if ((mDirectoryListener != null) && (selection != null)) {
- mDirectoryListener.onDirectoryPick(selection.getAbsolute()
+ if ((mDirectoryPickListener != null) && (selection != null)) {
+ mDirectoryPickListener.onDirectoryPick(selection.getAbsolute()
, mDirTypId);
dismiss();
}
@@ -674,6 +766,7 @@ public void closeAll() {
}
@Override public void onDestroy() {
+ mLastPopUpSelection = null;
closeAll();
super.onDestroy();
// RefWatcher refWatcher = AndroFotoFinderApp.getRefWatcher(getActivity());
@@ -688,7 +781,13 @@ public void onAttach(Activity activity) {
protected void setDirectoryListener(Activity activity) {
try {
- mDirectoryListener = (OnDirectoryInteractionListener) activity;
+ if ((activity == null) || activity instanceof OnDirectoryInteractionListener) {
+ mDirectoryListener = (OnDirectoryInteractionListener) activity;
+ }
+
+ if ((activity == null) || activity instanceof OnDirectoryPickListener) {
+ mDirectoryPickListener = (OnDirectoryPickListener) activity;
+ }
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnDirectoryInteractionListener");
@@ -752,11 +851,9 @@ protected String getStatusErrorMessage(String path) {
}
private void updateStatus() {
- int itemCount = getItemCount(mCurrentSelection);
-
String selectedPath = (this.mCurrentSelection != null) ? this.mCurrentSelection.getAbsolute() : null;
- String statusMessage = (itemCount == 0) ? mContext.getString(R.string.selection_none_hint) : getStatusErrorMessage(selectedPath);
+ String statusMessage = (!isPickable(mCurrentSelection)) ? mContext.getString(R.string.selection_none_hint) : getStatusErrorMessage(selectedPath);
boolean canPressOk = (statusMessage == null);
if (mCmdOk != null) mCmdOk.setEnabled(canPressOk);
@@ -765,6 +862,14 @@ private void updateStatus() {
setStatusMessage(statusMessage);
}
+ /** decides if an item can be picked */
+ protected boolean isPickable(IDirectory selection) {
+ if (selection == null) return false;
+ if (AlbumFile.isQueryFile (selection.getRelPath())) return true;
+ int itemCount = getItemCount(selection);
+ return (itemCount > 0);
+ }
+
private void setStatusMessage(String statusMessage) {
if (mStatus != null) {
if (statusMessage == null) {
@@ -787,7 +892,7 @@ private int getItemCount(IDirectory _directory) {
/*********************** local helper *******************************************/
private void updateParentPathBar(String currentSelection) {
if (this.mNavigation != null) {
- updateParentPathBar(this.mNavigation.getRoot().find(currentSelection));
+ updateParentPathBar(getSelectedDir(currentSelection));
}
}
@@ -864,6 +969,11 @@ public void defineDirectoryNavigation(IDirectory root, int dirTypId, String init
navigateTo(initialAbsolutePath);
}
+ public IDirectory getRoot() {
+ if (mNavigation != null) return mNavigation.getRoot();
+ return null;
+ }
+
/** refreshLocal tree to new newGrandParent by preserving selection */
private void navigateTo(int newGroupSelection, IDirectory newGrandParent) {
if (newGrandParent != null) {
@@ -888,7 +998,7 @@ public void navigateTo(String absolutePath) {
}
if ((mNavigation != null) && (absolutePath != null)) {
- mCurrentSelection = mNavigation.getRoot().find(absolutePath);
+ mCurrentSelection = getSelectedDir(absolutePath);
mNavigation.navigateTo(mCurrentSelection);
}
@@ -897,6 +1007,10 @@ public void navigateTo(String absolutePath) {
reloadTreeViewIfAvailable();
}
+ protected IDirectory getSelectedDir(String absolutePath) {
+ return mNavigation.getRoot().find(absolutePath);
+ }
+
/** Does nothing if either OnCreate() or defineDirectoryNavigation() has NOT been called yet */
private boolean reloadTreeViewIfAvailable() {
if ((mTreeView != null) && (mNavigation != null)) {
@@ -922,6 +1036,11 @@ public SelectedFiles getSrcFotos() {
return null;
}
+ public interface OnDirectoryPickListener {
+ /** called when user picks a new directory */
+ void onDirectoryPick(String selectedAbsolutePath, int queryTypeId);
+ }
+
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
@@ -932,10 +1051,7 @@ public SelectedFiles getSrcFotos() {
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments for more information.
*/
- public interface OnDirectoryInteractionListener {
- /** called when user picks a new directory */
- void onDirectoryPick(String selectedAbsolutePath, int queryTypeId);
-
+ public interface OnDirectoryInteractionListener extends OnDirectoryPickListener {
/** called when user cancels picking of a new directory */
void onDirectoryCancel(int queryTypeId);
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/directory/SaveAsPickerFragment.java b/app/src/main/java/de/k3b/android/androFotoFinder/directory/SaveAsPickerFragment.java
new file mode 100644
index 00000000..ffcd0231
--- /dev/null
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/directory/SaveAsPickerFragment.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2018 by k3b.
+ *
+ * This file is part of AndroFotoFinder / #APhotoManager.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see
+ */
+
+package de.k3b.android.androFotoFinder.directory;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import java.io.File;
+
+import de.k3b.android.androFotoFinder.AffUtils;
+import de.k3b.android.androFotoFinder.R;
+import de.k3b.android.androFotoFinder.imagedetail.ImageDetailActivityViewPager;
+import de.k3b.android.androFotoFinder.queries.FotoSql;
+import de.k3b.android.util.AndroidFileCommands;
+import de.k3b.android.util.OsUtils;
+import de.k3b.io.AlbumFile;
+import de.k3b.io.FileUtils;
+import de.k3b.io.IDirectory;
+import de.k3b.io.OSDirOrVirtualAlbumFile;
+import de.k3b.io.OSDirectory;
+import de.k3b.io.StringUtils;
+import de.k3b.io.collections.SelectedFiles;
+
+/**
+ * a picker with a fale name field and a directory picker.
+ *
+ * Created by k3b on 12.04.2018.
+ */
+
+public abstract class SaveAsPickerFragment extends DirectoryPickerFragment {
+ // these get lost on screen rotation so dialog must be closed on screen rotation.
+ private EditText edName = null;
+ private File path = null;
+ private String extension = ".txt";
+
+ public SaveAsPickerFragment(File path) {
+ this.path = path;
+ if (path != null) {
+ this.extension = FileUtils.getExtension(path.getName());
+
+ OSDirectory root = OsUtils.getRootOSDirectory(new OSDirOrVirtualAlbumFile(null, null, null));
+ this.defineDirectoryNavigation(root, FotoSql.QUERY_TYPE_UNDEFINED, FileUtils.tryGetCanonicalPath(path, null));
+ }
+ }
+
+ /* do not use activity callback */
+ @Override
+ protected void setDirectoryListener(Activity activity) {/* do not use activity callback */}
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View result = super.onCreateView(inflater,container,savedInstanceState);
+
+ result.findViewById(R.id.lblName).setVisibility(View.VISIBLE);
+ edName = (EditText) result.findViewById(R.id.edName);
+ edName.setVisibility(View.VISIBLE);
+
+ if (path != null) {
+ edName.setText(FileUtils.replaceExtension(path.getName(), ""));
+ }
+ return result;
+ }
+
+ public File getCurrentPath() {
+ if (mCurrentSelection == null) return null;
+ return new File(mCurrentSelection.getAbsolute(), edName.getText().toString() + extension );
+ }
+
+ /**
+ * To be overwritten to check if a path can be picked.
+ *
+ * @param path to be checked if it cannot be handled
+ * @return null if no error else error message with the reason why it cannot be selected
+ */
+ @Override
+ protected String getStatusErrorMessage(String path) {
+ // writeprotected
+ // overwriteExisting
+ // ok (new file or original file name proposal
+ String errorMessage = null;
+ if (errorMessage != null) {
+ int pos = errorMessage.indexOf('\n');
+ return (pos > 0) ? errorMessage.substring(0,pos) : errorMessage;
+ }
+ return super.getStatusErrorMessage(path);
+ }
+
+ @Override
+ protected void onDirectoryPick(IDirectory selection) {
+ IDirectory result = null;
+
+ String filenameWithoutExtension = StringUtils.trim(this.edName.getText());
+ String fullPath = (selection == null) ? null : selection.getAbsolute();
+ File sel = (StringUtils.isNullOrEmpty(fullPath)) ? null : new File(fullPath);
+ if (sel != null) {
+ if (sel.isFile()) {
+ result = selection;
+ } else if (sel.isDirectory() && !StringUtils.isNullOrEmpty(filenameWithoutExtension)) {
+ final String newName = filenameWithoutExtension + AlbumFile.SUFFIX_VALBUM;
+ final File newFile = new File(sel, newName);
+ result = selection.find(newFile.getAbsolutePath());
+ if (result == null) {
+ result = selection.createOsDirectory(newFile, selection, null);
+ }
+ }
+ }
+
+ if (result != null) {
+ // close dialog and return to caller
+ super.onDirectoryPick(result);
+ onFilePick(new File(result.getAbsolute()));
+ this.notifyDataSetChanged();
+ dismiss();
+ }
+ }
+
+ /** decides if an item can be picked */
+ protected boolean isPickable(IDirectory selection) {
+ // if ((selection != null) && AlbumFile.isQueryFile (selection.getRelPath()) ) return true;
+ return super.isPickable(selection);
+ }
+
+ @Override
+ protected IDirectory getSelectedDir(String absolutePath) {
+ if (absolutePath == null) return null;
+
+ File abs = new File(absolutePath);
+ if (!abs.isDirectory()) return super.getSelectedDir(abs.getParent());
+
+ return super.getSelectedDir(absolutePath);
+ }
+
+ abstract protected void onFilePick(File pickedOrCreatedFile);
+}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/gallery/cursor/GalleryCursorFragment.java b/app/src/main/java/de/k3b/android/androFotoFinder/gallery/cursor/GalleryCursorFragment.java
index 019951a6..6a9ba9d2 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/gallery/cursor/GalleryCursorFragment.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/gallery/cursor/GalleryCursorFragment.java
@@ -19,12 +19,16 @@
package de.k3b.android.androFotoFinder.gallery.cursor;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.LoaderManager;
+import android.content.ClipData;
+import android.content.ContentResolver;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.app.Fragment;
import android.support.annotation.NonNull;
@@ -68,6 +72,7 @@
import de.k3b.android.androFotoFinder.OnGalleryInteractionListener;
import de.k3b.android.androFotoFinder.queries.Queryable;
import de.k3b.android.androFotoFinder.queries.SqlJobTaskBase;
+import de.k3b.android.androFotoFinder.tagDB.TagSql;
import de.k3b.android.androFotoFinder.tagDB.TagTask;
import de.k3b.android.androFotoFinder.tagDB.TagWorflow;
import de.k3b.android.androFotoFinder.tagDB.TagsPickerFragment;
@@ -76,8 +81,11 @@
import de.k3b.android.util.MediaScanner;
import de.k3b.android.util.OsUtils;
import de.k3b.android.util.ResourceUtils;
+import de.k3b.android.widget.AboutDialogPreference;
import de.k3b.android.widget.Dialogs;
import de.k3b.database.QueryParameter;
+import de.k3b.io.ListUtils;
+import de.k3b.io.StringUtils;
import de.k3b.io.VISIBILITY;
import de.k3b.io.collections.SelectedFiles;
import de.k3b.io.collections.SelectedItems;
@@ -108,9 +116,11 @@ public class GalleryCursorFragment extends Fragment implements Queryable, Direc
private static final String INSTANCE_STATE_SEL_ONLY = "selectedOnly";
private static final String INSTANCE_STATE_LOADER_ID = "loaderID";
- private static final int MODE_VIEW = 0;
- private static final int MODE_PICK_SINGLE = 1;
- private static final int MODE_PICK_MULTIBLE = 2;
+ private static final int MODE_VIEW_PICKER_NONE = 0;
+ private static final int MODE_VIEW_PICK_SINGLE = 1;
+ private static final int MODE_VIEW_PICK_MULTIBLE = 2;
+
+ private static final boolean SUPPORT_MULTISELECTION = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN);
private static int nextLoaderID = 100;
private int loaderID = -1;
@@ -151,9 +161,10 @@ public class GalleryCursorFragment extends Fragment implements Queryable, Direc
private boolean mNoShareError = true;
/** true pick geo; false pick image */
- private boolean mGetGeo = false;
+ private boolean mModePickGeoElsePickImaage = false;
- private int mMode = MODE_VIEW;
+ /** one of the MODE_VIEW_PICKER_XXXX */
+ private int mMode = MODE_VIEW_PICKER_NONE;
private MoveOrCopyDestDirPicker mDestDirPicker = null;
/**************** construction ******************/
@@ -366,10 +377,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
String action = (intent != null) ? intent.getAction() : null;
if ((action != null) && ((Intent.ACTION_PICK.compareTo(action) == 0) || (Intent.ACTION_GET_CONTENT.compareTo(action) == 0))) {
- this.mMode = (intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE,false)) ? MODE_PICK_MULTIBLE : MODE_PICK_SINGLE;
+ this.mMode = (intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE,false)) ? MODE_VIEW_PICK_MULTIBLE : MODE_VIEW_PICK_SINGLE;
mMustReplaceMenue = true;
String schema = intent.getScheme();
- mGetGeo = ((schema != null) && ("geo".compareTo(schema) == 0));
+ mModePickGeoElsePickImaage = ((schema != null) && ("geo".compareTo(schema) == 0));
}
String path = (intent == null) ? null : intent.getStringExtra(AffUtils.EXTRA_SELECTED_ITEM_PATHS);
@@ -627,7 +638,7 @@ private void onOpenChildGallery(QueryParameter subGalleryQuery) {
if (Global.debugEnabledSql) {
Log.i(Global.LOG_CONTEXT, "Exec child gallery\n\t" + subGalleryQuery.toSqlString());
}
- FotoGalleryActivity.showActivity(getActivity(), null, subGalleryQuery, 0);
+ FotoGalleryActivity.showActivity(getActivity(), subGalleryQuery, 0);
}
/****************** path navigation *************************/
@@ -758,6 +769,27 @@ private void startMultiSelectionMode() {
getActivity().invalidateOptionsMenu();
}
+ /**
+ * Different menu modes
+ * * normal name searchbar-icon folder map (tags) (filter) menu
+ * * R.menu.menu_gallery_non_selected_only R.menu.menu_gallery_non_multiselect
+ * * selected selected cancel seleted-only share (save) menu
+ * * (Filter not available; this.isMultiSelectionActive();
+ * * R.menu.menu_gallery_multiselect_mode_all R.menu.menu_image_commands
+ * * locked name lock folder map menu
+ * * (no multiselection, no base-filters)
+ * * (this.locked; R.menu.menu_locked)
+ * * searchbar bar cancel-searc-bar (folder) (map) (tags) (filter) menu
+ * * picker-locked
+ * * R.menu.menu_gallery_pick R.menu.menu_locked
+ * * picker-non-locked selected ok cancel filter settings
+ * * R.menu.menu_gallery_pick R.menu.menu_gallery_non_multiselect
+ *
+ * (xxxx) with "IFROOM" (in wide screen only)
+ * searchbarmode is like "normal" where there is no "IFROOM" on no-searchbar items
+ *
+ * @param menu
+ */
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
@@ -769,12 +801,16 @@ public void onPrepareOptionsMenu(Menu menu) {
mMustReplaceMenue = false;
menu.clear();
mMenuRemoveAllSelected = null;
- if (mMode == MODE_VIEW) {
+ if (mMode == MODE_VIEW_PICKER_NONE) {
+ //
if (locked) { // view-locked
- mSelectedItems.clear();
- inflater.inflate(R.menu.menu_gallery_locked, menu);
- LockScreen.fixMenu(menu);
- } else if (isMultiSelectionActive()) { // view-multiselect
+ if (isMultiSelectionActive()) {
+ clearSelections();
+ }
+ inflater.inflate(R.menu.menu_locked, menu);
+ LockScreen.removeDangerousCommandsFromMenu(menu);
+ AboutDialogPreference.onPrepareOptionsMenu(getActivity(), menu);
+ } else if (this.isMultiSelectionActive()) { // view-multiselect
inflater.inflate(R.menu.menu_gallery_multiselect_mode_all, menu);
mShareOnlyToggle = menu.findItem(R.id.cmd_selected_only);
@@ -796,14 +832,14 @@ public void onPrepareOptionsMenu(Menu menu) {
}
- } else {
+ } else { // picker mode
inflater.inflate(R.menu.menu_gallery_pick, menu);
- if (locked) { // pick-locked
- mSelectedItems.clear();
- inflater.inflate(R.menu.menu_gallery_locked, menu);
- } else { // pick-single/multible
+ if (!locked) {
inflater.inflate(R.menu.menu_gallery_non_multiselect, menu);
+ } else { // pick-locked
+ LockScreen.removeDangerousCommandsFromMenu(menu);
}
+ AboutDialogPreference.onPrepareOptionsMenu(getActivity(), menu);
}
mMenuRemoveAllSelected = menu.findItem(R.id.cmd_selection_remove_all);
}
@@ -852,7 +888,7 @@ public boolean onOptionsItemSelected(MenuItem menuItem) {
case R.id.cmd_move:
return cmdMoveOrCopyWithDestDirPicker(true, fileCommands.getLastCopyToPath(), selectedFiles);
case R.id.cmd_show_geo:
- MapGeoPickerActivity.showActivity(this.getActivity(), selectedFiles, null);
+ MapGeoPickerActivity.showActivity(this.getActivity(), selectedFiles, null, 0);
return true;
case R.id.cmd_edit_geo:
GeoEditActivity.showActivity(this.getActivity(), selectedFiles, GeoEditActivity.RESULT_ID);
@@ -883,14 +919,28 @@ public boolean onOptionsItemSelected(MenuItem menuItem) {
private void cmdShowDetails() {
SelectedItems ids = getSelectedItems();
- String files = ((ids != null) && (ids.size() > 0)) ? mAdapter.createSelectedFiles(this.getActivity(), ids).toString().replace(",","\n") : null;
+
+ final Activity activity = this.getActivity();
+ CharSequence subQueryTypName = (activity instanceof FotoGalleryActivity)
+ ? ((FotoGalleryActivity)activity).getValueAsTitle(true)
+ : null;
+
+ String files = ((ids != null) && (ids.size() > 0)) ? mAdapter.createSelectedFiles(activity, ids).toString().replace(",","\n") : null;
ImageDetailMetaDialogBuilder.createImageDetailDialog(
- this.getActivity(),
+ activity,
getActivity().getTitle().toString(),
this.toString(),
ids,
files,
- (mGalleryContentQuery != null) ? mGalleryContentQuery.toSqlString() : null
+ (mGalleryContentQuery != null) ? mGalleryContentQuery.toSqlString() : null,
+ StringUtils.appendMessage(null,
+ getString(R.string.show_photo),
+ TagSql.getCount(activity, mGalleryContentQuery)),
+ subQueryTypName,
+ (mGalleryContentQuery == null) ? null : StringUtils.appendMessage(null,
+ getString(R.string.show_photo),
+ TagSql.getCount(activity, mGalleryContentQuery))
+
).show();
}
@@ -991,12 +1041,11 @@ protected void onDirectoryPick(IDirectory selection) {
dismiss();
}
};
-
private boolean cmdMoveOrCopyWithDestDirPicker(final boolean move, String lastCopyToPath, final SelectedFiles fotos) {
if (AndroidFileCommands.canProcessFile(this.getActivity(), false)) {
mDestDirPicker = MoveOrCopyDestDirPicker.newInstance(move, fotos);
- mDestDirPicker.defineDirectoryNavigation(OsUtils.getRootOSDirectory(),
+ mDestDirPicker.defineDirectoryNavigation(OsUtils.getRootOSDirectory(null),
(move) ? FotoSql.QUERY_TYPE_GROUP_MOVE : FotoSql.QUERY_TYPE_GROUP_COPY,
lastCopyToPath);
mDestDirPicker.setContextMenuId(LockScreen.isLocked(this.getActivity()) ? 0 : R.menu.menu_context_osdir);
@@ -1008,14 +1057,15 @@ private boolean cmdMoveOrCopyWithDestDirPicker(final boolean move, String lastCo
private boolean onPickOk() {
mDestDirPicker = null;
Activity parent = getActivity();
- Uri resultUri = getSelectedUri(parent);
+ List resultUri = getSelectedUri(parent, "onPickOk");
- if (resultUri != null) {
+ if ((resultUri != null) && (resultUri.size() > 0)) {
final Intent intent = new Intent();
- // permission result.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- intent.setData(resultUri);
- if (!mGetGeo) {
+ intent.setData(resultUri.get(0));
+ addAsClip(intent, parent.getContentResolver(), resultUri);
+ if (!mModePickGeoElsePickImaage) {
+ // permission result.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -1026,27 +1076,63 @@ private boolean onPickOk() {
return true;
}
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ private void addAsClip(Intent intent, ContentResolver contentResolver, List resultUri) {
+ if (SUPPORT_MULTISELECTION && (resultUri.size() > 1)) {
+ ClipData clip = null;
+ for (Uri uri : resultUri) {
+ ClipData clipData = ClipData.newUri(contentResolver, uri.toString(), uri);
+ if (clip == null) {
+ clip = clipData;
+ } else {
+ clip.addItem(clipData.getItemAt(0));
+ }
+ intent.setClipData(clip);
+ }
+ }
+ }
+
@Nullable
- private Uri getSelectedUri(Activity parent) {
- Uri resultUri = null;
+ private List getSelectedUri(Activity parent, Object... dbgContext) {
+ StringBuilder debugMessage = StringUtils.createDebugMessage(Global.debugEnabledSql,mDebugPrefix,"getSelectedUri", dbgContext);
+
+ List resultUri = null;
SelectedItems items = getSelectedItems();
if ((items != null) && (items.size() > 0)) {
long id = items.first();
- if (mGetGeo) {
- IGeoPoint initialPoint = FotoSql.execGetPosition(parent, null, id);
+ if (resultUri == null) {
+ resultUri = new ArrayList();
+ }
+ resultUri.add(getUri(parent, id));
+ }
+ if (debugMessage != null) {
- if (initialPoint != null) {
- GeoUri PARSER = new GeoUri(GeoUri.OPT_PARSE_INFER_MISSING);
+ StringUtils.appendMessage(debugMessage, "result",
+ ListUtils.toString(" ", resultUri));
+ Log.i(Global.LOG_CONTEXT, debugMessage.toString());
+ }
+ return resultUri;
+ }
- resultUri = Uri.parse(PARSER.toUriString(new GeoPointDto(initialPoint.getLatitude(),initialPoint.getLongitude(), IGeoPointInfo.NO_ZOOM)));
- }
+ private Uri getUri(Activity parent, long id) {
+ Uri resultUri = null;
+ if (mModePickGeoElsePickImaage) {
+ // mode pick gep
+ IGeoPoint initialPoint = FotoSql.execGetPosition(null, parent,
+ null, id, mDebugPrefix, "getSelectedUri");
+ if (initialPoint != null) {
+ GeoUri PARSER = new GeoUri(GeoUri.OPT_PARSE_INFER_MISSING);
- } else {
- resultUri = FotoSql.getUri(id);
+ resultUri = Uri.parse(PARSER.toUriString(new GeoPointDto(initialPoint.getLatitude(),initialPoint.getLongitude(), IGeoPointInfo.NO_ZOOM)));
}
+
+
+ } else {
+ // mode pick image
+ resultUri = FotoSql.getUri(id);
}
return resultUri;
}
@@ -1316,7 +1402,7 @@ private void onDuplicatesFound(SelectedItems selectedItems, StringBuffer debugMe
}
private boolean isMultiSelectionActive() {
- if (mMode != MODE_VIEW) return true;
+ if (mMode != MODE_VIEW_PICKER_NONE) return true;
return !mSelectedItems.isEmpty();
}
@@ -1344,7 +1430,7 @@ private void fix() {
/** return true if included; false if excluded */
private boolean toggleSelection(long imageID) {
boolean contains = mSelectedItems.contains(imageID);
- if (mMode == MODE_PICK_SINGLE) {
+ if (mMode == MODE_VIEW_PICK_SINGLE) {
clearSelections();
}
if (contains) {
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailActivityViewPager.java b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailActivityViewPager.java
index a7d4bf1d..3745893f 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailActivityViewPager.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailActivityViewPager.java
@@ -830,7 +830,7 @@ public boolean onCreateOptionsMenu(Menu menu) {
private void defineMenu(Menu menu) {
if (LockScreen.isLocked(this)) {
getMenuInflater().inflate(R.menu.menu_image_detail_locked, menu);
- LockScreen.fixMenu(menu);
+ LockScreen.removeDangerousCommandsFromMenu(menu);
} else {
getMenuInflater().inflate(R.menu.menu_image_detail, menu);
@@ -950,18 +950,20 @@ public boolean onOptionsItemSelected(MenuItem item) {
newFilter.setPath(dirPath);
// int callBackId = (MediaScanner.isNoMedia(dirPath,MediaScanner.DEFAULT_SCAN_DEPTH)) ? NOMEDIA_GALLERY : 0;
- FotoGalleryActivity.showActivity(this, this.mFilter, null, 0);
+ QueryParameter query = TagSql.filter2NewQuery(this.mFilter);
+ FotoGalleryActivity.showActivity(this, query, 0);
}
break;
}
case R.id.cmd_show_geo:
- MapGeoPickerActivity.showActivity(this, getCurrentFoto(), null);
+ MapGeoPickerActivity.showActivity(this, getCurrentFoto(), null, 0);
break;
case R.id.cmd_show_geo_as: {
final long imageId = getCurrentImageId();
- IGeoPoint _geo = FotoSql.execGetPosition(this, null, imageId);
+ IGeoPoint _geo = FotoSql.execGetPosition(null, this,
+ null, imageId, mDebugPrefix, "on cmd_show_geo_as");
final String currentFilePath = getCurrentFilePath();
GeoPointDto geo = new GeoPointDto(_geo.getLatitude(), _geo.getLongitude(), GeoPointDto.NO_ZOOM);
@@ -1056,14 +1058,22 @@ private void onSlideShowNext() {
private void cmdShowDetails(String fullFilePath, long currentImageId) {
- ImageDetailMetaDialogBuilder.createImageDetailDialog(this, fullFilePath, currentImageId, mGalleryContentQuery, mViewPager.getCurrentItem()).show();
+ StringBuilder countMsg = (mGalleryContentQuery == null) ? null : StringUtils.appendMessage(null,
+ getString(R.string.show_photo),
+ TagSql.getCount(this, mGalleryContentQuery));
+
+ ImageDetailMetaDialogBuilder.createImageDetailDialog(this, fullFilePath, currentImageId,
+ mGalleryContentQuery,
+ mViewPager.getCurrentItem(),
+ countMsg).show();
+
}
private boolean cmdMoveOrCopyWithDestDirPicker(final boolean move, String lastCopyToPath, final SelectedFiles fotos) {
if (AndroidFileCommands.canProcessFile(this, false)) {
mDestDirPicker = MoveOrCopyDestDirPicker.newInstance(move, fotos);
- mDestDirPicker.defineDirectoryNavigation(OsUtils.getRootOSDirectory(),
+ mDestDirPicker.defineDirectoryNavigation(OsUtils.getRootOSDirectory(null),
(move) ? FotoSql.QUERY_TYPE_GROUP_MOVE : FotoSql.QUERY_TYPE_GROUP_COPY,
lastCopyToPath);
mDestDirPicker.setContextMenuId(LockScreen.isLocked(this) ? 0 : R.menu.menu_context_osdir);
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java
index 2009544e..f05a2774 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java
@@ -51,7 +51,7 @@ public class ImageDetailMetaDialogBuilder {
public static Dialog createImageDetailDialog(Activity context, String filePath, long imageId,
QueryParameter query,
- long offset) {
+ long offset, Object... moreBlocks) {
StringBuilder result = new StringBuilder();
result
.append(imageId)
@@ -59,6 +59,15 @@ public static Dialog createImageDetailDialog(Activity context, String filePath,
.append("\n");
appendExifInfo(result, context, filePath, imageId);
appendQueryInfo(result, query, offset);
+
+ if ((moreBlocks != null) && (moreBlocks.length > 0)) {
+ for (Object subBlock : moreBlocks) {
+ if (subBlock != null) {
+ append(result,"\n");
+ append(result, subBlock.toString());
+ }
+ }
+ }
return createImageDetailDialog(context, filePath, result.toString());
}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/LocationMapFragment.java b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/LocationMapFragment.java
index 58bfcddb..f8964a3d 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/LocationMapFragment.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/LocationMapFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder / #APhotoManager.
*
@@ -40,6 +40,7 @@
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.SeekBar;
+import android.widget.Toast;
import org.osmdroid.api.*;
import org.osmdroid.events.DelayedMapListener;
@@ -64,8 +65,8 @@
import de.k3b.android.androFotoFinder.R;
import de.k3b.android.androFotoFinder.ThumbNailUtils;
import de.k3b.android.androFotoFinder.imagedetail.ImageDetailActivityViewPager;
+import de.k3b.android.androFotoFinder.queries.AndroidAlbumUtils;
import de.k3b.android.androFotoFinder.queries.FotoSql;
-import de.k3b.android.androFotoFinder.tagDB.TagSql;
import de.k3b.android.osmdroid.FolderOverlayEx;
import de.k3b.android.osmdroid.GuestureOverlay;
import de.k3b.android.osmdroid.IconOverlay;
@@ -83,7 +84,6 @@
import de.k3b.geo.api.IGeoPointInfo;
import de.k3b.geo.io.GeoUri;
import de.k3b.geo.io.gpx.GpxReaderBase;
-import de.k3b.io.GalleryFilterParameter;
import de.k3b.io.GeoRectangle;
import de.k3b.io.IGalleryFilter;
import de.k3b.io.IGeoRectangle;
@@ -98,6 +98,8 @@ public class LocationMapFragment extends DialogFragment {
protected String STATE_LAST_VIEWPORT = "LAST_VIEWPORT";
private static final int NO_ZOOM = OsmdroidUtil.NO_ZOOM;
+ private static final int RECALCULATE_ZOOM = OsmdroidUtil.RECALCULATE_ZOOM;
+
/** If there is more than 200 millisecs no zoom/scroll update markers */
protected static final int DEFAULT_INACTIVITY_DELAY_IN_MILLISECS = 200;
@@ -137,10 +139,11 @@ public class LocationMapFragment extends DialogFragment {
* see http://stackoverflow.com/questions/10411975/how-to-get-the-width-and-height-of-an-image-view-in-android/10412209#10412209
*/
private BoundingBox mDelayedZoomToBoundingBox = null;
- private int mDelayedZoomLevel = NO_ZOOM;
+ private int mDelayedZoomLevel = RECALCULATE_ZOOM;
private boolean mIsInitialized = false;
- private IGalleryFilter mRootFilter;
+ private QueryParameter mRootQuery;
+
private double mMinZoomLevel;
private double mMaxZoomLevel;
@@ -328,7 +331,8 @@ public boolean onZoom(ZoomEvent event) {
@Override
public void onFirstLayout(View v, int left, int top, int right, int bottom) {
mIsInitialized = true;
- zoomToBoundingBox("onFirstLayout", mDelayedZoomToBoundingBox, mDelayedZoomLevel);
+ final String why = "onFirstLayout";
+ int newZoom = zoomToBoundingBox(why, mDelayedZoomToBoundingBox, mDelayedZoomLevel);
mDelayedZoomToBoundingBox = null;
mDelayedZoomLevel = NO_ZOOM;
}
@@ -437,14 +441,16 @@ private void createZoomBar(View view) {
mZoomBar = (SeekBar) view.findViewById(R.id.zoomBar);
mMinZoomLevel = mMapView.getMinZoomLevel();
- mMaxZoomLevel = mMapView.getMaxZoomLevel();
+ mMaxZoomLevel = OsmdroidUtil.getMaximumZoomLevel(mMapView);
mZoomBar.setMax((int) (mMaxZoomLevel - mMinZoomLevel));
mZoomBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (fromUser)
+ if (fromUser) {
mMapView.getController().setZoom(progress + mMinZoomLevel);
+ }
+ Toast.makeText(getActivity(), "" + progress, Toast.LENGTH_SHORT).show();
}
@Override
@@ -467,38 +473,58 @@ private FolderOverlayEx createFolderOverlay(List overlays) {
return result;
}
- public void defineNavigation(IGalleryFilter rootFilter, GeoRectangle rectangle, int zoomlevel,
- SelectedItems selectedItems, Uri gpxAdditionalPointsContentUri) {
+ /**
+ * (re)define map display. Data sohow from rootQuery + rectangle + zoomlevel.
+ *
+ * @param rootQuery if not null contain database where to limit the photo data displayed
+ * @param depricated_rootFilter should be null. if not null contain database where to limit the data displayed
+ * @param rectangle if nut null the initial visible rectange
+ * @param zoomlevel the initial zoomlevel
+ * @param selectedItems if not null: items to be displayed as blue markers
+ * @param gpxAdditionalPointsContentUri if not null the file containing additional geo coords for blue marker
+ * @param zoomToFit true mean recalculate zoomlevel from rectangle
+ */
+ public void defineNavigation(QueryParameter rootQuery,
+ IGalleryFilter depricated_rootFilter,
+ GeoRectangle rectangle, int zoomlevel,
+ SelectedItems selectedItems,
+ Uri gpxAdditionalPointsContentUri, boolean zoomToFit) {
+ String debugContext = mDebugPrefix + "defineNavigation: ";
if (Global.debugEnabled || Global.debugEnabledMap) {
- Log.i(Global.LOG_CONTEXT, mDebugPrefix + "defineNavigation: " + rectangle + ";z=" + zoomlevel);
- }
-
- if (rootFilter != null) {
- this.mRootFilter = rootFilter;
+ Log.i(Global.LOG_CONTEXT, debugContext + rectangle + ";z=" + zoomlevel);
}
- // load this.mFolderOverlayBlueGpxMarker
- GeoRectangle increasedRrectangle = defineGpxAdditionalPoints(gpxAdditionalPointsContentUri, rectangle);
+ this.mRootQuery = AndroidAlbumUtils.getAsMergedNewQueryParameter(rootQuery, depricated_rootFilter);
mSelectedItemsHandler.define(selectedItems);
+ if (zoomToFit) {
+ zoomToFit(null, "defineNavigation");
+ } else {
+ // load this.mFolderOverlayBlueGpxMarker
+ GeoRectangle increasedRrectangle = defineGpxAdditionalPoints(gpxAdditionalPointsContentUri, rectangle);
- String debugContext = "defineNavigation";
- zoomToBoundingBox(debugContext, increasedRrectangle, zoomlevel);
-
- if (rootFilter != null) {
+ zoomToBoundingBox(debugContext, increasedRrectangle, zoomlevel);
+ }
+ if ((rootQuery != null) || (depricated_rootFilter != null)) {
reloadFotoMarker(debugContext);
}
}
protected void zoomToBoundingBox(String debugContext, GeoRectangle rectangle, int zoomlevel) {
- if ((rectangle != null) && !rectangle.isEmpty()) {
- BoundingBox boundingBox = new BoundingBox(
- rectangle.getLatitudeMax(),
- rectangle.getLogituedMin(),
- rectangle.getLatitudeMin(),
- rectangle.getLogituedMax());
-
- zoomToBoundingBox(debugContext, boundingBox, zoomlevel);
+ if (rectangle != null) {
+ if (!rectangle.isEmpty()) {
+ BoundingBox boundingBox = new BoundingBox(
+ rectangle.getLatitudeMax(),
+ rectangle.getLogituedMin(),
+ rectangle.getLatitudeMin(),
+ rectangle.getLogituedMax());
+
+ zoomToBoundingBox(debugContext, boundingBox, zoomlevel);
+ } else {
+ // rectangle is single point (size=0)
+ GeoPoint point = new GeoPoint(rectangle.getLatitudeMin(),rectangle.getLogituedMin());
+ OsmdroidUtil.zoomTo(this.mMapView, zoomlevel,point , null);
+ }
}
}
@@ -533,6 +559,8 @@ public boolean onGeoInfo(IGeoPointInfo iGeoPointInfo) {
gpxBox.increase(Global.mapMultiselectionBoxIncreaseByProcent, Global.mapMultiselectionBoxIncreaseMinSizeInDegrees);
}
} catch (IOException e) {
+ Log.w(Global.LOG_CONTEXT, mDebugPrefix + "defineGpxAdditionalPoints cannot open points from " + gpxAdditionalPointsContentUri);
+
e.printStackTrace();
}
if (mFolderOverlayBlueGpxMarker != null) {
@@ -569,37 +597,51 @@ private Overlay createMarkerEx(GeoPointDtoEx point) {
return new MarkerEx(mPopup).set(mId++, position, mSelectedItemsHandler.mBlueMarker, position );
}
- private void zoomToBoundingBox(String why, BoundingBox boundingBox, int zoomLevel) {
+ private int zoomToBoundingBox(String why, BoundingBox boundingBox, int zoomLevel) {
+ int recalculatedZoom = NO_ZOOM;
if (boundingBox != null) {
+ final double oldZoomLevelDouble = (mMapView == null) ? -1 : mMapView.getZoomLevelDouble();
+ final BoundingBox oldBoundingBox = (mMapView == null) ? null : mMapView.getBoundingBox();
if (mIsInitialized) {
// if map is already initialized
GeoPoint min = new GeoPoint(boundingBox.getLatSouth(), boundingBox.getLonWest());
if (zoomLevel != NO_ZOOM) {
- OsmdroidUtil.zoomTo(this.mMapView, zoomLevel, min, null);
+ recalculatedZoom = OsmdroidUtil.zoomTo(this.mMapView, zoomLevel, min, null);
} else {
GeoPoint max = new GeoPoint(boundingBox.getLatNorth(), boundingBox.getLonEast());
- OsmdroidUtil.zoomTo(this.mMapView, OsmdroidUtil.NO_ZOOM, min, max);
+ recalculatedZoom = OsmdroidUtil.zoomTo(this.mMapView, OsmdroidUtil.NO_ZOOM, min, max);
// this.mMapView.zoomToBoundingBox(boundingBox); this is to inexact
}
if (Global.debugEnabledMap) {
Log.i(Global.LOG_CONTEXT, mDebugPrefix
+ "zoomToBoundingBox(" + why
- + "; z=" + mMapView.getZoomLevelDouble()
+ ") :"
- + boundingBox
+ + boundingBox + "; z=" + zoomLevel // oldZoomLevelDouble
+ " <= "
- + mMapView.getBoundingBox()
+ + oldBoundingBox+ "; z=" + oldZoomLevelDouble + "(" + recalculatedZoom + ")"
);
}
- setZoomBarZoomLevel(why, mMapView.getZoomLevelDouble());
+ setZoomBarZoomLevel(why, recalculatedZoom);
} else {
// map not initialized yet. do it later.
this.mDelayedZoomToBoundingBox = boundingBox;
+
this.mDelayedZoomLevel = zoomLevel;
+ if (Global.debugEnabledMap) {
+ Log.i(Global.LOG_CONTEXT, mDebugPrefix
+ + "zoomToBoundingBox-enque(" + why
+ + "; z=" + oldZoomLevelDouble
+ + ") :"
+ + boundingBox
+ + " <= "
+ + oldBoundingBox
+ );
+ }
}
}
+ return recalculatedZoom;
}
/** all marker clicks will be delegated to LocationMapFragment#onFotoMarkerClicked() */
@@ -650,8 +692,8 @@ private void reloadFotoMarker(BoundingBox latLonArea, double groupingFactor, Lis
QueryParameter query = FotoSql.getQueryGroupByPlace(groupingFactor);
query.clearWhere();
- if (this.mRootFilter != null) {
- TagSql.filter2QueryEx(query, this.mRootFilter, true);
+ if (this.mRootQuery != null) {
+ query.getWhereFrom(this.mRootQuery, true);
}
// delta: make the grouping area a little bit bigger than the viewport
@@ -985,14 +1027,14 @@ public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.cmd_photo:
- return showPhoto(getGeoPointById(markerId, geoPosition));
+ return showPhoto(getGeoPointById(markerId, geoPosition, "showPhoto"));
case R.id.cmd_gallery:
- return showGallery(getGeoPointById(markerId, geoPosition));
+ return showGallery(getGeoPointById(markerId, geoPosition,"showGallery"));
case R.id.cmd_zoom:
- return zoomToFit(getGeoPointById(markerId, geoPosition));
+ return zoomToFit(getGeoPointById(markerId, geoPosition,"on cmd zoomToFit"));
case R.id.cmd_show_geo_as: {
- IGeoPoint _geo = getGeoPointById(markerId, geoPosition);
+ IGeoPoint _geo = getGeoPointById(markerId, geoPosition,"cmd_show_geo_as");
GeoPointDto geo = new GeoPointDto(_geo.getLatitude(), _geo.getLongitude(), GeoPointDto.NO_ZOOM);
geo.setId(""+markerId);
geo.setName("#"+markerId);
@@ -1022,9 +1064,7 @@ protected void closePopup() {
}
private boolean showPhoto(IGeoPoint geoPosition) {
- GalleryFilterParameter filter = getMarkerFilter(geoPosition);
- QueryParameter query = new QueryParameter();
- TagSql.filter2QueryEx(query, filter, false);
+ QueryParameter query = getQueryForPositionRectangle(geoPosition);
FotoSql.setSort(query, FotoSql.SORT_BY_DATE, false);
ImageDetailActivityViewPager.showActivity(this.getActivity(), null, 0, query, 0);
@@ -1032,18 +1072,19 @@ private boolean showPhoto(IGeoPoint geoPosition) {
}
private boolean showGallery(IGeoPoint geoPosition) {
- GalleryFilterParameter filter = getMarkerFilter(geoPosition);
- FotoGalleryActivity.showActivity(this.getActivity(), filter, null, 0);
+ FotoGalleryActivity.showActivity(this.getActivity(), getQueryForPositionRectangle(geoPosition), 0);
return true;
}
- private boolean zoomToFit(IGeoPoint geoPosition) {
+ private boolean zoomToFit(IGeoPoint geoCenterPoint, Object... dbgContext) {
+ QueryParameter baseQuery = getQueryForPositionRectangle(geoCenterPoint);
BoundingBox BoundingBox = null;
- IGeoRectangle fittingRectangle = FotoSql.execGetGeoRectangle(this.getActivity(), getMarkerFilter(geoPosition), null);
+ IGeoRectangle fittingRectangle = FotoSql.execGetGeoRectangle(null, this.getActivity(),
+ baseQuery, null, mDebugPrefix, "zoomToFit", dbgContext);
double delta = getDelta(fittingRectangle);
- if (delta < 1e-6) {
- BoundingBox = getMarkerBoundingBox(geoPosition);
+ if ((geoCenterPoint != null) && (delta < 1e-6)) {
+ BoundingBox = getMarkerBoundingBox(geoCenterPoint);
} else {
double enlarge = delta * 0.2;
@@ -1069,10 +1110,18 @@ private double getDelta(IGeoRectangle fittingRectangle) {
, Math.abs(fittingRectangle.getLatitudeMax() - fittingRectangle.getLatitudeMin()));
}
- private GalleryFilterParameter getMarkerFilter(IGeoPoint geoPosition) {
+ private QueryParameter getQueryForPositionRectangle(IGeoPoint geoPosition) {
+ QueryParameter result = AndroidAlbumUtils.getAsMergedNewQueryParameter(mRootQuery, null);
+
+ GeoRectangle rect = getRectangleFrom(new GeoRectangle(), geoPosition);
+ FotoSql.addWhereFilterLatLon(result, rect);
+
+ return result;
+ }
+
+ private GeoRectangle getRectangleFrom(GeoRectangle result, IGeoPoint geoPosition) {
double delta = getMarkerDelta();
- GalleryFilterParameter result = new GalleryFilterParameter().get(mRootFilter);
result.setNonGeoOnly(false);
result.setLatitude(geoPosition.getLatitude() - delta, geoPosition.getLatitude() + delta);
result.setLogitude(geoPosition.getLongitude() - delta, geoPosition.getLongitude() + delta);
@@ -1097,9 +1146,10 @@ private double getMarkerDelta() {
return 1/groupingFactor/2;
}
- private IGeoPoint getGeoPointById(int markerId, IGeoPoint notFoundValue) {
+ private IGeoPoint getGeoPointById(int markerId, IGeoPoint notFoundValue, Object... dbgContext) {
if (markerId != NO_MARKER_ID) {
- IGeoPoint pos = FotoSql.execGetPosition(this.getActivity(), null, markerId);
+ IGeoPoint pos = FotoSql.execGetPosition(null, this.getActivity(),
+ null, markerId, mDebugPrefix, "getGeoPointById", dbgContext);
if (pos != null) {
return pos;
}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/MapGeoPickerActivity.java b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/MapGeoPickerActivity.java
index 6effeca2..545cc290 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/MapGeoPickerActivity.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/MapGeoPickerActivity.java
@@ -21,7 +21,6 @@
import android.app.ActionBar;
import android.app.Activity;
-import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
@@ -29,27 +28,32 @@
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.Window;
+import android.view.View;
import android.widget.Toast;
import org.osmdroid.api.IGeoPoint;
+import org.osmdroid.views.MapView;
import de.k3b.android.androFotoFinder.AffUtils;
-import de.k3b.android.androFotoFinder.BookmarkController;
import de.k3b.android.androFotoFinder.Common;
import de.k3b.android.androFotoFinder.FotoGalleryActivity;
-import de.k3b.android.androFotoFinder.GalleryFilterActivity;
import de.k3b.android.androFotoFinder.Global;
import de.k3b.android.androFotoFinder.LockScreen;
import de.k3b.android.androFotoFinder.R;
import de.k3b.android.androFotoFinder.SettingsActivity;
import de.k3b.android.androFotoFinder.imagedetail.ImageDetailActivityViewPager;
+import de.k3b.android.androFotoFinder.imagedetail.ImageDetailMetaDialogBuilder;
+import de.k3b.android.androFotoFinder.queries.AndroidAlbumUtils;
import de.k3b.android.androFotoFinder.queries.FotoSql;
+import de.k3b.android.androFotoFinder.tagDB.TagSql;
import de.k3b.android.osmdroid.OsmdroidUtil;
import de.k3b.android.widget.AboutDialogPreference;
-import de.k3b.android.widget.LocalizedActivity;
+import de.k3b.android.widget.BaseQueryActivity;
import de.k3b.database.QueryParameter;
+import de.k3b.io.IDirectory;
+import de.k3b.io.StringUtils;
import de.k3b.io.collections.SelectedFiles;
import de.k3b.io.collections.SelectedItems;
import de.k3b.geo.api.GeoPointDto;
@@ -59,7 +63,8 @@
import de.k3b.io.GeoRectangle;
import de.k3b.io.IGeoRectangle;
-public class MapGeoPickerActivity extends LocalizedActivity implements Common {
+// BaseQueryActivity LocalizedActivity
+public class MapGeoPickerActivity extends BaseQueryActivity implements Common {
private static final String mDebugPrefix = "GalM-";
private static final String STATE_Filter = "filterMap";
private static final String STATE_LAST_GEO = "geoLastView";
@@ -70,24 +75,23 @@ public class MapGeoPickerActivity extends LocalizedActivity implements Common {
private boolean mSaveLastUsedFilterToSharedPrefs = true;
private boolean mSaveLastUsedGeoToSharedPrefs = true;
- private GalleryFilterParameter mFilter;
private GeoUri mGeoUriParser = new GeoUri(GeoUri.OPT_PARSE_INFER_MISSING);
- private BookmarkController mBookmarkController = null;
+ // lockscreen support
+ private boolean locked = false; // if != Global.locked : must update menu
+ private boolean mMustReplaceMenue = false;
- public static void showActivity(Activity context, SelectedFiles selectedItems, GalleryFilterParameter filter) {
+ public static void showActivity(Activity context, SelectedFiles selectedItems,
+ QueryParameter query, int requestCode) {
Uri initalUri = null;
final Intent intent = new Intent().setClass(context,
MapGeoPickerActivity.class);
- GalleryFilterParameter localFilter = new GalleryFilterParameter();
-
- if (filter != null) {
- localFilter.get(filter);
- }
+ AndroidAlbumUtils.saveFilterAndQuery(context, null, intent, null, null, query);
if (AffUtils.putSelectedFiles(intent, selectedItems)) {
- IGeoPoint initialPoint = FotoSql.execGetPosition(context, null, selectedItems.getId(0));
+ IGeoPoint initialPoint = FotoSql.execGetPosition(null, context,
+ null, selectedItems.getId(0), context, mDebugPrefix, "showActivity");
if (initialPoint != null) {
GeoUri PARSER = new GeoUri(GeoUri.OPT_PARSE_INFER_MISSING);
@@ -96,15 +100,16 @@ public static void showActivity(Activity context, SelectedFiles selectedItems, G
}
}
- localFilter.setNonGeoOnly(false);
- intent.putExtra(EXTRA_FILTER, localFilter.toString());
-
intent.setAction(Intent.ACTION_VIEW);
if (Global.debugEnabled) {
Log.d(Global.LOG_CONTEXT, context.getClass().getSimpleName()
+ " > MapGeoPickerActivity.showActivity@" + initalUri);
}
- context.startActivity(intent);
+ if (requestCode != 0) {
+ context.startActivityForResult(intent, requestCode);
+ } else {
+ context.startActivity(intent);
+ }
}
@Override
@@ -117,14 +122,12 @@ protected void onCreate(Bundle savedInstanceState) {
Log.d(Global.LOG_CONTEXT, mDebugPrefix + "onCreate " + intent.toUri(Intent.URI_INTENT_SCHEME));
}
- mBookmarkController = new BookmarkController(this);
- mBookmarkController.loadState(intent,savedInstanceState);
-
- GeoPointDto geoPointFromIntent = getGeoPointDtoFromIntent(intent);
+ final GeoPointDto geoPointFromIntent = getGeoPointDtoFromIntent(intent);
// no geo: from intent: use last used value
mSaveLastUsedGeoToSharedPrefs = (geoPointFromIntent == null);
- Uri additionalPointsContentUri = ((intent != null) && (geoPointFromIntent == null)) ? intent.getData() : null;
+ final Uri uriFromIntent = intent.getData();
+ final Uri additionalPointsContentUri = ((intent != null) && (geoPointFromIntent == null)) ? uriFromIntent : null;
String lastGeoUri = sharedPref.getString(STATE_LAST_GEO, "geo:53,8?z=6");
IGeoPointInfo lastGeo = mGeoUriParser.fromUri(lastGeoUri);
@@ -137,23 +140,8 @@ protected void onCreate(Bundle savedInstanceState) {
IGeoPointInfo initalZoom = (mSaveLastUsedGeoToSharedPrefs) ? lastGeo : geoPointFromIntent;
- String extraTitle = intent.getStringExtra(EXTRA_TITLE);
- if (extraTitle == null && (geoPointFromIntent == null)) {
- extraTitle = getString(R.string.app_map_title);
- }
-
- if (extraTitle == null) {
- this.requestWindowFeature(Window.FEATURE_NO_TITLE);
- }
-
setContentView(R.layout.activity_map_geo_picker);
- if (extraTitle != null) {
- this.setTitle(extraTitle);
- } else {
- setNoTitle();
- }
-
mMap = (PickerLocationMapFragment) getFragmentManager().findFragmentById(R.id.fragment_map);
GeoRectangle rectangle = null;
@@ -165,81 +153,78 @@ protected void onCreate(Bundle savedInstanceState) {
rectangle.setLogituedMax(initalZoom.getLongitude()).setLatitudeMax(initalZoom.getLatitude());
} // else (savedInstanceState != null) restore after rotation. fragment takes care of restoring map pos
- SelectedItems selectedItems = AffUtils.getSelectedItems(intent);
+ final SelectedItems selectedItems = AffUtils.getSelectedItems(intent);
// TODO !!! #62 gpx/kml files: wie an LocatonMapFragment übergeben??
String filter = null;
// for debugging: where does the filter come from
String dbgFilter = null;
- if (intent != null) {
- filter = intent.getStringExtra(EXTRA_FILTER);
- }
- this.mSaveLastUsedFilterToSharedPrefs = (filter == null); // false if controlled via intent
- if (savedInstanceState != null) {
- filter = savedInstanceState.getString(STATE_Filter);
- if (filter != null) dbgFilter = "filter from savedInstanceState=" + filter;
- }
-
- if (this.mSaveLastUsedFilterToSharedPrefs) {
- filter = sharedPref.getString(STATE_Filter, null);
- if (filter != null) dbgFilter = "filter from sharedPref=" + filter;
- }
-
- this.mFilter = new GalleryFilterParameter();
- if (filter != null) {
- GalleryFilterParameter.parse(filter, this.mFilter);
+ final boolean zoom2fit = false;
+
+ onCreateData(savedInstanceState);
+
+ {
+ // bugfix: first defineNavigation will not work until map is created completely
+ // so wait until then
+ // note: delayed params must be final
+ final GeoRectangle _rectangle = rectangle;
+ final int _zoom = zoom;
+ mMap.mMapView.addOnFirstLayoutListener(new MapView.OnFirstLayoutListener() {
+ @Override
+ public void onFirstLayout(View v, int left, int top, int right, int bottom) {
+ mMap.defineNavigation(mGalleryQueryParameter.calculateEffectiveGalleryContentQuery(),
+ null, geoPointFromIntent, _rectangle, _zoom, selectedItems, additionalPointsContentUri, zoom2fit);
+ }
+ });
}
+ }
- if (Global.debugEnabled) {
- Log.i(Global.LOG_CONTEXT, mDebugPrefix + dbgFilter + " => " + this.mFilter);
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // if lock mode has changed redefine menu
+ final boolean locked = LockScreen.isLocked(this);
+ if (this.locked != locked) {
+ this.locked = locked;
+ mMustReplaceMenue = true;
+ invalidateOptionsMenu();
}
-
- mMap.defineNavigation(this.mFilter, geoPointFromIntent, rectangle, zoom, selectedItems, additionalPointsContentUri);
}
- @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(LockScreen.isLocked(this) ? R.menu.menu_map_context_locked : R.menu.menu_map_geo_picker, menu);
- AboutDialogPreference.onPrepareOptionsMenu(this, menu);
-
+ mMustReplaceMenue = true;
+ fixOptionsMenu(menu);
return true;
}
- @Override
- protected void onDestroy() {
- saveSettings(this);
- super.onDestroy();
- }
-
- @Override
- public void onSaveInstanceState(Bundle savedInstanceState) {
- super.onSaveInstanceState(savedInstanceState);
- saveSettings(this);
- mBookmarkController.saveState(null, savedInstanceState);
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
- if ((savedInstanceState != null) && (this.mFilter != null)) {
- savedInstanceState.putString(STATE_Filter, this.mFilter.toString());
- }
+ // if lock mode has changed redefine menu
+ fixOptionsMenu(menu);
+ return true;
}
- private void saveSettings(Context context) {
- if (mSaveLastUsedFilterToSharedPrefs || mSaveLastUsedGeoToSharedPrefs) {
- // save settings
- SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
- SharedPreferences.Editor edit = sharedPref.edit();
+ private void fixOptionsMenu(Menu menu) {
+ final boolean locked = LockScreen.isLocked(this);
+ if (mMustReplaceMenue || (locked != this.locked)) {
+ mMustReplaceMenue = false;
+ this.locked = locked;
+ menu.clear();
+ MenuInflater inflater = getMenuInflater();
- if (mSaveLastUsedFilterToSharedPrefs && (mFilter != null)) {
- edit.putString(STATE_Filter, mFilter.toString());
- }
+ if (locked) {
+ inflater.inflate(R.menu.menu_locked, menu);
+ LockScreen.removeDangerousCommandsFromMenu(menu);
+ } else {
+ inflater.inflate(R.menu.menu_map_geo_picker, menu);
- if (mSaveLastUsedGeoToSharedPrefs) {
- String currentGeoUri = mMap.getCurrentGeoUri();
- edit.putString(STATE_LAST_GEO, currentGeoUri);
}
-
- edit.apply();
+ AboutDialogPreference.onPrepareOptionsMenu(this, menu);
}
}
@@ -249,9 +234,6 @@ public boolean onOptionsItemSelected(MenuItem item) {
return true;
// Handle presses on the action bar items
switch (item.getItemId()) {
- case R.id.cmd_filter:
- openFilter();
- return true;
//cmd_lock
case R.id.cmd_about:
AboutDialogPreference.createAboutDialog(this).show();
@@ -263,8 +245,11 @@ public boolean onOptionsItemSelected(MenuItem item) {
return showPhoto(mMap.getCurrentGeoRectangle());
case R.id.cmd_gallery:
return showGallery(mMap.getCurrentGeoRectangle());
+ case R.id.action_details:
+ cmdShowDetails();
+ return true;
default:
- return super.onOptionsItemSelected(item);
+ return onOptionsItemSelected(item, AffUtils.getSelectedItems(getIntent()));
}
}
@@ -279,48 +264,59 @@ private boolean showPhoto(IGeoRectangle geoArea) {
}
private boolean showGallery(IGeoRectangle geoArea) {
+ QueryParameter query = getAsMergedQuery(geoArea);
+
+ FotoGalleryActivity.showActivity(this, query, 0);
+ return true;
+ }
+
+ private QueryParameter getAsMergedQuery() {
+ return getAsMergedQuery(mMap.getCurrentGeoRectangle());
+ }
+
+ private QueryParameter getAsMergedQuery(IGeoRectangle geoArea) {
GalleryFilterParameter filter = new GalleryFilterParameter();
filter.get(geoArea);
- FotoGalleryActivity.showActivity(this, filter, null, 0);
- return true;
+ return TagSql.filter2NewQuery(filter);
+ }
+
+ private void cmdShowDetails() {
+ final QueryParameter asMergedQuery = getAsMergedQuery();
+
+ CharSequence subQuerymTitle = getValueAsTitle(true);
+ ImageDetailMetaDialogBuilder.createImageDetailDialog(
+ this,
+ getTitle().toString(),
+ asMergedQuery.toSqlString(),
+ StringUtils.appendMessage(null,
+ getString(R.string.show_photo),
+ TagSql.getCount(this, asMergedQuery)),
+ subQuerymTitle
+ ).show();
}
-
- /**
- * Call back from sub-activities.
- * Process Change StartTime (longpress start), Select StopTime before stop
- * (longpress stop) or filter change for detailReport
- */
@Override
- protected void onActivityResult(final int requestCode,
- final int resultCode, final Intent intent) {
- super.onActivityResult(requestCode, resultCode, intent);
-
- switch (requestCode) {
- case GalleryFilterActivity.resultID :
- mBookmarkController.loadState(intent, null);
- onFilterChanged(GalleryFilterActivity.getFilter(intent));
- break;
- default:
- throw new IllegalStateException();
+ protected void reloadGui(String why) {
+ if (mMap != null) {
+ QueryParameter query = this.mGalleryQueryParameter.calculateEffectiveGalleryContentQuery();
+ mMap.defineNavigation(query,
+ null,null, IGeoPointInfo.NO_ZOOM, null, null, false);
}
+
}
- private void onFilterChanged(GalleryFilterParameter filter) {
- if ((mMap != null) && (filter != null)) {
- this.mFilter = filter;
- mMap.defineNavigation(this.mFilter, null, OsmdroidUtil.NO_ZOOM, null, null);
- }
+ @Override
+ protected void defineDirectoryNavigation(IDirectory directoryRoot) {
}
- private void openFilter() {
- GalleryFilterActivity.showActivity(this, this.mFilter, null,
- mBookmarkController.getlastBookmarkFileName(), GalleryFilterActivity.resultID);
+ /** allows childclass to have their own sharedPreference names */
+ @Override
+ protected String fixSharedPrefSuffix(String statSuffix) {
+ return super.fixSharedPrefSuffix(statSuffix) + "-map";
}
private GeoPointDto getGeoPointDtoFromIntent(Intent intent) {
- final Uri uri = (intent != null) ? intent.getData() : null;
- String uriAsString = (uri != null) ? uri.toString() : null;
+ String uriAsString = (intent != null) ? intent.getDataString() : null;
GeoPointDto pointFromIntent = null;
if (uriAsString != null) {
Toast.makeText(this, getString(R.string.app_name) + ": received " + uriAsString, Toast.LENGTH_LONG).show();
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/PickerLocationMapFragment.java b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/PickerLocationMapFragment.java
index 3c8fa48a..145ce804 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/PickerLocationMapFragment.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/PickerLocationMapFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder.
*
@@ -41,6 +41,7 @@
import de.k3b.android.androFotoFinder.queries.FotoSql;
import de.k3b.android.osmdroid.IconOverlay;
import de.k3b.android.util.ResourceUtils;
+import de.k3b.database.QueryParameter;
import de.k3b.io.collections.SelectedItems;
import de.k3b.geo.api.GeoPointDto;
import de.k3b.geo.api.IGeoPointInfo;
@@ -129,9 +130,18 @@ protected void definteOverlays(MapView mapView) {
}
/** get all important parameters for displaying the map */
- public void defineNavigation(GalleryFilterParameter rootFilter, IGeoPointInfo selectedItem,
+ /**
+ * (re)define map display
+ * @param rootQuery if not null contain database where to limit the photo data displayed
+ * @param depricated_rootFilter should be null. if not null contain database where to limit the data displayed
+ * @param rectangle if nut null the initial visible rectange
+ * @param zoomlevel the initial zoomlevel
+ * @param selectedItems if not null: items to be displayed as blue markers
+ * @param zoomToFit true mean recalculate zoomlevel from rectangle
+ */
+ public void defineNavigation(QueryParameter rootQuery, GalleryFilterParameter depricated_rootFilter, IGeoPointInfo selectedItem,
GeoRectangle rectangle, int zoomlevel,
- SelectedItems selectedItems, Uri additionalPointsContentUri) {
+ SelectedItems selectedItems, Uri additionalPointsContentUri, boolean zoomToFit) {
IGeoPointInfo currentSelection = selectedItem;
if ((currentSelection == null) && (getCurrentSelectionPosition() == null)) {
// first call with no geo: take last use from config
@@ -141,7 +151,7 @@ public void defineNavigation(GalleryFilterParameter rootFilter, IGeoPointInfo se
currentSelection = (lastValue == null) ? null : mGeoUriEngine.fromUri(lastValue);
}
- super.defineNavigation(rootFilter, rectangle, zoomlevel, selectedItems, additionalPointsContentUri);
+ super.defineNavigation(rootQuery, depricated_rootFilter, rectangle, zoomlevel, selectedItems, additionalPointsContentUri, zoomToFit);
if (currentSelection != null) {
updateMarker(null, NO_MARKER_ID, new GeoPoint(currentSelection.getLatitude(), currentSelection.getLongitude()), null);
}
@@ -160,7 +170,8 @@ protected void onOk() {
Activity activity = getActivity();
if (mMarkerId != NO_MARKER_ID) {
- result = FotoSql.execGetPosition(activity, null, mMarkerId);
+ result = FotoSql.execGetPosition(null, activity, null, mMarkerId,
+ mDebugPrefix,"onOk");
}
if (result == null) {
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/media/package-info.java b/app/src/main/java/de/k3b/android/androFotoFinder/media/package-info.java
index 7b6030ee..68dc29da 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/media/package-info.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/media/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 by k3b.
+ * Copyright (c) 2017-2018 by k3b.
*
* This file is part of AndroFotoFinder / #APhotoManager.
*
@@ -113,7 +113,68 @@
[TODO:syncAndroid]-->[TODO:syncPC] : {TODO:z}
@enduml
+camera-worflow-simple.png
+@startuml
+ title Workflow Photo management Android-PC (simple)
+ package "PC" {
+ [Folder pc-in "from-android"]
+ [DigiKam]
+ }
+
+ package "Android" {
+ [Camera]
+ [Folder "camera"]
+ [Folder android-out "to-pc"]
+ ["A Photo Manager"]
+ }
+ [Camera] --> [Folder "camera"] #blue : {1-take}
+ [Folder "camera"] <-- ["A Photo Manager"] #blue : {2-move}
+ [Folder "camera"] --> [Folder android-out "to-pc"] : {2-Autoprocessing:\nRename+Exif}
+ ["A Photo Manager"] --> [Folder android-out "to-pc"] #blue : {3-process}
+
+ [Folder android-out "to-pc"] <--> [Folder pc-in "from-android"] : {4-Syncthing}
+ [Folder pc-in "from-android"] <-- [DigiKam] #blue : {5-process}
+
+@enduml
+
+
+camera-worflow-huge.png
+@startuml
+ title Workflow Photo management Android-PC (huge)
+ package "PC" {
+ [Folder pc-in "from-android"]
+ [Folder pc "hires"]
+ [Folder pc-out "lowres"]
+ [DigiKam]
+ [IrfanView]
+ }
+
+ package "Android" {
+ [Camera]
+ [Folder "camera"]
+ [Folder android-out "to-pc"]
+ [Folder android-in "from-pc"]
+ ["A Photo Manager"]
+ }
+
+ [Camera] --> [Folder "camera"] #blue : {1-take}
+ [Folder "camera"] <-- ["A Photo Manager"] #blue : {2-move}
+ [Folder "camera"] --> [Folder android-out "to-pc"] : {2-Autoprocessing:\nRename+Exif}
+
+ ["A Photo Manager"] --> [Folder android-out "to-pc"] #blue : {3-process}
+
+ [Folder android-out "to-pc"] --> [Folder pc-in "from-android"] : {4-Syncthing}
+ [Folder pc-in "from-android"] <-- [DigiKam] #blue : {5-process\nmove}
+ [Folder pc "hires"] <- [Folder pc-in "from-android"] : {5}
+ [Folder pc "hires"] <-- [DigiKam] #blue : {6-process}
+ [Folder pc "hires"] <-- [IrfanView] #blue : {7-resize}
+ [Folder pc-out "lowres"] <- [Folder pc "hires"] : {7}
+
+ [Folder android-in "from-pc"] <-- [Folder pc-out "lowres"] : {8b-Syncthing}
+
+ ["A Photo Manager"] --> [Folder android-in "from-pc"] #blue : {9-process}
+@enduml
*/
\ No newline at end of file
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/AndroidAlbumUtils.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/AndroidAlbumUtils.java
new file mode 100644
index 00000000..345b3c5a
--- /dev/null
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/AndroidAlbumUtils.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2018 by k3b.
+ *
+ * This file is part of AndroFotoFinder / #APhotoManager.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see
+ */
+
+package de.k3b.android.androFotoFinder.queries;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import de.k3b.android.androFotoFinder.Common;
+import de.k3b.android.androFotoFinder.GalleryFilterPathState;
+import de.k3b.android.androFotoFinder.Global;
+import de.k3b.android.androFotoFinder.R;
+import de.k3b.android.androFotoFinder.tagDB.TagSql;
+import de.k3b.android.util.IntentUtil;
+import de.k3b.android.util.MediaScanner;
+import de.k3b.database.QueryParameter;
+import de.k3b.io.AlbumFile;
+import de.k3b.io.FileUtils;
+import de.k3b.io.GalleryFilterParameter;
+import de.k3b.io.IGalleryFilter;
+import de.k3b.io.StringUtils;
+import de.k3b.tagDB.TagProcessor;
+
+/**
+ * Handles file io of QueryParameter and GalleryFilterParameter via intent.
+ *
+ * Created by k3b on 28.03.2018.
+ */
+public class AndroidAlbumUtils implements Common {
+ private static final String mDebugPrefix = AndroidAlbumUtils.class.getSimpleName();
+
+ /** get from savedInstanceState else intent: inten.data=file-uri else EXTRA_QUERY else EXTRA_FILTER */
+ @Deprecated
+ public static GalleryFilterParameter getFilterAndRestQuery(
+ @NonNull Context context, @Nullable Bundle savedInstanceState,
+ @Nullable Intent intent, @Nullable QueryParameter resultQueryWithoutFilter,
+ boolean ignoreFilter, @Nullable StringBuilder dbgFilter) {
+ if (savedInstanceState != null) {
+ return getFilterAndRestQuery(
+ context,
+ null,
+ savedInstanceState.getString(EXTRA_QUERY),
+ savedInstanceState.getString(EXTRA_FILTER),
+ resultQueryWithoutFilter, ignoreFilter, dbgFilter);
+ } else if (intent != null) {
+ return getFilterAndRestQuery(
+ context,
+ IntentUtil.getUri(intent),
+ intent.getStringExtra(EXTRA_QUERY),
+ intent.getStringExtra(EXTRA_FILTER),
+ resultQueryWithoutFilter, ignoreFilter, dbgFilter);
+ }
+ return null;
+ }
+
+ /** get from savedInstanceState else intent else sharedPref: inten.data=file-uri else EXTRA_QUERY else EXTRA_FILTER */
+ public static QueryParameter getQuery(
+ @NonNull Context context, @NonNull String paramNameSuffix,
+ @Nullable Bundle savedInstanceState,
+ @Nullable Intent intent, @Nullable SharedPreferences sharedPref,
+ StringBuilder outQueryFileUri, @Nullable StringBuilder dbgMessageResult) {
+ QueryParameter result = null;
+
+ if (savedInstanceState == null) {
+ // onCreate (first call) : if intent is usefull use it else use sharedPref
+ if (intent != null) {
+ Uri uri = IntentUtil.getUri(intent);
+ result = getQueryFromFirstMatching(
+ "Intent-uri", context,
+ uri,
+ null,
+ null,
+ dbgMessageResult);
+ if (result == null) {
+ result = getQueryFromFirstMatching(
+ "Intent", context,
+ uri,
+ intent.getStringExtra(EXTRA_QUERY),
+ intent.getStringExtra(EXTRA_FILTER),
+ dbgMessageResult);
+ } else if (outQueryFileUri != null) {
+ outQueryFileUri.append(uri.toString());
+ }
+ }
+
+ if ((result == null) && (sharedPref != null)) {
+ result = getQueryFromFirstMatching(
+ "SharedPreferences", context,
+ null,
+ sharedPref.getString(EXTRA_QUERY + paramNameSuffix, null),
+ sharedPref.getString(EXTRA_FILTER + paramNameSuffix, null),
+ dbgMessageResult);
+ }
+ } else {
+ // (savedInstanceState != null) : onCreate after screen rotation
+ result = getQueryFromFirstMatching(
+ "InstanceState", context,
+ null,
+ savedInstanceState.getString(EXTRA_QUERY + paramNameSuffix),
+ savedInstanceState.getString(EXTRA_FILTER + paramNameSuffix),
+ dbgMessageResult);
+ }
+ return result;
+ }
+
+ /** used by folder picker if album-file is picked instead of folder */
+ public static QueryParameter getQueryFromUri(String dbgContext, @NonNull Context context, Uri uri, @Nullable StringBuilder dbgFilter) {
+ // ignore geo: or area: uri-s
+ String path = (IntentUtil.isFileOrContentUri(uri, true)) ? uri.getPath() : null;
+ if (!StringUtils.isNullOrEmpty(path)) {
+ // #118 app specific content uri convert
+ // from {content://approvider}//storage/emulated/0/DCIM/... to /storage/emulated/0/DCIM/
+ path = FileUtils.fixPath(path);
+ if (dbgFilter != null) {
+ dbgFilter.append(dbgContext).append("\n");
+ } else if (Global.debugEnabled) {
+ Log.d(Global.LOG_CONTEXT, dbgContext);
+ }
+
+ if ((context != null) && AlbumFile.isQueryFile(path)) {
+ try {
+ QueryParameter query = QueryParameter.load(context.getContentResolver().openInputStream(uri));
+ if (query != null) {
+ Map found = FotoSql.execGetPathIdMap(context, path);
+ if ((found == null) || (found.size() == 0)) {
+ AndroidAlbumUtils.albumMediaScan(dbgContext + " not found mediadb => ", context, new File(path), 1);
+ }
+
+ if (dbgFilter != null) {
+ dbgFilter.append("query album from uri ").append(uri).append("\n")
+ .append(query).append("\n");
+ }
+ return query;
+ }
+
+ // not found in filesystem.
+ AndroidAlbumUtils.albumMediaScan(dbgContext + " not found in filesystem => ", context, new File(path), 1);
+ } catch (IOException e) {
+ Log.e(Global.LOG_CONTEXT, mDebugPrefix + ".loadFrom(" + uri +
+ ") failed: " + e.getMessage(), e);
+ if (dbgFilter != null) {
+ dbgFilter.append("query album from uri '").append(uri).append("' failed:")
+ .append(e.getMessage()).append("\n");
+ }
+ }
+ }
+
+ // replace file wildcards with sql wildcards
+ path = path.replace("*","%");
+
+ // non full-path-query becomes contains-path-query
+ if (!path.startsWith("/")) {
+ path = "%" + path;
+ if (!path.endsWith("%")) {
+ path += "%";
+ }
+ }
+ if (path.length() > 2) {
+ if (dbgFilter != null) {
+ dbgFilter.append("path query from uri '").append(uri).append("' : '")
+ .append(path).append("'\n");
+ }
+ GalleryFilterParameter filter = new GalleryFilterParameter().setPath(path);
+ return TagSql.filter2NewQuery(filter);
+ }
+ }
+ return null;
+ }
+
+ @Deprecated
+ public static GalleryFilterParameter getGalleryFilterAndRestQueryFromQueryUri(
+ @NonNull Context context, Uri uri, QueryParameter resultQueryWithoutFilter,
+ boolean ignoreFilter, @Nullable StringBuilder dbgFilter) {
+ QueryParameter query = getQueryFromUri(mDebugPrefix, context, uri, null);
+ if (query != null) {
+ if ((uri != null) && (dbgFilter != null)) {
+ dbgFilter.append("query from uri ").append(uri).append("\n");
+ }
+ return getFilterAndRestQuery(query, resultQueryWithoutFilter, ignoreFilter, dbgFilter);
+ }
+ return null;
+ }
+
+ @Deprecated
+ private static GalleryFilterParameter getFilterAndRestQuery(
+ @NonNull Context context, Uri uri, String sql, String filter, QueryParameter resultQueryWithoutFilter,
+ boolean ignoreFilter, @Nullable StringBuilder dbgFilter) {
+ if (uri != null) {
+ return getGalleryFilterAndRestQueryFromQueryUri(context, uri, resultQueryWithoutFilter, ignoreFilter, dbgFilter);
+ }
+
+ if ((sql != null) && (dbgFilter != null)) {
+ dbgFilter.append("query from ").append(EXTRA_QUERY).append("\n\t").append(sql).append("\n");
+ }
+ QueryParameter query = QueryParameter.parse(sql);
+ final GalleryFilterParameter result = getFilterAndRestQuery(query, resultQueryWithoutFilter, ignoreFilter, dbgFilter);
+
+ if (filter == null) {
+ // no own value for filter return parsed filter
+ return result;
+ }
+
+ if (filter != null) {
+ if (dbgFilter != null) {
+ dbgFilter.append("filter from ").append(EXTRA_FILTER).append("=").append(filter).append("\n");
+ }
+ return GalleryFilterParameter.parse(filter, new GalleryFilterParameter());
+ }
+ return null;
+ }
+
+ /**
+ * @param dbgMessagePrefix
+ * @param context
+ * @param uri if not null and exist: load from uri
+ * @param sql else if not null: parse query from this sql
+ * @param filter else if not null parse query from filter
+ */
+ private static QueryParameter getQueryFromFirstMatching(
+ String dbgMessagePrefix, @NonNull Context context, @Nullable Uri uri, @Nullable String sql, @Nullable String filter,
+ @Nullable StringBuilder dbgMessageResult) {
+ if (uri != null) {
+ QueryParameter query = getQueryFromUri(dbgMessagePrefix, context, uri, dbgMessageResult);
+ if (query != null) {
+ return query;
+ }
+ }
+
+ if (sql != null) {
+ if (dbgMessageResult != null) {
+ dbgMessageResult.append(dbgMessagePrefix).append(" query from ").append(EXTRA_QUERY).append("\n\t").append(sql).append("\n");
+ }
+ return QueryParameter.parse(sql);
+ }
+
+ if (filter != null) {
+ if (dbgMessageResult != null) {
+ dbgMessageResult.append(dbgMessagePrefix).append(" filter from ").append(EXTRA_FILTER).append("=").append(filter).append("\n");
+ }
+ return TagSql.filter2NewQuery(GalleryFilterParameter.parse(filter, new GalleryFilterParameter()));
+ }
+ return null;
+ }
+
+ @Deprecated
+ private static GalleryFilterParameter getFilterAndRestQuery(
+ QueryParameter query, QueryParameter resultQueryWithoutFilter,
+ boolean ignoreFilter, @Nullable StringBuilder dbgFilter) {
+
+ final GalleryFilterParameter result = (ignoreFilter) ? null : (GalleryFilterParameter) TagSql.parseQueryEx(query, true);
+
+ if (resultQueryWithoutFilter != null) {
+ resultQueryWithoutFilter.clear();
+ resultQueryWithoutFilter.getFrom(query);
+ }
+ return result;
+ }
+
+ /**
+ *
+ * @param context
+ * @param destUri if not null: merged-query-filter will be saved to this uri/file with update mediaDB
+ * @param destIntent if not null: merged-query-filter will be saved to this intent
+ * @param destBundle if not null: merged-query-filter will be saved to this intent
+ * @param srcFilter if not null filter will be appended to query parameter
+ * @param srcQueryParameter
+ */
+ public static void saveFilterAndQuery(
+ @NonNull Context context, Uri destUri, Intent destIntent, Bundle destBundle,
+ final IGalleryFilter srcFilter, final QueryParameter srcQueryParameter) {
+ final String dbgContext = mDebugPrefix + ".saveFilterAndQuery(" + destUri + ")";
+ final QueryParameter mergedQuery = getAsMergedNewQueryParameter(srcQueryParameter, srcFilter);
+
+ if (destUri != null) {
+ PrintWriter out = null;
+ try {
+ mergedQuery.save(context.getContentResolver().openOutputStream(destUri, "w"));
+ insertToMediaDB(dbgContext, context, destUri);
+ } catch (IOException e) {
+ Log.e(Global.LOG_CONTEXT, dbgContext +
+ " failed: " + e.getMessage(), e);
+ } finally {
+ FileUtils.close(out, "");
+ }
+ if (destIntent != null) destIntent.setData(destUri);
+ }
+
+ if (destBundle != null) {
+ if (srcQueryParameter != null)
+ destBundle.putString(EXTRA_QUERY, mergedQuery.toReParseableString());
+ else if (srcFilter != null)
+ destBundle.putString(EXTRA_FILTER, srcFilter.toString());
+ }
+ if (destIntent != null) {
+ if (srcQueryParameter != null)
+ destIntent.putExtra(EXTRA_QUERY, mergedQuery.toReParseableString());
+ else if (srcFilter != null)
+ destIntent.putExtra(EXTRA_FILTER, srcFilter.toString());
+ }
+ }
+
+ /** create a copy of srcQueryParameter and add srcFilter */
+ public static QueryParameter getAsMergedNewQueryParameter(QueryParameter srcQueryParameter, IGalleryFilter srcFilter) {
+ final QueryParameter mergedQuery = new QueryParameter(srcQueryParameter);
+ TagSql.filter2QueryEx(mergedQuery, srcFilter, false);
+ return mergedQuery;
+ }
+
+ public static void insertToMediaDB(String dbgContext, @NonNull Context context, Uri uri) {
+ if (IntentUtil.isFileUri(uri)) {
+ File f = FileUtils.tryGetCanonicalFile(IntentUtil.getFile(uri));
+ insertToMediaDB(dbgContext, context, f);
+ }
+ }
+
+ public static void insertToMediaDB(String dbgContext, @NonNull Context context, File fileToBeScannedAndInserted) {
+ if (fileToBeScannedAndInserted != null) {
+ ContentValues values = new ContentValues();
+ String newAbsolutePath = MediaScanner.setFileFields(values, fileToBeScannedAndInserted);
+ values.put(FotoSql.SQL_COL_EXT_MEDIA_TYPE, FotoSql.MEDIA_TYPE_ALBUM_FILE);
+ FotoSql.insertOrUpdateMediaDatabase(dbgContext, context, newAbsolutePath, values, null, 1l);
+ }
+ }
+
+ // !!! todo einbauen in filter-edit if item not found in db and/or filesystem und in media scanner
+ /** return the number of modified(added+deleted) items */
+ public static int albumMediaScan(String dbgContext, Context context, File _root, int subDirLevels) {
+ String dbgMessage = dbgContext + ": albumMediaScan(" + _root + "): ";
+
+ File root = FileUtils.getFirstExistingDir(_root);
+ int result = 0;
+ if (root != null) {
+ List currentFiles = AlbumFile.getFilePaths(null, root, subDirLevels);
+ List databaseFiles = FotoSql.getAlbumFiles(context, FileUtils.tryGetCanonicalPath(root, null), subDirLevels);
+
+ List added = new ArrayList();
+ List removed = new ArrayList();
+ TagProcessor.getDiff(databaseFiles, currentFiles, added, removed);
+
+ for (String insert : added) {
+ insertToMediaDB(dbgMessage + "add-new", context, new File(insert));
+ result++;
+ }
+
+ result += FotoSql.deleteMedia(dbgMessage + "delete-obsolete", context, removed, false);
+ }
+ return result;
+ }
+
+ public static void saveAs(Context context, File outFile, final QueryParameter currentFilter) {
+ if (Global.debugEnabled) {
+ Log.d(Global.LOG_CONTEXT, "onSaveAs(" + outFile.getAbsolutePath() + ")");
+ }
+ PrintWriter out = null;
+ try {
+ out = new PrintWriter(outFile);
+ out.println(currentFilter.toReParseableString());
+ out.close();
+ out = null;
+
+ AndroidAlbumUtils.insertToMediaDB(
+ ".saveAlbumAs",
+ context,
+ outFile);
+ GalleryFilterPathState.saveAsPreference(context, Uri.fromFile(outFile), null);
+ } catch (IOException err) {
+ String errorMessage = context.getString(R.string.mk_err_failed_format, outFile.getAbsoluteFile());
+ Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show();
+ Log.e(Global.LOG_CONTEXT, errorMessage, err);
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoSql.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoSql.java
index e569114f..0e8281d3 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoSql.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoSql.java
@@ -47,6 +47,9 @@
import de.k3b.android.androFotoFinder.R;
import de.k3b.android.util.DBUtils;
import de.k3b.database.QueryParameter;
+import de.k3b.io.AlbumFile;
+import de.k3b.io.ListUtils;
+import de.k3b.io.StringUtils;
import de.k3b.io.VISIBILITY;
import de.k3b.io.collections.SelectedFiles;
import de.k3b.io.collections.SelectedItems;
@@ -133,8 +136,9 @@ public class FotoSql extends FotoSqlBase {
public static final String SQL_COL_EXT_MEDIA_TYPE = MediaStore.Files.FileColumns.MEDIA_TYPE;
- public static final int MEDIA_TYPE_IMAGE = MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE; // 1
+ public static final int MEDIA_TYPE_IMAGE = MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE; // 1
public static final int MEDIA_TYPE_IMAGE_PRIVATE = 1000 + MEDIA_TYPE_IMAGE; // 1001 APhoto manager specific
+ public static final int MEDIA_TYPE_ALBUM_FILE = 0;
protected static final String FILTER_EXPR_PRIVATE
= "(" + SQL_COL_EXT_MEDIA_TYPE + " = " + MEDIA_TYPE_IMAGE_PRIVATE + ")";
@@ -185,6 +189,17 @@ public class FotoSql extends FotoSqlBase {
.addGroupBy(SQL_EXPR_FOLDER)
.addOrderBy(SQL_EXPR_FOLDER);
+ public static final QueryParameter queryVAlbum = new QueryParameter()
+ .setID(QUERY_TYPE_GROUP_ALBUM)
+ .addColumn(
+ SQL_COL_PK,
+ SQL_COL_PATH + " AS " + SQL_COL_DISPLAY_TEXT,
+ "0 AS " + SQL_COL_COUNT,
+ "null AS " + SQL_COL_GPS)
+ .addFrom(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME)
+ .addWhere(SQL_COL_PATH + " like '%" + AlbumFile.SUFFIX_VALBUM + "'")
+ .addOrderBy(SQL_COL_PATH);
+
/* image entries may become duplicated if media scanner finds new images that have not been inserted into media database yet
* and aFotoSql tries to show the new image and triggers a filescan. */
public static final QueryParameter queryGetDuplicates = new QueryParameter()
@@ -307,49 +322,88 @@ public static void filter2Query(QueryParameter resultQuery, IGalleryFilter filte
resultQuery.clearWhere();
}
- if (filter.isNonGeoOnly()) {
- resultQuery.addWhere(FILTER_EXPR_NO_GPS);
- } else {
- addWhereFilterLatLon(resultQuery, filter);
- }
+ addWhereFilterLatLon(resultQuery, filter);
- if (filter.getDateMin() != 0) resultQuery.addWhere(FILTER_EXPR_DATE_MIN, Long.toString(filter.getDateMin()));
- if (filter.getDateMax() != 0) resultQuery.addWhere(FILTER_EXPR_DATE_MAX, Long.toString(filter.getDateMax()));
+ addWhereDateMinMax(resultQuery, filter.getDateMin(), filter.getDateMax());
String path = filter.getPath();
if ((path != null) && (path.length() > 0)) resultQuery.addWhere(FILTER_EXPR_PATH_LIKE, path);
}
}
+ public static void addWhereDateMinMax(QueryParameter resultQuery, final long dateMin, final long dateMax) {
+
+ if (dateMin != 0) resultQuery.addWhere(FILTER_EXPR_DATE_MIN, Long.toString(dateMin));
+
+ if (dateMax != 0) resultQuery.addWhere(FILTER_EXPR_DATE_MAX, Long.toString(dateMax));
+ }
+
/** translates a query back to filter */
- public static IGalleryFilter parseQuery(QueryParameter query, boolean remove) {
+ public static IGalleryFilter parseQuery(QueryParameter query, boolean removeFromSourceQuery) {
if (query != null) {
GalleryFilterParameter filter = new GalleryFilterParameter();
- if (null != getParams(query, FILTER_EXPR_NO_GPS, remove)) {
- filter.setNonGeoOnly(true);
- } else {
- filter.setLogitude(getParam(query, FILTER_EXPR_LON_MIN, remove), getParam(query, FILTER_EXPR_LON_MAX, remove));
- filter.setLatitude(getParam(query, FILTER_EXPR_LAT_MIN, remove), getParam(query, FILTER_EXPR_LAT_MAX, remove));
- }
+ parseQueryGeo(query, filter, removeFromSourceQuery);
- filter.setRatingMin(GalleryFilterParameter.parseRating(getParam(query, FILTER_EXPR_RATING_MIN, remove)));
- filter.setDate(getParam(query, FILTER_EXPR_DATE_MIN, remove), getParam(query, FILTER_EXPR_DATE_MAX, remove));
- filter.setPath(getParam(query, FILTER_EXPR_PATH_LIKE, remove));
+ filter.setRatingMin(GalleryFilterParameter.parseRating(getParam(query, FILTER_EXPR_RATING_MIN, removeFromSourceQuery)));
+ filter.setDate(getParam(query, FILTER_EXPR_DATE_MIN, removeFromSourceQuery), getParam(query, FILTER_EXPR_DATE_MAX, removeFromSourceQuery));
+ filter.setPath(getFilePath(query, removeFromSourceQuery));
return filter;
}
return null;
}
+ /** extracts geo infos from srcQuery to destFilter */
+ public static GeoRectangle parseQueryGeo(QueryParameter srcQuery, GeoRectangle destFilter, boolean removeFromSourceQuery) {
+ if (null != getParams(srcQuery, FILTER_EXPR_NO_GPS, removeFromSourceQuery)) {
+ if (destFilter != null) destFilter.setNonGeoOnly(true);
+ } else {
+ final String lonMin = getParam(srcQuery, FILTER_EXPR_LON_MIN, removeFromSourceQuery);
+ final String lonMax = getParam(srcQuery, FILTER_EXPR_LON_MAX, removeFromSourceQuery);
+ final String latMin = getParam(srcQuery, FILTER_EXPR_LAT_MIN, removeFromSourceQuery);
+ final String latMax = getParam(srcQuery, FILTER_EXPR_LAT_MAX, removeFromSourceQuery);
+ if (destFilter != null) {
+ destFilter.setLogitude(lonMin, lonMax);
+ destFilter.setLatitude(latMin, latMax);
+ }
+ }
+ return destFilter;
+ }
+
+ public static String getFilePath(QueryParameter query, boolean removeFromSourceQuery) {
+ if (query == null) return null;
+ return getParam(query, FILTER_EXPR_PATH_LIKE, removeFromSourceQuery);
+ }
+
+ /** append path expressions from src to dest. Return null if unchanged. */
+ public static QueryParameter copyPathExpressions(QueryParameter dest, QueryParameter src) {
+ QueryParameter resultQuery = null;
+ if (src != null) {
+ // duplicate will be modified. preserve original
+ QueryParameter remainingQuery = new QueryParameter(src);
+ String pathExpr = null;
+ while (true) {
+ pathExpr = getFilePath(remainingQuery, true);
+ if (pathExpr != null) {
+ if (resultQuery == null) resultQuery = new QueryParameter(dest);
+ resultQuery.addWhere(FILTER_EXPR_PATH_LIKE, pathExpr);
+ } else {
+ break;
+ }
+ }
+ }
+ return resultQuery;
+ }
+
/** @return return param for expression inside query. null if expression is not in query or number of params is not 1. */
- protected static String getParam(QueryParameter query, String expresion, boolean remove) {
- final String[] result = getParams(query, expresion, remove);
+ protected static String getParam(QueryParameter query, String expresion, boolean removeFromSourceQuery) {
+ final String[] result = getParams(query, expresion, removeFromSourceQuery);
return ((result != null) && (result.length > 0)) ? result[0] : null;
}
/** @return return all params for expression inside query. null if expression is not in query */
- protected static String[] getParams(QueryParameter query, String expresion, boolean remove) {
- return query.getWhereParameter(expresion, remove);
+ protected static String[] getParams(QueryParameter query, String expresion, boolean removeFromSourceQuery) {
+ return query.getWhereParameter(expresion, removeFromSourceQuery);
}
public static QueryParameter setWhereSelectionPks(QueryParameter query, SelectedItems selectedItems) {
@@ -386,14 +440,18 @@ public static void setWhereFileNames(QueryParameter query, String... fileNames)
}
public static void addWhereLatLonNotNull(QueryParameter query) {
- query.addWhere(FotoSql.SQL_COL_LAT + " is not null and " + FotoSql.SQL_COL_LON + " is not null")
- ;
+ query.addWhere(FotoSql.SQL_COL_LAT + " is not null and "
+ + FotoSql.SQL_COL_LON + " is not null");
}
- public static void addWhereFilterLatLon(QueryParameter parameters, IGeoRectangle filter) {
- if ((parameters != null) && (filter != null)) {
- addWhereFilterLatLon(parameters, filter.getLatitudeMin(),
- filter.getLatitudeMax(), filter.getLogituedMin(), filter.getLogituedMax());
+ public static void addWhereFilterLatLon(QueryParameter resultQuery, IGeoRectangle filter) {
+ if ((resultQuery != null) && (filter != null)) {
+ if (filter.isNonGeoOnly()) {
+ resultQuery.addWhere(FILTER_EXPR_NO_GPS);
+ } else {
+ addWhereFilterLatLon(resultQuery, filter.getLatitudeMin(),
+ filter.getLatitudeMax(), filter.getLogituedMin(), filter.getLogituedMax());
+ }
}
}
@@ -587,7 +645,7 @@ public static boolean set(GalleryFilterParameter dest, String selectedAbsolutePa
public static String execGetFotoPath(Context context, Uri uriWithID) {
Cursor c = null;
try {
- c = createCursorForQuery("execGetFotoPath", context, uriWithID.toString(), null, null, null, FotoSql.SQL_COL_PATH);
+ c = createCursorForQuery(null, "execGetFotoPath(uri)", context, uriWithID.toString(), null, null, null, FotoSql.SQL_COL_PATH);
if (c.moveToFirst()) {
return DBUtils.getString(c,FotoSql.SQL_COL_PATH, null);
}
@@ -605,7 +663,7 @@ public static List execGetFotoPaths(Context context, String pathFilter)
Cursor c = null;
try {
- c = createCursorForQuery("execGetFotoPaths", context,SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME,
+ c = createCursorForQuery(null, "execGetFotoPaths(pathFilter)", context,SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME,
FotoSql.SQL_COL_PATH + " like ? and " + FILTER_EXPR_PRIVATE_PUBLIC,
new String[]{pathFilter}, FotoSql.SQL_COL_PATH, FotoSql.SQL_COL_PATH);
while (c.moveToNext()) {
@@ -623,16 +681,18 @@ public static List execGetFotoPaths(Context context, String pathFilter)
return result;
}
- public static Cursor createCursorForQuery(String dbgContext, final Context context, QueryParameter parameters, VISIBILITY visibility) {
+ public static Cursor createCursorForQuery(
+ StringBuilder out_debugMessage, String dbgContext, final Context context,
+ QueryParameter parameters, VISIBILITY visibility) {
if (visibility != null) setWhereVisibility(parameters, visibility);
- return createCursorForQuery(dbgContext, context, parameters.toFrom(), parameters.toAndroidWhere(),
+ return createCursorForQuery(out_debugMessage, dbgContext, context, parameters.toFrom(), parameters.toAndroidWhere(),
parameters.toAndroidParameters(), parameters.toOrderBy(),
parameters.toColumns()
);
}
/** every cursor query should go through this. adds logging if enabled */
- private static Cursor createCursorForQuery(String dbgContext, final Context context, final String from, final String sqlWhereStatement,
+ private static Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgContext, final Context context, final String from, final String sqlWhereStatement,
final String[] sqlWhereParameters, final String sqlSortOrder,
final String... sqlSelectColums) {
ContentResolver resolver = context.getContentResolver();
@@ -644,18 +704,25 @@ private static Cursor createCursorForQuery(String dbgContext, final Context cont
} catch (Exception ex) {
excpetion = ex;
} finally {
- if ((excpetion != null) || Global.debugEnabledSql) {
- Log.i(Global.LOG_CONTEXT, dbgContext + ": FotoSql.createCursorForQuery: " + excpetion +
- "\n" +
+ if ((excpetion != null) || Global.debugEnabledSql || (out_debugMessage != null)) {
+ StringBuilder message = StringUtils.appendMessage(out_debugMessage, excpetion,
+ dbgContext,"FotoSql.createCursorForQuery:\n" ,
QueryParameter.toString(sqlSelectColums, null, from, sqlWhereStatement,
- sqlWhereParameters, sqlSortOrder, query.getCount()), excpetion);
+ sqlWhereParameters, sqlSortOrder, query.getCount()));
+ if (out_debugMessage == null) {
+ Log.i(Global.LOG_CONTEXT, message.toString(), excpetion);
+ } // else logging is done by caller
}
}
return query;
}
- public static IGeoRectangle execGetGeoRectangle(Context context, IGalleryFilter filter, SelectedItems selectedItems) {
+ public static IGeoRectangle execGetGeoRectangle(StringBuilder out_debugMessage, Context context, QueryParameter baseQuery,
+ SelectedItems selectedItems, Object... dbgContext) {
+ StringBuilder debugMessage = (out_debugMessage == null)
+ ? StringUtils.createDebugMessage(Global.debugEnabledSql, dbgContext)
+ : out_debugMessage;
QueryParameter query = new QueryParameter()
.setID(QUERY_TYPE_UNDEFINED)
.addColumn(
@@ -668,8 +735,8 @@ public static IGeoRectangle execGetGeoRectangle(Context context, IGalleryFilter
.addFrom(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME)
;
- if (!GalleryFilterParameter.isEmpty(filter)) {
- filter2Query(query, filter, true);
+ if (baseQuery != null) {
+ query.getWhereFrom(baseQuery, true);
}
if (selectedItems != null) {
@@ -677,36 +744,42 @@ public static IGeoRectangle execGetGeoRectangle(Context context, IGalleryFilter
}
FotoSql.addWhereLatLonNotNull(query);
+ GeoRectangle result = null;
Cursor c = null;
try {
- c = createCursorForQuery("execGetGeoRectangle", context, query, VISIBILITY.PRIVATE_PUBLIC);
+ c = createCursorForQuery(debugMessage, "execGetGeoRectangle", context, query, VISIBILITY.PRIVATE_PUBLIC);
if (c.moveToFirst()) {
- GeoRectangle result = new GeoRectangle();
+ result = new GeoRectangle();
result.setLatitude(c.getDouble(0), c.getDouble(1));
result.setLogitude(c.getDouble(2), c.getDouble(3));
- if (Global.debugEnabledSql) {
- Log.i(Global.LOG_CONTEXT, "FotoSql.execGetGeoRectangle() => " + result + " from " + c.getLong(4) + " via\n\t" + query);
- }
-
return result;
}
} catch (Exception ex) {
Log.e(Global.LOG_CONTEXT, "FotoSql.execGetGeoRectangle(): error executing " + query, ex);
} finally {
if (c != null) c.close();
+ if (debugMessage != null) {
+ StringUtils.appendMessage(debugMessage, "result", result);
+ if (out_debugMessage == null) {
+ Log.i(Global.LOG_CONTEXT, debugMessage.toString());
+ }
+ }
}
- return null;
+ return result;
}
/** gets IGeoPoint either from file if fullPath is not null else from db via id */
- public static IGeoPoint execGetPosition(Context context, String fullPath, long id) {
+ public static IGeoPoint execGetPosition(StringBuilder out_debugMessage, Context context,
+ String fullPath, long id, Object... dbgContext) {
+ StringBuilder debugMessage = (out_debugMessage == null) ? StringUtils.createDebugMessage(Global.debugEnabledSql, dbgContext) : out_debugMessage;
QueryParameter query = new QueryParameter()
.setID(QUERY_TYPE_UNDEFINED)
.addColumn(SQL_COL_LAT, SQL_COL_LON)
.addFrom(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME)
.addWhere(SQL_COL_LAT + " IS NOT NULL")
- .addWhere(SQL_COL_LON + " IS NOT NULL");
+ .addWhere(SQL_COL_LON + " IS NOT NULL")
+ .addWhere("(" + SQL_COL_LON + " <> 0 OR " + SQL_COL_LAT + " <> 0)");
if (fullPath != null) {
query.addWhere(SQL_COL_PATH + "= ?", fullPath);
@@ -715,17 +788,24 @@ public static IGeoPoint execGetPosition(Context context, String fullPath, long i
query.addWhere(FILTER_COL_PK, "" + id);
}
+ GeoPoint result = null;
Cursor c = null;
try {
- c = createCursorForQuery("execGetPosition", context, query, VISIBILITY.PRIVATE_PUBLIC);
+ c = createCursorForQuery(debugMessage, "execGetPosition", context, query, VISIBILITY.PRIVATE_PUBLIC);
if (c.moveToFirst()) {
- GeoPoint result = new GeoPoint(c.getDouble(0),c.getDouble(1));
+ result = new GeoPoint(c.getDouble(0),c.getDouble(1));
return result;
}
} catch (Exception ex) {
Log.e(Global.LOG_CONTEXT, "FotoSql.execGetPosition: error executing " + query, ex);
} finally {
if (c != null) c.close();
+ if (debugMessage != null) {
+ StringUtils.appendMessage(debugMessage, "result", result);
+ if (out_debugMessage == null) {
+ Log.i(Global.LOG_CONTEXT, debugMessage.toString());
+ } // else logging by caller
+ }
}
return null;
}
@@ -746,7 +826,7 @@ public static Map execGetPathIdMap(Context context, String... file
Cursor c = null;
try {
- c = createCursorForQuery("execGetPathIdMap", context, query, VISIBILITY.PRIVATE_PUBLIC);
+ c = createCursorForQuery(null, "execGetPathIdMap", context, query, null);
while (c.moveToNext()) {
result.put(c.getString(1),c.getLong(0));
}
@@ -813,7 +893,7 @@ public static int execRenameFolder(Context context, String pathOld, String pathN
.addWhere(SQL_COL_EXT_MEDIA_TYPE + " IS NOT NULL")
;
- SelectedFiles selectedFiles= getSelectedfiles(context, queryAffectedFiles, sqlColNewPathAlias);
+ SelectedFiles selectedFiles= getSelectedfiles(context, queryAffectedFiles, sqlColNewPathAlias, null);
String[] paths = selectedFiles.getFileNames();
Long[] ids = selectedFiles.getIds();
@@ -859,7 +939,7 @@ private static int _del_execRenameFolder_batch_not_working(Context context, Stri
Cursor c = null;
try {
- c = createCursorForQuery(dbgContext, context, queryAffectedFiles, null);
+ c = createCursorForQuery(null, dbgContext, context, queryAffectedFiles, null);
int pkColNo = c.getColumnIndex(FotoSql.SQL_COL_PK);
int pathColNo = c.getColumnIndex(sqlColNewPathAlias);
@@ -911,16 +991,22 @@ protected static int exexUpdateImpl(String dbgContext, Context context, ContentV
protected static String getFilterExprPathLikeWithVisibility(VISIBILITY visibility) {
// visibility VISIBILITY.PRIVATE_PUBLIC
- return FotoSql.FILTER_EXPR_PATH_LIKE + " AND " + getFilterExpressionVisibility(visibility);
+ String resultExpression = FotoSql.FILTER_EXPR_PATH_LIKE;
+ if (visibility != null) {
+ resultExpression += " AND " + getFilterExpressionVisibility(visibility);
+ }
+ return resultExpression;
}
+ /** return id of inserted item */
public static Long insertOrUpdateMediaDatabase(String dbgContext, Context context,
String dbUpdateFilterJpgFullPathName,
- ContentValues values, Long updateSuccessValue) {
+ ContentValues values, VISIBILITY visibility,
+ Long updateSuccessValue) {
Long result = updateSuccessValue;
int modifyCount = FotoSql.execUpdate(dbgContext, context, dbUpdateFilterJpgFullPathName,
- values, VISIBILITY.PRIVATE_PUBLIC);
+ values, visibility);
if (modifyCount == 0) {
// update failed (probably becauce oldFullPathName not found. try insert it.
@@ -964,6 +1050,15 @@ public static int execDeleteByPath(String dbgContext, Activity context, String p
return delCount;
}
+ public static int deleteMedia(String dbgContext, Context context, List pathsToBeRemoved,
+ boolean preventDeleteImageFile) {
+ if ((pathsToBeRemoved != null) && (pathsToBeRemoved.size() > 0)) {
+ String whereDelete = SQL_COL_PATH + " in ('" + ListUtils.toString("','", pathsToBeRemoved) + "')";
+ return deleteMedia(dbgContext, context, whereDelete, null, preventDeleteImageFile);
+ }
+ return 0;
+ }
+
public static int deleteMediaWithNullPath(Context context) {
/// delete where SQL_COL_PATH + " is null" throws null pointer exception
QueryParameter wherePathIsNull = new QueryParameter();
@@ -972,7 +1067,7 @@ public static int deleteMediaWithNullPath(Context context) {
// return deleteMedia("delete without path (_data = null)", context, wherePathIsNull.toAndroidWhere(), null, false);
- SelectedFiles filesWitoutPath = getSelectedfiles(context, wherePathIsNull, FotoSql.SQL_COL_PATH);
+ SelectedFiles filesWitoutPath = getSelectedfiles(context, wherePathIsNull, FotoSql.SQL_COL_PATH, VISIBILITY.PRIVATE_PUBLIC);
String pksAsString = filesWitoutPath.toIdString();
if ((pksAsString != null) && (pksAsString.length() > 0)) {
QueryParameter whereInIds = new QueryParameter();
@@ -1060,22 +1155,41 @@ public static String getUriString(long imageID) {
return SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME + "/" + imageID;
}
- public static SelectedFiles getSelectedfiles(Context context, String sqlWhere) {
+ public static SelectedFiles getSelectedfiles(Context context, String sqlWhere, VISIBILITY visibility) {
QueryParameter query = new QueryParameter(FotoSql.queryChangePath);
query.addWhere(sqlWhere);
query.addOrderBy(FotoSql.SQL_COL_PATH);
- return getSelectedfiles(context, query, FotoSql.SQL_COL_PATH);
+ return getSelectedfiles(context, query, FotoSql.SQL_COL_PATH, visibility);
+
+ }
+
+ @Nullable
+ public static long getCount(Context context, QueryParameter query) {
+ QueryParameter queryModified = new QueryParameter(query);
+ queryModified.clearColumns().addColumn("count(*)");
+ Cursor c = null;
+ try {
+ c = FotoSql.createCursorForQuery(null, "getCount", context, queryModified, null);
+ if (c.moveToNext()) {
+ return c.getLong(0);
+ }
+ } catch (Exception ex) {
+ Log.e(Global.LOG_CONTEXT, "FotoSql.getCount() error :", ex);
+ } finally {
+ if (c != null) c.close();
+ }
+ return 0;
}
@Nullable
- private static SelectedFiles getSelectedfiles(Context context, QueryParameter query, String colnameForPath) {
+ private static SelectedFiles getSelectedfiles(Context context, QueryParameter query, String colnameForPath, VISIBILITY visibility) {
SelectedFiles result = null;
Cursor c = null;
try {
- c = FotoSql.createCursorForQuery("getSelectedfiles", context, query, VISIBILITY.PRIVATE_PUBLIC);
+ c = FotoSql.createCursorForQuery(null, "getSelectedfiles", context, query, visibility);
int len = c.getCount();
Long[] ids = new Long[len];
String[] paths = new String[len];
@@ -1149,7 +1263,7 @@ private static List getFileNamesImpl(Context context, QueryParameter par
Cursor cursor = null;
try {
- cursor = createCursorForQuery("getFileNames", context, parameters, VISIBILITY.PRIVATE_PUBLIC);
+ cursor = createCursorForQuery(null, "getFileNames", context, parameters, VISIBILITY.PRIVATE_PUBLIC);
int colPath = cursor.getColumnIndex(SQL_COL_DISPLAY_TEXT);
if (colPath == -1) colPath = cursor.getColumnIndex(SQL_COL_PATH);
@@ -1193,14 +1307,44 @@ public static QueryParameter setWhereVisibility(QueryParameter parameters, VISIB
if (parameters.toFrom() == null) {
parameters.addFrom(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME);
}
- String sqlWhere = parameters.toAndroidWhere();
- if ((sqlWhere == null) || (parameters.toFrom().contains(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME) && !sqlWhere.contains(SQL_COL_EXT_MEDIA_TYPE))) {
- parameters.addWhere(getFilterExpressionVisibility(visibility));
+
+ if (visibility != null) {
+ String sqlWhere = parameters.toAndroidWhere();
+ if ((sqlWhere == null) || (parameters.toFrom().contains(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME) && !sqlWhere.contains(SQL_COL_EXT_MEDIA_TYPE))) {
+ parameters.addWhere(getFilterExpressionVisibility(visibility));
+ }
}
return parameters;
}
+ public static List getAlbumFiles(Context context, String path, int subDirLevels) {
+ SelectedFiles databaseFiles = FotoSql.getSelectedfiles(context,
+ SQL_COL_PATH +" like '" + path + "/%" + AlbumFile.SUFFIX_VALBUM + "' OR " +
+ SQL_COL_PATH +" like '" + path + "/%" + AlbumFile.SUFFIX_QUERY + "'", null);
+ String[] fileNames = (databaseFiles == null) ? null : databaseFiles.getFileNames();
+ List paths = new ArrayList();
+
+ // copy all items that are not deeper that subDirLevels
+ int numSegs = StringUtils.charCount(path,'/') + subDirLevels;
+ if (fileNames != null) {
+ for (int i = fileNames.length - 1; i >= 0; i--) {
+ String fileName = fileNames[i];
+ if ((fileName != null) && (StringUtils.charCount(fileName, '/') <= numSegs)) {
+ paths.add(fileName);
+ }
+ }
+ }
+ return paths;
+ }
+
+ public static int execRename(Context context, String oldFullPath, String newFullPath) {
+ ContentValues values = new ContentValues();
+ values.put(SQL_COL_PATH, newFullPath);
+ return FotoSql.execUpdate("rename file", context, oldFullPath,
+ values, null);
+ }
+
public static class CursorLoaderWithException extends CursorLoader {
private final QueryParameter query;
private Exception mException;
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoThumbSql.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoThumbSql.java
index 4bbfa6a5..137eb258 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoThumbSql.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoThumbSql.java
@@ -25,7 +25,6 @@
import de.k3b.android.androFotoFinder.Global;
import de.k3b.database.QueryParameter;
-import de.k3b.io.IGalleryFilter;
import de.k3b.io.VISIBILITY;
/**
@@ -67,7 +66,7 @@ private static String getStatistic(Context context, QueryParameter query, String
Cursor c = null;
try {
- c = FotoSql.createCursorForQuery(mDebugPrefix + "getStatistic", context, query, VISIBILITY.PRIVATE_PUBLIC);
+ c = FotoSql.createCursorForQuery(null, mDebugPrefix + "getStatistic", context, query, VISIBILITY.PRIVATE_PUBLIC);
if (Global.debugEnabledSql) {
Log.i(Global.LOG_CONTEXT, mDebugPrefix + "getStatistic " + c.getCount() +
"\n\t" + query.toSqlString());
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagSql.java b/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagSql.java
index d8e77f31..b15fd3c4 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagSql.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagSql.java
@@ -35,6 +35,7 @@
import de.k3b.FotoLibGlobal;
import de.k3b.android.androFotoFinder.Global;
import de.k3b.android.androFotoFinder.media.MediaContentValues;
+import de.k3b.android.androFotoFinder.queries.AndroidAlbumUtils;
import de.k3b.android.androFotoFinder.queries.FotoSql;
import de.k3b.android.util.MediaScanner;
import de.k3b.io.GalleryFilterParameter;
@@ -94,9 +95,16 @@ public static IGalleryFilter parseQueryEx(QueryParameter query, boolean remove)
// from more complex to less complex
String[] params;
- if ((params = getParams(query, FILTER_EXPR_ANY_LIKE, remove)) != null) {
- resultFilter.setInAnyField(params[0]);
+ StringBuilder any = null;
+ while ((params = getParams(query, FILTER_EXPR_ANY_LIKE, remove)) != null) {
+ if (any == null) {
+ any = new StringBuilder().append(params[0]);
+ } else {
+ any.append(" ").append(params[0]);
+
+ }
}
+ if (any != null) resultFilter.setInAnyField(any.toString());
parseTagsFromQuery(query, remove, resultFilter);
@@ -152,17 +160,17 @@ private static void parseTagsFromQuery(QueryParameter query, boolean remove, Gal
}
+ public static QueryParameter filter2NewQuery(IGalleryFilter filter) {
+ return AndroidAlbumUtils.getAsMergedNewQueryParameter(null, filter);
+ }
+
public static void filter2QueryEx(QueryParameter resultQuery, IGalleryFilter filter, boolean clearWhereBefore) {
if ((resultQuery != null) && (!GalleryFilterParameter.isEmpty(filter))) {
filter2Query(resultQuery, filter, clearWhereBefore);
if (Global.Media.enableIptcMediaScanner) {
- String any = filter.getInAnyField();
- if ((any != null) && (any.length() > 0)) {
- if (!any.contains("%")) {
- any = "%" + any + "%";
- }
- resultQuery.addWhere(FILTER_EXPR_ANY_LIKE, any, any, any, any);
- }
+ String allAny = filter.getInAnyField();
+
+ addFilterAny(resultQuery, allAny);
List includes = ListUtils.emptyAsNull(filter.getTagsAllIncluded());
@@ -193,6 +201,19 @@ public static void filter2QueryEx(QueryParameter resultQuery, IGalleryFilter fil
}
}
+ public static void addFilterAny(QueryParameter resultQuery, String allAny) {
+ if (allAny != null) {
+ for (String any : allAny.split(" ")) {
+ if ((any != null) && (any.length() > 0)) {
+ if (!any.contains("%")) {
+ any = "%" + any + "%";
+ }
+ resultQuery.addWhere(FILTER_EXPR_ANY_LIKE, any, any, any, any);
+ }
+ }
+ }
+ }
+
private static QueryParameter addWhereTagExcluded(QueryParameter resultQuery, String tag, boolean withNoTags) {
return resultQuery.addWhere((withNoTags) ? FILTER_EXPR_TAG_NONE_OR_EXCLUDED : FILTER_EXPR_TAG_EXCLUDED, "%;" + tag + ";%");
}
@@ -381,7 +402,7 @@ public static int getTagRefCount(Context context, List tags) {
if (addWhereAnyOfTags(query, tags) > 0) {
Cursor c = null;
try {
- c = createCursorForQuery("getTagRefCount", context, query, VISIBILITY.PRIVATE_PUBLIC);
+ c = createCursorForQuery(null, "getTagRefCount", context, query, VISIBILITY.PRIVATE_PUBLIC);
if (c.moveToFirst()) {
return c.getInt(0);
}
@@ -435,7 +456,7 @@ public static List loadTagWorflowItems(Context context, String s
if (filterCount > 0) {
try {
- c = createCursorForQuery("loadTagWorflowItems", context, query, VISIBILITY.PRIVATE_PUBLIC);
+ c = createCursorForQuery(null, "loadTagWorflowItems", context, query, VISIBILITY.PRIVATE_PUBLIC);
if (c.moveToFirst()) {
do {
result.add(new TagWorflowItem(c.getLong(0), c.getString(1), TagConverter.fromString(c.getString(2)),
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagsPickerFragment.java b/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagsPickerFragment.java
index 48b5185b..3c1608c0 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagsPickerFragment.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagsPickerFragment.java
@@ -490,7 +490,7 @@ public static boolean handleMenuShow(int menuItemItemId, Tag selectedTag, Activi
ImageDetailActivityViewPager.showActivity(context, null, 0, createSubQueryByTag(parentFilter, selectedTag), 0);
return true;
case R.id.cmd_gallery:
- FotoGalleryActivity.showActivity(context, createSubFilterByTag(parentFilter, selectedTag), null, 0);
+ FotoGalleryActivity.showActivity(context, createSubQueryByTag(parentFilter, selectedTag), 0);
return true;
default:break;
}
@@ -500,8 +500,7 @@ public static boolean handleMenuShow(int menuItemItemId, Tag selectedTag, Activi
@NonNull
private static QueryParameter createSubQueryByTag(IGalleryFilter parentFilter, Tag selectedTag) {
GalleryFilterParameter filter = createSubFilterByTag(parentFilter, selectedTag);
- QueryParameter query = new QueryParameter();
- TagSql.filter2QueryEx(query, filter, false);
+ QueryParameter query = TagSql.filter2NewQuery(filter);
FotoSql.setSort(query, FotoSql.SORT_BY_DATE, false);
return query;
}
diff --git a/app/src/main/java/de/k3b/android/osmdroid/OsmdroidUtil.java b/app/src/main/java/de/k3b/android/osmdroid/OsmdroidUtil.java
index 75e7e63f..cde37568 100644
--- a/app/src/main/java/de/k3b/android/osmdroid/OsmdroidUtil.java
+++ b/app/src/main/java/de/k3b/android/osmdroid/OsmdroidUtil.java
@@ -29,59 +29,71 @@
import org.osmdroid.api.IMapController;
import org.osmdroid.tileprovider.MapTileProviderBase;
import org.osmdroid.util.GeoPoint;
+import org.osmdroid.util.TileSystem;
import org.osmdroid.views.MapView;
-import microsoft.mappoint.TileSystem;
-
/**
* Helper for OsmDroid lib.
*
* Created by k3b on 16.03.2015.
*/
public class OsmdroidUtil {
+ // osmdroid supports 0..29.0 but this app only 0..22
+ private static int MAX_ZOOM_LEVEL = 22;
public static final int NO_ZOOM = -1; // GeoPointDto.NO_ZOOM;
+ public static final int RECALCULATE_ZOOM = NO_ZOOM; // GeoPointDto.NO_ZOOM;
/**
* Similar to MapView.zoomToBoundingBox that seems to be to inexact.
* @param zoom if NO_ZOOM (-1) zoom is calculated from min and max
*/
- public static void zoomTo(MapView mapView, int zoom, IGeoPoint min, IGeoPoint max) {
+ public static int zoomTo(MapView mapView, int zoom, IGeoPoint min, IGeoPoint max) {
int calculatedZoom = zoom;
- MapTileProviderBase tileProvider = mapView.getTileProvider();
- IMapController controller = mapView.getController();
- IGeoPoint center = min;
-
- if (max != null) {
- center = new GeoPoint((max.getLatitudeE6() + min.getLatitudeE6()) / 2, (max.getLongitudeE6() + min.getLongitudeE6()) / 2);
-
- if (calculatedZoom == NO_ZOOM) {
- // int pixels = Math.min(mapView.getWidth(), mapView.getHeight());
- double pixels = Math.sqrt((mapView.getWidth() * mapView.getWidth()) + (mapView.getHeight() * mapView.getHeight()));
- final double requiredMinimalGroundResolutionInMetersPerPixel
- = ((double) new GeoPoint(min.getLatitude(), min.getLongitude()).distanceToAsDouble(max)) / pixels;
- calculatedZoom = calculateZoom(center.getLatitude(), requiredMinimalGroundResolutionInMetersPerPixel, tileProvider.getMaximumZoomLevel(), tileProvider.getMinimumZoomLevel());
+ if (mapView != null) {
+ MapTileProviderBase tileProvider = mapView.getTileProvider();
+ IMapController controller = mapView.getController();
+ IGeoPoint center = min;
+
+ if (max != null) {
+ center = new GeoPoint((max.getLatitude() + min.getLatitude()) / 2, (max.getLongitude() + min.getLongitude()) / 2);
+
+ if (calculatedZoom == NO_ZOOM) {
+ // int pixels = Math.min(mapView.getWidth(), mapView.getHeight());
+ double pixels = Math.sqrt((mapView.getWidth() * mapView.getWidth()) + (mapView.getHeight() * mapView.getHeight()));
+ final double requiredMinimalGroundResolutionInMetersPerPixel
+ = ((double) new GeoPoint(min.getLatitude(), min.getLongitude()).distanceToAsDouble(max)) / pixels;
+ calculatedZoom = calculateZoom(center.getLatitude(), requiredMinimalGroundResolutionInMetersPerPixel, getMaximumZoomLevel(tileProvider), tileProvider.getMinimumZoomLevel());
+ }
+ }
+ if (calculatedZoom != NO_ZOOM) {
+ controller.setZoom((double) calculatedZoom);
}
- }
- if (calculatedZoom != NO_ZOOM) {
- controller.setZoom(calculatedZoom);
- }
- if (center != null) {
- controller.setCenter(center);
- }
-/*
- if (logger.isDebugEnabled()) {
- logger.debug("DelayedSetCenterZoom.execute({}: ({}) .. ({}),z={}) => ({}), z={} => {}",
- debugContext,
- min, max, mZoomLevel, center, zoom, getStatusForDebug());
+ if (center != null) {
+ controller.setCenter(center);
}
-*/
+ }
+ return calculatedZoom;
+ }
+
+ public static double getMaximumZoomLevel(MapView map) {
+ final double maxZoomLevel = map.getMaxZoomLevel();
+ return (maxZoomLevel > MAX_ZOOM_LEVEL) ? MAX_ZOOM_LEVEL : maxZoomLevel;
+ }
+
+ public static int getMaximumZoomLevel(MapTileProviderBase tileProvider) {
+ final int maxZoomLevel = tileProvider.getMaximumZoomLevel();
+ return (maxZoomLevel > MAX_ZOOM_LEVEL) ? MAX_ZOOM_LEVEL : maxZoomLevel;
}
- private static int calculateZoom(double latitude, double requiredMinimalGroundResolutionInMetersPerPixel, int maximumZoomLevel, int minimumZoomLevel) {
- for (int zoom = maximumZoomLevel; zoom >= minimumZoomLevel; zoom--) {
- if (TileSystem.GroundResolution(latitude, zoom) > requiredMinimalGroundResolutionInMetersPerPixel)
+ private static int calculateZoom(double latitude, double requiredMinimalGroundResolutionInMetersPerPixel,
+ int maximumZoomLevel, int minimumZoomLevel) {
+ double groundResolution;
+ for (int zoom = Math.min(maximumZoomLevel, 15); zoom >= minimumZoomLevel; zoom--) {
+ // groundResolution = TileSystemFix_978.GroundResolution(latitude, zoom);
+ groundResolution = TileSystem.GroundResolution(latitude, zoom);
+ if (groundResolution > requiredMinimalGroundResolutionInMetersPerPixel)
return zoom;
}
diff --git a/app/src/main/java/de/k3b/android/util/AndroidFileCommands.java b/app/src/main/java/de/k3b/android/util/AndroidFileCommands.java
index ee5bd8b3..67c026c1 100644
--- a/app/src/main/java/de/k3b/android/util/AndroidFileCommands.java
+++ b/app/src/main/java/de/k3b/android/util/AndroidFileCommands.java
@@ -215,8 +215,13 @@ public int execRename(File srcDirFile, String newFolderName) {
if (destDirFile != null) {
destDirFile.getParentFile().mkdirs();
+ boolean isDir = srcDirFile.isDirectory();
if (srcDirFile.renameTo(destDirFile)) {
- modifyCount = FotoSql.execRenameFolder(this.mContext, srcDirFile.getAbsolutePath() + "/", destDirFile.getAbsolutePath() + "/");
+ if (isDir) {
+ modifyCount = FotoSql.execRenameFolder(this.mContext, srcDirFile.getAbsolutePath() + "/", destDirFile.getAbsolutePath() + "/");
+ } else {
+ modifyCount = FotoSql.execRename(mContext, srcDirFile.getAbsolutePath(), destDirFile.getAbsolutePath());
+ }
if (modifyCount < 0) {
destDirFile.renameTo(srcDirFile); // error: undo change
return -1;
@@ -329,17 +334,41 @@ public int deleteFiles(SelectedFiles fotos, IProgessListener progessListener) {
}
@SuppressLint("ValidFragment")
- class MediaScannerDirectoryPickerFragment extends DirectoryPickerFragment {
+ private static class MediaScannerDirectoryPickerFragment extends DirectoryPickerFragment {
+ private AndroidFileCommands mParent = null;
+
/** do not use activity callback */
@Override protected void setDirectoryListener(Activity activity) {}
@Override
protected void onDirectoryPick(IDirectory selection) {
dismiss();
- if (selection != null) {
- onMediaScannerAnswer(selection.getAbsolute());
+ if ((mParent != null) && (selection != null)) {
+ mParent.onMediaScannerAnswer(selection.getAbsolute());
}
}
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ // else the java.lang.InstantiationException: can't instantiate
+ // class de.k3b.android.util.AndroidFileCommands$MediaScannerDirectoryPickerFragment;
+ // no empty constructor
+ // on orientation change
+ dismiss();
+ }
+
+ public void setParent(AndroidFileCommands parent) {
+ this.mParent = parent;
+ }
+
+ @Override
+ public void dismiss() {
+ setParent(null);
+ super.dismiss();
+ }
+
}
public boolean cmdMediaScannerWithQuestion() {
@@ -352,35 +381,14 @@ public boolean cmdMediaScannerWithQuestion() {
return true;
} else if (AndroidFileCommands.canProcessFile(mContext, this.isInBackground)) {
// show dialog to get start parameter
- DirectoryPickerFragment destDir = new MediaScannerDirectoryPickerFragment() {
- /** do not use activity callback */
- @Override protected void setDirectoryListener(Activity activity) {}
-
- @Override
- protected void onDirectoryPick(IDirectory selection) {
- dismiss();
- if (selection != null) {
- onMediaScannerAnswer(selection.getAbsolute());
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- // else the java.lang.InstantiationException: can't instantiate
- // class de.k3b.android.util.AndroidFileCommands$MediaScannerDirectoryPickerFragment;
- // no empty constructor
- // on orientation change
- dismiss();
- }
- };
+ MediaScannerDirectoryPickerFragment destDir = new MediaScannerDirectoryPickerFragment();
+ destDir.setParent(this);
destDir.setTitleId(R.string.scanner_dir_question);
- destDir.defineDirectoryNavigation(OsUtils.getRootOSDirectory(),
+ destDir.defineDirectoryNavigation(OsUtils.getRootOSDirectory(null),
FotoSql.QUERY_TYPE_UNDEFINED,
getLastCopyToPath());
- destDir.setContextMenuId(LockScreen.isLocked(mContext) ? 0 : R.menu.menu_context_osdir);
+ destDir.setContextMenuId(LockScreen.isLocked(mContext) ? 0 : R.menu.menu_context_osdir);
destDir.show(mContext.getFragmentManager(), "scannerPick");
return true;
diff --git a/app/src/main/java/de/k3b/android/util/IntentUtil.java b/app/src/main/java/de/k3b/android/util/IntentUtil.java
index 2dfe1d4f..313d3985 100644
--- a/app/src/main/java/de/k3b/android/util/IntentUtil.java
+++ b/app/src/main/java/de/k3b/android/util/IntentUtil.java
@@ -58,6 +58,7 @@ public static String getFilePath(Context context, Uri uri) {
if ((scheme == null) || ("file".equals(scheme))) {
path = uri.getPath();
} else if ("content".equals(scheme)) {
+ // try to translate via media db
path = FotoSql.execGetFotoPath(context, uri);
if (path != null) {
if (Global.debugEnabled) {
@@ -65,10 +66,21 @@ public static String getFilePath(Context context, Uri uri) {
"' to '" + path + "'");
}
} else {
- Log.i(Global.LOG_CONTEXT, "Cannot translate from '" + uri +
- "' to local file");
+ // #118 try to translate from app specific content uri
+ // i.e. "OI file manager" uses "content://org.openintents.filemanager/%2Fstorage%2Femulated%2F0%2FDCIM%2F%F0%9F%93%B8test%2F180122mytest001.jpg"
+ path = uri.getPath();
}
}
+
+ // #118 app specific content uri convert from //storage/emulated/0/DCIM/... to /storage/emulated/0/DCIM/
+ if ((path != null) && (path.startsWith("//"))) path = path.substring(1);
+ final File file = getExistingFileOrNull(path);
+
+ if (file == null) {
+ path = null;
+ Log.i(Global.LOG_CONTEXT, "Cannot translate from '" + uri +
+ "' to local file");
+ }
}
return path;
}
@@ -88,20 +100,39 @@ public static Uri getUri(Intent intent) {
/** return null if uri is not a valid file scheam */
@Nullable
public static File getFile(Uri uri) {
+ // #118: opened from "OI file manager" with app specific content uri-s i.e.
+ // "content://org.openintents.filemanager/%2Fstorage%2Femulated%2F0%2FDCIM%2F%F0%9F%93%B8test%2F180122mytest001.jpg"
if (isFileUri(uri)) {
+ final File file = getExistingFileOrNull(uri.getPath());
+ if (file != null) return file;
+ }
+ return null;
+ }
+
+ private static File getExistingFileOrNull(String fullPath) {
+ if (fullPath != null) {
try {
- return new File(uri.getPath());
+ final File file = new File(fullPath);
+ if ((file != null) && (file.exists())) {
+ return file;
+ }
} catch (Exception ex) {
- ; // i.e. contain illegal chars
+ Log.d(Global.LOG_CONTEXT,
+ "Cannot open " + fullPath + " as file ");
}
}
return null;
}
public static boolean isFileUri(Uri uri) {
+ return isFileOrContentUri(uri,false);
+ }
+
+ public static boolean isFileOrContentUri(Uri uri, boolean allowContent) {
if (uri == null) return false;
String scheme = uri.getScheme();
- return StringUtils.isNullOrEmpty(scheme) || (0 == "file".compareTo(scheme));
+ return StringUtils.isNullOrEmpty(scheme) || (0 == "file".compareTo(scheme)
+ || (allowContent && (0 == "content".compareTo(scheme))));
}
public static boolean isFileUri(String initalFileUrl) {
diff --git a/app/src/main/java/de/k3b/android/util/MediaScanner.java b/app/src/main/java/de/k3b/android/util/MediaScanner.java
index 4b36504f..002f97f4 100644
--- a/app/src/main/java/de/k3b/android/util/MediaScanner.java
+++ b/app/src/main/java/de/k3b/android/util/MediaScanner.java
@@ -235,7 +235,9 @@ public Long insertOrUpdateMediaDatabase(String dbgContext, Context context,
if ((currentJpgFile != null) && currentJpgFile.exists() && currentJpgFile.canRead()) {
ContentValues values = createDefaultContentValues();
getExifFromFile(values, currentJpgFile);
- Long result = FotoSql.insertOrUpdateMediaDatabase(dbgContext, context, dbUpdateFilterJpgFullPathName, values, updateSuccessValue);
+ Long result = FotoSql.insertOrUpdateMediaDatabase(
+ dbgContext, context, dbUpdateFilterJpgFullPathName,
+ values, VISIBILITY.PRIVATE_PUBLIC, updateSuccessValue);
return result;
}
@@ -301,7 +303,7 @@ private int renameInMediaDatabase(Context context, Map old2NewFi
Cursor c = null;
try {
- c = FotoSql.createCursorForQuery("renameInMediaDatabase", context, query, VISIBILITY.PRIVATE_PUBLIC);
+ c = FotoSql.createCursorForQuery(null, "renameInMediaDatabase", context, query, VISIBILITY.PRIVATE_PUBLIC);
int pkColNo = c.getColumnIndex(FotoSql.SQL_COL_PK);
int pathColNo = c.getColumnIndex(FotoSql.SQL_COL_PATH);
while (c.moveToNext()) {
@@ -436,14 +438,24 @@ public int updatePathRelatedFields(Context context, Cursor cursor, String newAbs
}
/** sets the path related fields */
- private void setPathRelatedFieldsIfNeccessary(ContentValues values, String newAbsolutePath, String oldAbsolutePath) {
+ private static void setPathRelatedFieldsIfNeccessary(ContentValues values, String newAbsolutePath, String oldAbsolutePath) {
setFieldIfNeccessary(values, DB_TITLE, generateTitleFromFilePath(newAbsolutePath), generateTitleFromFilePath(oldAbsolutePath));
setFieldIfNeccessary(values, DB_DISPLAY_NAME, generateDisplayNameFromFilePath(newAbsolutePath), generateDisplayNameFromFilePath(oldAbsolutePath));
values.put(DB_DATA, newAbsolutePath);
}
+ /** sets the path related fields */
+ public static String setFileFields(ContentValues values, File file) {
+ String newAbsolutePath = FileUtils.tryGetCanonicalPath(file, file.getAbsolutePath());
+ setPathRelatedFieldsIfNeccessary(values, newAbsolutePath, null);
+ values.put(DB_DATE_MODIFIED, file.lastModified() / 1000);
+ values.put(DB_SIZE, file.length());
+ return newAbsolutePath;
+ }
+
+
/** values[fieldName]=newCalculatedValue if current not set or equals oldCalculatedValue */
- private void setFieldIfNeccessary(ContentValues values, String fieldName, String newCalculatedValue, String oldCalculatedValue) {
+ private static void setFieldIfNeccessary(ContentValues values, String fieldName, String newCalculatedValue, String oldCalculatedValue) {
String currentValue = values.getAsString(fieldName);
if ((currentValue == null) || (TextUtils.isEmpty(currentValue.trim())) || (currentValue.equals(oldCalculatedValue))) {
values.put(fieldName, newCalculatedValue);
@@ -485,7 +497,7 @@ private int insert_Android42(String dbgContext, Context context, File file) {
@NonNull
// generates a title based on file name
- protected String generateTitleFromFilePath(String _filePath) {
+ protected static String generateTitleFromFilePath(String _filePath) {
String filePath = generateDisplayNameFromFilePath(_filePath);
if (filePath != null) {
diff --git a/app/src/main/java/de/k3b/android/util/MenuUtils.java b/app/src/main/java/de/k3b/android/util/MenuUtils.java
index 560541da..07d7c41e 100644
--- a/app/src/main/java/de/k3b/android/util/MenuUtils.java
+++ b/app/src/main/java/de/k3b/android/util/MenuUtils.java
@@ -68,6 +68,19 @@ public static void setShowAsActionFlags(Menu menu, int actionEnum, int... menuId
if (sub != null) sub.setShowAsActionFlags(SHOW_AS_ACTION_NEVER);
}
}
+ /**
+ * For api convinience: remove 0, 1 or more menuitems by id
+ * @param menu where items are removed from
+ * @param menuIds 0 or more menu-ids to be modified.
+ */
+ public static void removeItems(Menu menu, int... menuIds) {
+ if ((menu != null) && (menuIds != null)) {
+ for (int menuId: menuIds) {
+ menu.removeItem(menuId);
+ }
+ }
+
+ }
public static void changeShowAsActionFlags(EditText edit, final int actionEnum, final int... menuIds) {
ActionMode.Callback callback = new ActionMode.Callback() {
diff --git a/app/src/main/java/de/k3b/android/util/OsUtils.java b/app/src/main/java/de/k3b/android/util/OsUtils.java
index 175c7c01..b206c0f3 100644
--- a/app/src/main/java/de/k3b/android/util/OsUtils.java
+++ b/app/src/main/java/de/k3b/android/util/OsUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder.
*
@@ -23,6 +23,7 @@
import java.io.File;
+import de.k3b.io.FileUtils;
import de.k3b.io.OSDirectory;
/**
@@ -60,20 +61,32 @@ public static File buildPath(File base, String... segments) {
return cur;
}
- public static OSDirectory getRootOSDirectory() {
+ /**
+ * create android specific dir root.
+ *
+ * @param factory null or factory that creates OSDirectory or subclass of OSDirectory.
+ */
+ public static OSDirectory getRootOSDirectory(OSDirectory factory) {
// #103: bugfix
// this works for android-4.4 an earlier and on rooted devices
- OSDirectory root = new OSDirectory("/", null);
+ OSDirectory root = createOsDirectory(FileUtils.tryGetCanonicalFile("/"), factory);
if (root.getChildren().size() == 0) {
// on android-5.0 an newer root access is not allowed.
// i.e. /storage/emulated/0
File externalRoot = Environment.getExternalStorageDirectory();
if (externalRoot != null) {
- root = new OSDirectory(externalRoot.getAbsolutePath(), null);
+ root = createOsDirectory(externalRoot, factory);
}
}
return root;
}
+ private static OSDirectory createOsDirectory(File file, OSDirectory factory) {
+ if (factory != null) return factory.createOsDirectory(file, null, null);
+ return new OSDirectory(file, null, null);
+ }
+ public static File getDefaultPhotoRoot() {
+ return new File(Environment.getExternalStorageDirectory(),Environment.DIRECTORY_DCIM);
+ }
}
diff --git a/app/src/main/java/de/k3b/android/widget/BaseQueryActivity.java b/app/src/main/java/de/k3b/android/widget/BaseQueryActivity.java
new file mode 100644
index 00000000..da4e9c62
--- /dev/null
+++ b/app/src/main/java/de/k3b/android/widget/BaseQueryActivity.java
@@ -0,0 +1,1176 @@
+/*
+ * Copyright (c) 2015-2018 by k3b.
+ *
+ * This file is part of AndroFotoFinder / #APhotoManager.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see
+ */
+
+package de.k3b.android.widget;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import de.k3b.FotoLibGlobal;
+import de.k3b.android.androFotoFinder.Common;
+import de.k3b.android.androFotoFinder.GalleryFilterActivity;
+import de.k3b.android.androFotoFinder.GalleryFilterPathState;
+import de.k3b.android.androFotoFinder.Global;
+import de.k3b.android.androFotoFinder.LockScreen;
+import de.k3b.android.androFotoFinder.R;
+import de.k3b.android.androFotoFinder.directory.DirectoryLoaderTask;
+import de.k3b.android.androFotoFinder.directory.DirectoryPickerFragment;
+import de.k3b.android.androFotoFinder.locationmap.LocationMapFragment;
+import de.k3b.android.androFotoFinder.queries.AndroidAlbumUtils;
+import de.k3b.android.androFotoFinder.queries.FotoSql;
+import de.k3b.android.androFotoFinder.tagDB.TagSql;
+import de.k3b.android.androFotoFinder.tagDB.TagsPickerFragment;
+import de.k3b.android.osmdroid.OsmdroidUtil;
+import de.k3b.android.util.MediaScanner;
+
+import de.k3b.database.QueryParameter;
+import de.k3b.io.AlbumFile;
+import de.k3b.io.Directory;
+import de.k3b.io.DirectoryFormatter;
+import de.k3b.io.GalleryFilterParameter;
+import de.k3b.io.IDirectory;
+import de.k3b.io.IGalleryFilter;
+import de.k3b.io.ListUtils;
+import de.k3b.io.StringUtils;
+import de.k3b.io.collections.SelectedItems;
+import de.k3b.tagDB.Tag;
+
+/**
+ * All that is is needed for to have a base-filter plus a sub-filter
+ *
+ * Created by k3b on 10.07.2018.
+ */
+public abstract class BaseQueryActivity extends ActivityWithAutoCloseDialogs implements Common, DirectoryPickerFragment.OnDirectoryInteractionListener,
+ LocationMapFragment.OnDirectoryInteractionListener,
+ TagsPickerFragment.ITagsPicker {
+ protected static final String mDebugPrefix = "GalleryA-";
+
+ private static final String DLG_NAVIGATOR_TAG = "navigator";
+ private static final String DEFAULT_BOOKMARKNAME_PICK_GEO = "pickGeoFromPhoto";
+ public static final int resultID = 522;
+
+ protected GalleryQueryParameter mGalleryQueryParameter = new GalleryQueryParameter();
+
+ protected String mTitleResultCount = "";
+
+ protected boolean mHasEmbeddedDirPicker = false;
+
+ /**
+ * every thing that belongs to search.
+ * visible gallery items are mGalleryContentBaseQuery + expression(mCurrentSubFilterMode)
+ */
+ protected class GalleryQueryParameter {
+
+ /** picker has different set of filter parameters as ordenary gallery view */
+ private static final String PICK_NONE_SUFFIX = "";
+ private static final String PICK_GEO_SUFFIX = "-pick-geo";
+ private static final String PICK_IMAGE_SUFFIX = "-pick-image";
+
+ /**
+ * one of the PICK_XXXX_SUFFIX constants
+ * view/pick-image/pick-geo have different state persistence.
+ * naem=STATE_XXXXX + mSharedPrefKeySuffix
+ * ""==view; "-pick-image"; "-pick-geo"
+ */
+ private String mSharedPrefKeySuffix = PICK_NONE_SUFFIX;
+
+ /**
+ * STATE_... to persist current filter
+ */
+ private static final String STATE_DirQueryID = "DirQueryID";
+ private static final String STATE_SortID = "SortID";
+ private static final String STATE_SortAscending = "SortAscending";
+
+ private static final String STATE_SUB_FILTER = "subFilter";
+
+ private static final String STATE_SUB_FILTR_MODE = "currentSubFilterMode";
+
+ /**
+ * mCurrentSubFilterMode = SUB_FILTER_MODE_XXX: which filter addon is currently active
+ */
+ private static final int SUB_FILTER_MODE_NONE = -1;
+ private static final int SUB_FILTER_MODE_PATH = 0;
+ private static final int SUB_FILTER_MODE_GEO = 1;
+ private static final int SUB_FILTER_MODE_TAG = 2;
+ private static final int SUB_FILTER_MODE_SEARCH_BAR = 3;
+ private static final int SUB_FILTER_MODE_ALBUM = 4;
+ private static final int SUB_FILTER_MODE_DATE = 5;
+
+ /**
+ * mCurrentSubFilterMode = SUB_FILTER_MODE_XXX: which filter addon is currently active:
+ * Filter = basefilter + mCurrentSubFilterMode
+ */
+ private int mCurrentSubFilterMode = SUB_FILTER_MODE_NONE;
+ private String mAlbumName = null;
+
+ /**
+ * mCurrentSubFilterMode defines which of the Filter parameters define the current visible items
+ */
+ private GalleryFilterParameter mCurrentSubFilterSettings = new GalleryFilterParameter();
+
+ /**
+ * sql defines current visible items with optional sort order
+ */
+ protected QueryParameter mGalleryContentBaseQuery = null;
+
+ /**
+ * one of the FotoSql.QUERY_TYPE_xxx values
+ */
+ protected int mDirQueryID = FotoSql.QUERY_TYPE_GROUP_DEFAULT;
+
+ /**
+ * current sort order
+ */
+ private int mCurrentSortID = FotoSql.SORT_BY_DEFAULT;
+ /**
+ * current sort order
+ */
+ private boolean mCurrentSortAscending = false;
+
+ /**
+ * true: if activity started without special intent-parameters,
+ * the last mCurrentSubFilterSettings is saved/loaded for next use
+ */
+ private boolean mSaveLastUsedFilterToSharedPrefs = true;
+
+ /**
+ * one of the FotoSql.QUERY_TYPE_xxx values. if undefined use default
+ */
+ public int getDirQueryID() {
+ if (this.mDirQueryID == FotoSql.QUERY_TYPE_UNDEFINED)
+ return FotoSql.QUERY_TYPE_GROUP_DEFAULT;
+
+ return this.mDirQueryID;
+ }
+
+ public int getSortID() {
+ return mCurrentSortID;
+ }
+
+ public void setSortID(int sortID) {
+ if (sortID == mCurrentSortID) {
+ mCurrentSortAscending = !mCurrentSortAscending;
+ } else {
+ mCurrentSortAscending = true;
+ mCurrentSortID = sortID;
+ }
+ }
+
+ public String getSortDisplayName(Context context) {
+ return FotoSql.getName(context, this.mCurrentSortID) + " " + ((mCurrentSortAscending) ? IGalleryFilter.SORT_DIRECTION_ASCENDING : IGalleryFilter.SORT_DIRECTION_DESCENDING);
+ }
+
+ public boolean clearPathIfActive() {
+ if ((mCurrentSubFilterMode == SUB_FILTER_MODE_PATH) && (mCurrentSubFilterSettings.getPath() != null)) {
+ mCurrentSubFilterSettings.setPath(null);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * combine root-query plus current selected directoryRoot/geo/tags
+ */
+ public QueryParameter calculateEffectiveGalleryContentQuery() {
+ return calculateEffectiveGalleryContentQuery(mGalleryContentBaseQuery);
+ }
+
+ /**
+ * combine root-query plus current selected directoryRoot
+ */
+ private QueryParameter calculateEffectiveGalleryContentQuery(QueryParameter rootQuery) {
+ QueryParameter result = new QueryParameter(rootQuery);
+
+ final IGalleryFilter currentSubFilterSettings = this.getCurrentSubFilterSettings();
+ if (currentSubFilterSettings != null) {
+
+ if (result == null) return null;
+
+ switch (mCurrentSubFilterMode) {
+ case SUB_FILTER_MODE_SEARCH_BAR:
+ TagSql.addFilterAny(result, currentSubFilterSettings.getInAnyField());
+ break;
+ case SUB_FILTER_MODE_GEO:
+ FotoSql.addWhereFilterLatLon(result, currentSubFilterSettings);
+ break;
+ case SUB_FILTER_MODE_TAG:
+ TagSql.addWhereTagsIncluded(result, currentSubFilterSettings.getTagsAllIncluded(), false);
+ break;
+ case SUB_FILTER_MODE_PATH:
+ case SUB_FILTER_MODE_ALBUM: {
+ final String path = currentSubFilterSettings.getPath();
+ if (!StringUtils.isNullOrEmpty(path)) {
+ Uri uri = Uri.fromFile(new File(path));
+ QueryParameter albumQuery = AndroidAlbumUtils.getQueryFromUri(
+ mDebugPrefix + " calculateEffectiveGalleryContentQuery ",
+ BaseQueryActivity.this, uri, null);
+ if (albumQuery != null) {
+ result.getWhereFrom(albumQuery, true);
+ } else if (MediaScanner.isNoMedia(path, MediaScanner.DEFAULT_SCAN_DEPTH)) {
+ // do not show (parent-)directories that contain ".nomedia"
+ return null;
+ } else {
+ FotoSql.addPathWhere(result, path, this.getDirQueryID());
+ }
+ }
+ }
+ break;
+
+ case SUB_FILTER_MODE_DATE:
+ FotoSql.addWhereDateMinMax(result, currentSubFilterSettings.getDateMin(), currentSubFilterSettings.getDateMax());
+ break;
+ }
+ }
+
+ if (mCurrentSortID != IGalleryFilter.SORT_BY_NONE) {
+ FotoSql.setSort(result, mCurrentSortID, mCurrentSortAscending);
+ }
+ return result;
+ }
+
+ public boolean isGeoPick() {
+ return (mSharedPrefKeySuffix != null) && mSharedPrefKeySuffix.equals(PICK_GEO_SUFFIX);
+ }
+
+ // load from intent/ savedInstanceState/ SharedPrefs
+ // Use cases
+
+ /**
+ * load from savedInstanceState/ intent/ SharedPrefs
+ * Use cases
+ * * stand alone gallery (i.e. from file manager with intent-uri of image.jpg/virtual-album/directory)
+ * * sub gallery drill down from other apm activity with intent containing file-uri/query-extra and/or filter-extra
+ * * as picker for
+ * * * ACTION_PICK geo via image
+ * * * ACTION_PICK pick image
+ * * * ACTION_GET_CONTENT
+ *
+ * @param context
+ * @param savedInstanceState
+ */
+ private void loadSettingsAndInstanceState(Activity context, Bundle savedInstanceState) {
+
+ Intent intent = context.getIntent();
+
+ // for debugging: where does the filter come from
+ StringBuilder dbgMessageResult = (Global.debugEnabled) ? new StringBuilder() : null;
+
+ // special name handling for pickers
+ String action = (intent != null) ? intent.getAction() : null;
+ if ((action != null) && ((Intent.ACTION_PICK.compareTo(action) == 0) || (Intent.ACTION_GET_CONTENT.compareTo(action) == 0))) {
+ String schema = intent.getScheme();
+ if ((schema != null) && ("geo".compareTo(schema) == 0)) {
+ this.mSharedPrefKeySuffix = PICK_GEO_SUFFIX;
+ if (dbgMessageResult != null) dbgMessageResult.append("pick geo ");
+ } else {
+ this.mSharedPrefKeySuffix = PICK_IMAGE_SUFFIX;
+ if (dbgMessageResult != null) dbgMessageResult.append("pick photo ");
+ }
+ this.mSaveLastUsedFilterToSharedPrefs = true;
+ } else {
+ this.mSharedPrefKeySuffix = PICK_NONE_SUFFIX;
+ // save only if no intent-uri is involved
+ this.mSaveLastUsedFilterToSharedPrefs = StringUtils.isNullOrEmpty(intent.getDataString());
+ }
+ this.mSharedPrefKeySuffix = fixSharedPrefSuffix(this.mSharedPrefKeySuffix);
+ SharedPreferences sharedPref =
+ (this.mSaveLastUsedFilterToSharedPrefs)
+ ? PreferenceManager.getDefaultSharedPreferences(context)
+ : null;
+
+ StringBuilder uriQueryFile = new StringBuilder();
+ this.mGalleryContentBaseQuery = AndroidAlbumUtils.getQuery(
+ BaseQueryActivity.this, mSharedPrefKeySuffix,
+ savedInstanceState, intent, sharedPref, uriQueryFile, dbgMessageResult);
+
+ if (dbgMessageResult != null) dbgMessageResult.append("SubFilter ");
+ boolean found = false;
+ if (savedInstanceState == null) {
+ // onCreate (first call) : if intent is usefull use it else use sharedPref
+ if (!found && (intent != null)) {
+
+ found = setState(dbgMessageResult, " from-Intent: ",
+ intent.getStringExtra(STATE_SUB_FILTER),
+ intent.getIntExtra(STATE_DirQueryID, this.getDirQueryID()),
+ intent.getIntExtra(STATE_SortID, this.mCurrentSortID),
+ intent.getBooleanExtra(STATE_SortAscending, this.mCurrentSortAscending),
+ intent.getIntExtra(STATE_SUB_FILTR_MODE, this.mCurrentSubFilterMode));
+ }
+ if (!found && (sharedPref != null)) {
+ found = setState(dbgMessageResult, " from-SharedPrefs: ",
+ sharedPref.getString(STATE_SUB_FILTER + mSharedPrefKeySuffix, null),
+ sharedPref.getInt(STATE_DirQueryID + mSharedPrefKeySuffix, this.getDirQueryID()),
+ sharedPref.getInt(STATE_SortID + mSharedPrefKeySuffix, this.mCurrentSortID),
+ sharedPref.getBoolean(STATE_SortAscending + mSharedPrefKeySuffix, this.mCurrentSortAscending),
+ sharedPref.getInt(STATE_SUB_FILTR_MODE, this.mCurrentSubFilterMode));
+ }
+ } else {
+ // (savedInstanceState != null) : onCreate after screen rotation
+
+ found = setState(dbgMessageResult, " from-InstanceState: ",
+ savedInstanceState.getString(STATE_SUB_FILTER, null),
+ savedInstanceState.getInt(STATE_DirQueryID, this.getDirQueryID()),
+ savedInstanceState.getInt(STATE_SortID, this.mCurrentSortID),
+ savedInstanceState.getBoolean(STATE_SortAscending, this.mCurrentSortAscending),
+ savedInstanceState.getInt(STATE_SUB_FILTR_MODE, this.mCurrentSubFilterMode));
+ }
+
+ // all parameters loaded: either album, filter or path
+ if (this.mGalleryContentBaseQuery == null) {
+ this.mGalleryContentBaseQuery = FotoSql.getQuery(FotoSql.QUERY_TYPE_DEFAULT);
+ if (dbgMessageResult != null) dbgMessageResult.append(" no query in parameters-use defaults ");
+ }
+
+ if (uriQueryFile.length() > 0) {
+ this.setAlbum(uriQueryFile.toString());
+ }
+
+ if (dbgMessageResult != null) {
+ Log.i(Global.LOG_CONTEXT, mDebugPrefix + dbgMessageResult.toString());
+ }
+ }
+
+ private void setAlbum(String uriQueryFile) {
+ Uri uri = (uriQueryFile == null) ? null : Uri.parse(uriQueryFile);
+ if (uri != null) {
+ this.mAlbumName = uri.getLastPathSegment();
+ this.mCurrentSubFilterMode = SUB_FILTER_MODE_ALBUM;
+ }
+ }
+
+ private boolean setState(StringBuilder dbgMessageResult, String dbgContext,
+ String subFilterSettingsAsString, int dirQueryID, int sortID,
+ boolean sortAscending, int subFilterMode) {
+ if (subFilterSettingsAsString != null) {
+ // SubFilterSettings, DirQueryID, SortID, SortAscending, SubFilterMode
+ GalleryFilterParameter.parse(subFilterSettingsAsString, mCurrentSubFilterSettings);
+ if (dbgMessageResult != null)
+ dbgMessageResult.append(dbgContext).append(subFilterSettingsAsString);
+ this.mDirQueryID = dirQueryID;
+ this.mCurrentSortID = sortID;
+ this.mCurrentSortAscending = sortAscending;
+ this.mCurrentSubFilterMode = subFilterMode;
+ return true;
+ }
+ return false;
+ }
+
+ private void saveToInstanceState(Context context, Bundle savedInstanceState) {
+ saveToSharedPrefs(context);
+
+ // SubFilterSettings, DirQueryID, SortID, SortAscending, SubFilterMode
+ if (mCurrentSubFilterSettings != null) {
+ savedInstanceState.putString(STATE_SUB_FILTER, mCurrentSubFilterSettings.toString());
+ }
+ savedInstanceState.putInt(STATE_DirQueryID, this.getDirQueryID());
+ savedInstanceState.putInt(STATE_SortID, this.mCurrentSortID);
+ savedInstanceState.putBoolean(STATE_SortAscending, this.mCurrentSortAscending);
+ savedInstanceState.putInt(STATE_SUB_FILTR_MODE, this.mCurrentSubFilterMode);
+
+ if ((mGalleryQueryParameter != null) && (mGalleryQueryParameter.mGalleryContentBaseQuery != null)) {
+ savedInstanceState.putString(EXTRA_QUERY, mGalleryQueryParameter.mGalleryContentBaseQuery.toReParseableString());
+ }
+
+ }
+
+ private void saveToSharedPrefs(Context context) {
+ if (mSaveLastUsedFilterToSharedPrefs) {
+ // SubFilterSettings, DirQueryID, SortID, SortAscending, SubFilterMode
+ SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor edit = sharedPref.edit();
+
+ if (getCurrentSubFilterSettings() != null) {
+ edit.putString(STATE_SUB_FILTER + mSharedPrefKeySuffix, getCurrentSubFilterSettings().toString());
+ }
+
+ edit.putInt(STATE_DirQueryID + mSharedPrefKeySuffix, this.getDirQueryID());
+ edit.putInt(STATE_SortID + mSharedPrefKeySuffix, this.mCurrentSortID);
+ edit.putBoolean(STATE_SortAscending + mSharedPrefKeySuffix, this.mCurrentSortAscending);
+ edit.putInt(STATE_SUB_FILTR_MODE, this.mCurrentSubFilterMode);
+
+ if ((mGalleryQueryParameter != null) && (mGalleryQueryParameter.mGalleryContentBaseQuery != null)) {
+ edit.putString(EXTRA_QUERY + mSharedPrefKeySuffix, mGalleryQueryParameter.mGalleryContentBaseQuery.toReParseableString());
+ }
+
+ edit.apply();
+ }
+ }
+
+ public GalleryFilterParameter getCurrentSubFilterSettings() {
+ return mCurrentSubFilterSettings;
+ }
+
+ // ...path, {album, +tag, ?search, #date, lat+long
+ protected CharSequence getValueAsTitle(boolean longName) {
+ final int subFilterMode = this.mCurrentSubFilterMode;
+ GalleryFilterParameter v = mCurrentSubFilterSettings;
+
+ if (v != null) {
+ switch (subFilterMode) {
+ case SUB_FILTER_MODE_PATH: {
+ File f = v.getPathFile();
+ if (f == null) break;
+ StringBuilder result = new StringBuilder();
+ result.insert(0, f.getName());
+ f = f.getParentFile();
+ if (f != null) {
+ result.insert(0, "/");
+ result.insert(0, f.getName());
+ }
+ result.insert(0, "...");
+ if (longName) result.insert(0, getString(R.string.folder_menu_title));
+
+ return result;
+ }
+
+ case SUB_FILTER_MODE_ALBUM: {
+ if (this.mAlbumName != null) return getValueAsTitle(longName, "{", R.string.bookmark, this.mAlbumName);
+ return null;
+ }
+
+ case SUB_FILTER_MODE_GEO:
+ final int lat = (int) v.getLatitudeMin();
+ final int lon = (int) v.getLogituedMin();
+ return ((longName) ? getString(R.string.area_menu_title) : "") + lat + " " + lon;
+ case SUB_FILTER_MODE_TAG:
+ return getValueAsTitle(longName, "+", R.string.tags_activity_title, ListUtils.toString(v.getTagsAllIncluded()));
+ case SUB_FILTER_MODE_SEARCH_BAR:
+ return getValueAsTitle(longName, "?", R.string.searchbar_menu_title, v.getInAnyField());
+ case SUB_FILTER_MODE_DATE:
+ return getValueAsTitle(longName, "#", R.string.date_picker_menu_title, v.getDatePath());
+ }
+ }
+ return null;
+ }
+
+ private CharSequence getValueAsTitle(boolean longName, String prefix, int stringResourceId, String value) {
+ StringBuilder result = new StringBuilder();
+ if (longName && (stringResourceId != 0)) result.append(getString(stringResourceId)).append(": ");
+ if (!StringUtils.isNullOrEmpty(value)) result.append(prefix);
+ result.append(value);
+ return result;
+ }
+ }
+
+ // ...path, {album, +tag, ?search, #date, lat+long
+ public CharSequence getValueAsTitle(boolean longName) {
+ if (mGalleryQueryParameter == null) return null;
+ return mGalleryQueryParameter.getValueAsTitle(longName);
+ }
+
+ /** allows childclass to have their own sharedPreference names */
+ protected String fixSharedPrefSuffix(String statSuffix) {
+ return statSuffix;
+ }
+
+
+ private FolderApi mFolderApi = null;
+ private FolderApi getFolderApi() {
+ if (mFolderApi == null) {
+ mFolderApi = new FolderApi();
+ }
+ return mFolderApi;
+ }
+
+ protected class FolderApi {
+ // either folder picker or date picker
+ private static final int QUERY_TYPE_GROUP_ALBUM = FotoSql.QUERY_TYPE_GROUP_ALBUM;
+ // either folder picker or date picker
+ private static final int QUERY_TYPE_GROUP_DATE = FotoSql.QUERY_TYPE_GROUP_DATE;
+
+ private IDirectory mDirectoryRoot = null;
+ private IDirectory mDateRoot = null;
+
+ /**
+ * true if activity should show navigator dialog after loading mDirectoryRoot is complete
+ */
+ private boolean mMustShowNavigator = false;
+
+ /**
+ * set while dir picker is active
+ */
+ private DirectoryPickerFragment mDirPicker = null;
+
+ private void openDatePicker() {
+ openPicker(BaseQueryActivity.GalleryQueryParameter.SUB_FILTER_MODE_DATE, QUERY_TYPE_GROUP_DATE);
+ }
+
+ private void openFolderPicker() {
+ openPicker(BaseQueryActivity.GalleryQueryParameter.SUB_FILTER_MODE_PATH, QUERY_TYPE_GROUP_ALBUM);
+ }
+
+ private void openPicker(final int filterMode, int _dirQueryID) {
+ mGalleryQueryParameter.mCurrentSubFilterMode = filterMode;
+ final Activity context = BaseQueryActivity.this;
+
+ /** if wrong datatype was saved: gallery is not allowed for dirPicker */
+ final int dirQueryID =
+ (FotoSql.QUERY_TYPE_GALLERY == _dirQueryID)
+ ? QUERY_TYPE_GROUP_ALBUM
+ : _dirQueryID;
+
+ mGalleryQueryParameter.mDirQueryID = dirQueryID;
+
+ final boolean loadDate = (dirQueryID == QUERY_TYPE_GROUP_DATE);
+ final IDirectory currentDirectoryRoot = loadDate ? this.mDateRoot : this.mDirectoryRoot;
+ if (currentDirectoryRoot == null) {
+ // not loaded yet. load directoryRoot in background
+ ;
+ final QueryParameter mergedBaseQuery = FotoSql.getQuery(dirQueryID);
+ mergedBaseQuery.getWhereFrom(mGalleryQueryParameter.mGalleryContentBaseQuery, false);
+ if (mergedBaseQuery != null) {
+ this.mMustShowNavigator = true;
+ mergedBaseQuery.setID(dirQueryID);
+
+ DirectoryLoaderTask loader = new DirectoryLoaderTask(context, loadDate ? FotoLibGlobal.datePickerUseDecade : false,
+ mDebugPrefix + " from openPicker(loadDate=" +
+ loadDate + ")") {
+ @Override
+ protected void onPostExecute(IDirectory directoryRoot) {
+ onDirectoryDataLoadComplete(loadDate, directoryRoot);
+ }
+ };
+
+ if (!loadDate) {
+ // limit valbums to matching parent-path query
+ QueryParameter vAlbumQueryWithPathExpr = FotoSql.copyPathExpressions(FotoSql.queryVAlbum, mergedBaseQuery);
+ if (vAlbumQueryWithPathExpr == null)
+ vAlbumQueryWithPathExpr = FotoSql.queryVAlbum;
+
+ // load dir-s + "*.album"
+ loader.execute(mergedBaseQuery, vAlbumQueryWithPathExpr);
+ } else {
+ loader.execute(mergedBaseQuery);
+ }
+ } else {
+ Log.e(Global.LOG_CONTEXT, mDebugPrefix + " this.mDirQueryID undefined "
+ + mGalleryQueryParameter.mDirQueryID);
+ }
+ // if not loaded yet
+ } else {
+ // if loaded
+ mMustShowNavigator = false;
+ final FragmentManager manager = getFragmentManager();
+ DirectoryPickerFragment dirDialog = new DirectoryPickerFragment();
+
+ dirDialog.setContextMenuId(LockScreen.isLocked(context) ? 0 : R.menu.menu_context_dirpicker);
+
+ String initialPath = mGalleryQueryParameter.getCurrentSubFilterSettings().getPath();
+
+ if ((initialPath == null) && (!loadDate)) {
+ initialPath = new GalleryFilterPathState()
+ .load(BaseQueryActivity.this,
+ null,null)
+ .getPathDefault(null);
+ }
+
+ if ((initialPath != null) && (initialPath.endsWith("%"))) {
+ initialPath = initialPath.substring(0,initialPath.length() - 1);
+ }
+ dirDialog.defineDirectoryNavigation(currentDirectoryRoot, dirQueryID,
+ initialPath);
+
+ mDirPicker = dirDialog;
+ setAutoClose(mDirPicker, null, null);
+ dirDialog.show(manager, DLG_NAVIGATOR_TAG);
+ }
+ }
+
+ private void onDirectoryDataLoadComplete(final boolean loadDate, IDirectory directoryRoot) {
+ if (directoryRoot == null) {
+ final String message = getString(R.string.folder_err_load_failed_format, FotoSql.getName(BaseQueryActivity.this, mGalleryQueryParameter.getDirQueryID()));
+ Toast.makeText(BaseQueryActivity.this, message, Toast.LENGTH_LONG).show();
+ } else {
+ boolean mustDefineNavigation;
+ if (loadDate) {
+ mustDefineNavigation= (mGalleryQueryParameter.getCurrentSubFilterSettings().getDatePath() != null);
+ this.mDateRoot = directoryRoot;
+ } else {
+ mustDefineNavigation= (mGalleryQueryParameter.getCurrentSubFilterSettings().getPath() != null);
+ this.mDirectoryRoot = directoryRoot;
+ }
+
+ final boolean mustShowFolderPicker = (directoryRoot != null) && (this.mMustShowNavigator);
+
+ if (Global.debugEnabled) {
+ StringBuilder name = new StringBuilder(directoryRoot.getAbsolute());
+ Directory.appendCount(name, directoryRoot, Directory.OPT_DIR | Directory.OPT_SUB_DIR);
+ Log.i(Global.LOG_CONTEXT, mDebugPrefix + "onDirectoryDataLoadComplete(" +
+ "mustDefineNavigation=" + mustDefineNavigation +
+ ", mustShowFolderPicker=" + mustShowFolderPicker +
+ ", content=" + name + ",loadDate=" +
+ loadDate + ")");
+ }
+
+ if (mustDefineNavigation) {
+ defineDirectoryNavigation(directoryRoot);
+ }
+ Global.debugMemory(mDebugPrefix, "onDirectoryDataLoadComplete");
+
+ if (mustShowFolderPicker) {
+ if (loadDate) {
+ openDatePicker();
+ } else {
+ openFolderPicker();
+ }
+ }
+ }
+ }
+
+ private void refreshSelection() {
+ IDirectory lastPopUpSelection = (mDirPicker == null) ? null : mDirPicker.getLastPopUpSelection();
+ if (lastPopUpSelection != null) lastPopUpSelection.refresh();
+ }
+
+ private void invalidateDirectories(String why) {
+ mDirectoryRoot = invalidateDirectories(why, mDirectoryRoot);
+ mDateRoot = invalidateDirectories(why, mDateRoot);
+ }
+
+ private IDirectory invalidateDirectories(String why, IDirectory directoryRoot) {
+ if (directoryRoot != null) {
+ if (Global.debugEnabled) {
+ StringBuilder name = new StringBuilder(directoryRoot.getAbsolute());
+ Directory.appendCount(name, directoryRoot, Directory.OPT_DIR | Directory.OPT_SUB_DIR);
+ Log.i(Global.LOG_CONTEXT, mDebugPrefix + "invalidateDirectories(" + name + ") because of " + why);
+ }
+ if (mDirPicker == null) {
+ directoryRoot.destroy();
+ directoryRoot = null; // must refreshLocal next time
+ }
+ }
+ return directoryRoot;
+ }
+ }
+
+ abstract protected void defineDirectoryNavigation(IDirectory directoryRoot);
+
+ private void openLatLonPicker(SelectedItems selectedItems) {
+ this.mGalleryQueryParameter.mCurrentSubFilterMode = BaseQueryActivity.GalleryQueryParameter.SUB_FILTER_MODE_GEO;
+
+ final FragmentManager manager = getFragmentManager();
+ LocationMapFragment dialog = new LocationMapFragment();
+ dialog.defineNavigation(this.mGalleryQueryParameter.mGalleryContentBaseQuery,
+ null,
+ this.mGalleryQueryParameter.getCurrentSubFilterSettings(), OsmdroidUtil.NO_ZOOM, selectedItems, null, false);
+
+ dialog.show(manager, DLG_NAVIGATOR_TAG);
+ }
+
+ private void openFilter() {
+ GalleryFilterActivity.showActivity(this,
+ null,
+ this.mGalleryQueryParameter.mGalleryContentBaseQuery,
+ null, BaseQueryActivity.resultID);
+ }
+
+ private void openTagPicker() {
+ mGalleryQueryParameter.mCurrentSubFilterMode = BaseQueryActivity.GalleryQueryParameter.SUB_FILTER_MODE_TAG;
+
+ final FragmentManager manager = getFragmentManager();
+ TagsPickerFragment dlg = new TagsPickerFragment();
+ dlg.setFragmentOnwner(this);
+ dlg.setTitleId(R.string.tags_activity_title);
+ List included = this.mGalleryQueryParameter.getCurrentSubFilterSettings().getTagsAllIncluded();
+ if (included == null) included = new ArrayList();
+ dlg.setAddNames(included);
+ dlg.show(manager, DLG_NAVIGATOR_TAG);
+ }
+
+ /**
+ * called by {@link TagsPickerFragment}
+ */
+ @Override
+ public boolean onCancel(String msg) {
+ return true;
+ }
+
+ /**
+ * called by {@link TagsPickerFragment}
+ */
+ @Override
+ public boolean onOk(List addNames, List removeNames) {
+ Log.d(Global.LOG_CONTEXT, "BaseQueryActivity.navigateTo " + ListUtils.toString(addNames) + " from "
+ + ListUtils.toString(mGalleryQueryParameter.getCurrentSubFilterSettings().getTagsAllIncluded()));
+ mGalleryQueryParameter.mCurrentSubFilterMode = BaseQueryActivity.GalleryQueryParameter.SUB_FILTER_MODE_TAG;
+ mGalleryQueryParameter.getCurrentSubFilterSettings().setTagsAllIncluded(new ArrayList(addNames));
+ reloadGui("navigate to tags");
+ return true;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Global.debugMemory(mDebugPrefix, "onCreate");
+ super.onCreate(savedInstanceState);
+ this.getContentResolver().registerContentObserver(FotoSql.SQL_TABLE_EXTERNAL_CONTENT_URI, true, mMediaObserverDirectory);
+ this.getContentResolver().registerContentObserver(FotoSql.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, true, mMediaObserverDirectory);
+ }
+
+ protected void onCreateData(Bundle savedInstanceState) {
+ final Intent intent = getIntent();
+
+ this.mGalleryQueryParameter.loadSettingsAndInstanceState(this, savedInstanceState);
+ }
+
+ /**
+ * OnDirectoryInteractionListener: called when user selects a new directoryRoot
+ */
+ @Override
+ public void onDirectoryPick(String selectedAbsolutePath, int queryTypeId) {
+ if (!this.mHasEmbeddedDirPicker) {
+ navigateTo(selectedAbsolutePath, queryTypeId);
+ }
+ closeDialogIfNeeded();
+
+ }
+
+ /**
+ * after media db change cached Directories must be recalculated
+ */
+ private final ContentObserver mMediaObserverDirectory = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ invalidateDirectories(mDebugPrefix + "#onChange from mMediaObserverDirectory");
+ }
+ };
+
+ /* OnDirectoryInteractionListener */
+ @Override
+ public void invalidateDirectories(String why) {
+ getFolderApi().invalidateDirectories(why);
+ }
+
+ /**
+ * DirectoryPickerFragment#OnDirectoryInteractionListener: called when user cancels selection of a new directoryRoot
+ */
+ @Override
+ public void onDirectoryCancel(int queryTypeId) {
+ closeDialogIfNeeded();
+ }
+
+ @Override
+ protected void closeDialogIfNeeded() {
+ super.closeDialogIfNeeded();
+ getFolderApi().mDirPicker = null;
+ }
+
+ /** DirectoryPickerFragment#OnDirectoryInteractionListener: called after the selection in tree has changed */
+ @Override
+ public void onDirectorySelectionChanged(String selectedAbsolutePath, int queryTypeId) {
+ if (this.mHasEmbeddedDirPicker) {
+ navigateTo(selectedAbsolutePath, queryTypeId);
+ }
+ }
+
+ private void navigateTo(String selectedAbsolutePath, int queryTypeId) {
+
+ if (selectedAbsolutePath != null) {
+ final GalleryFilterParameter currentSubFilterSettings = this.mGalleryQueryParameter.getCurrentSubFilterSettings();
+ if (mGalleryQueryParameter.mCurrentSubFilterMode == GalleryQueryParameter.SUB_FILTER_MODE_GEO) {
+ final String why = "FotoGalleryActivity.navigateTo tags geo ";
+ Log.d(Global.LOG_CONTEXT, why + selectedAbsolutePath + " from "
+ + DirectoryFormatter.formatLatLon(currentSubFilterSettings.getLatitudeMin()
+ ,currentSubFilterSettings.getLogituedMin()
+ ,currentSubFilterSettings.getLatitudeMax()
+ ,currentSubFilterSettings.getLogituedMax()));
+ currentSubFilterSettings.get(DirectoryFormatter.parseLatLon(selectedAbsolutePath));
+
+ reloadGui(why);
+
+ } else if (mGalleryQueryParameter.mCurrentSubFilterMode == GalleryQueryParameter.SUB_FILTER_MODE_TAG) {
+ final String why = "FotoGalleryActivity.navigateTo tags ";
+ Log.d(Global.LOG_CONTEXT, why + selectedAbsolutePath + " from "
+ + ListUtils.toString(this.mGalleryQueryParameter.getCurrentSubFilterSettings().getTagsAllIncluded()));
+ currentSubFilterSettings.setTagsAllIncluded(new ArrayList<>(ListUtils.fromString(selectedAbsolutePath)));
+
+ reloadGui(why);
+ } else if (mGalleryQueryParameter.mCurrentSubFilterMode == GalleryQueryParameter.SUB_FILTER_MODE_DATE) {
+ final String why = "FotoGalleryActivity.navigateTo date ";
+ Log.d(Global.LOG_CONTEXT, why + selectedAbsolutePath + " from " + currentSubFilterSettings.getDatePath());
+
+ Date from = new Date();
+ Date to = new Date();
+ DirectoryFormatter.getDates(selectedAbsolutePath, from, to);
+
+ currentSubFilterSettings.setDate(from.getTime(), to.getTime());
+ this.mGalleryQueryParameter.mCurrentSubFilterMode = GalleryQueryParameter.SUB_FILTER_MODE_DATE;
+ this.mGalleryQueryParameter.mDirQueryID = queryTypeId;
+ setTitle();
+
+ reloadGui(why);
+ } else if (mGalleryQueryParameter.mCurrentSubFilterMode == GalleryQueryParameter.SUB_FILTER_MODE_PATH) {
+ GalleryFilterPathState state = new GalleryFilterPathState()
+ .load(BaseQueryActivity.this,
+ null,null);
+ File queryFile = AlbumFile.getExistingQueryFileOrNull(selectedAbsolutePath);
+ if (queryFile != null) {
+ final String why = "FotoGalleryActivity.navigate to virtual album ";
+ Log.d(Global.LOG_CONTEXT, why + selectedAbsolutePath);
+
+ QueryParameter albumQuery = AndroidAlbumUtils.getQueryFromUri(mDebugPrefix + " navigateTo ", this, Uri.fromFile(queryFile), null);
+ if (albumQuery != null) {
+ this.mGalleryQueryParameter.mGalleryContentBaseQuery = albumQuery;
+ this.mGalleryQueryParameter.mCurrentSubFilterMode = GalleryQueryParameter.SUB_FILTER_MODE_ALBUM;
+ currentSubFilterSettings.setPath(selectedAbsolutePath);
+
+ state.setLastPath(queryFile.getParent());
+ state.setAlbum(Uri.fromFile(queryFile));
+
+ reloadGui(why);
+ }
+ } else {
+ final String why = "FotoGalleryActivity.navigateTo dir ";
+ Log.d(Global.LOG_CONTEXT, why + selectedAbsolutePath + " from " + currentSubFilterSettings.getPath());
+
+ currentSubFilterSettings.setPath(selectedAbsolutePath + "/%");
+ this.mGalleryQueryParameter.mCurrentSubFilterMode = GalleryQueryParameter.SUB_FILTER_MODE_PATH;
+ this.mGalleryQueryParameter.mDirQueryID = queryTypeId;
+ state.setLastPath(selectedAbsolutePath);
+ setTitle();
+
+ reloadGui(why);
+ }
+ state.save(this, null);
+
+ } // if SUB_FILTER_MODE_PATH
+ }
+ }
+
+
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ this.mGalleryQueryParameter.saveToInstanceState(this, savedInstanceState);
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ @Override
+ protected void onPause() {
+ Global.debugMemory(mDebugPrefix, "onPause");
+ this.mGalleryQueryParameter.saveToSharedPrefs(this);
+ super.onPause();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ invalidateDirectories(mDebugPrefix + "#onLowMemory");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ this.getContentResolver().unregisterContentObserver(mMediaObserverDirectory);
+ this.mGalleryQueryParameter.mGalleryContentBaseQuery = null;
+ invalidateDirectories(mDebugPrefix + "#onDestroy");
+ }
+
+ @Override
+ protected void onResume() {
+ Global.debugMemory(mDebugPrefix, "onResume");
+ super.onResume();
+ }
+
+ /**
+ * Call back from sub-activities.
+ * Process Change StartTime (longpress start), Select StopTime before stop
+ * (longpress stop) or filter change for detailReport
+ */
+ @Override
+ protected void onActivityResult(final int requestCode,
+ final int resultCode, final Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+
+ getFolderApi().refreshSelection();
+
+ switch (requestCode) {
+ case BaseQueryActivity.resultID:
+ onBaseFilterChanged(AndroidAlbumUtils.getQuery(
+ this, "", null, intent, null, null, null)
+ , mDebugPrefix + "#onActivityResult from GalleryFilterActivity");
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ protected abstract void reloadGui(String why);
+
+ /**
+ * called by {@link TagsPickerFragment}
+ */
+ @Override
+ public boolean onTagPopUpClick(int menuItemItemId, Tag selectedTag) {
+ return TagsPickerFragment.handleMenuShow(menuItemItemId, selectedTag, this, this.mGalleryQueryParameter.getCurrentSubFilterSettings());
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ final boolean result = super.onCreateOptionsMenu(menu);
+ initSearchView(menu.findItem(R.id.cmd_searchbar));
+ return result;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ final boolean result = super.onPrepareOptionsMenu(menu);
+ initSearchView(menu.findItem(R.id.cmd_searchbar));
+ return result;
+ }
+
+ protected boolean onOptionsItemSelected(MenuItem item, SelectedItems selectedItems) {
+ // Handle presses on the action bar items
+ switch (item.getItemId()) {
+ case R.id.cmd_select_date:
+ getFolderApi().openDatePicker();
+ return true;
+
+ case R.id.cmd_select_folder:
+ getFolderApi().openFolderPicker();
+ return true;
+
+ case R.id.cmd_select_lat_lon:
+ openLatLonPicker(selectedItems);
+ return true;
+ case R.id.cmd_select_tag:
+ openTagPicker();
+ return true;
+ case R.id.cmd_filter:
+ openFilter();
+ return true;
+ case R.id.cmd_sort_date:
+ this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_DATE);
+ reloadGui("sort date");
+ return true;
+ case R.id.cmd_sort_directory:
+ this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_NAME);
+ reloadGui("sort dir");
+ return true;
+ case R.id.cmd_sort_path_len:
+ this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_NAME_LEN);
+ reloadGui("sort len");
+ return true;
+ case R.id.cmd_sort_file_len:
+ this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_FILE_LEN);
+ reloadGui("sort size");
+ return true;
+
+ case R.id.cmd_sort_width:
+ this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_WIDTH);
+ reloadGui("sort width");
+ return true;
+
+ case R.id.cmd_sort_location:
+ this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_LOCATION);
+ reloadGui("sort geo");
+ return true;
+
+ case R.id.cmd_sort_rating:
+ this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_RATING);
+ reloadGui("sort rating");
+ return true;
+
+ case R.id.cmd_sort_modification:
+ this.mGalleryQueryParameter.setSortID(FotoSql.SORT_BY_MODIFICATION);
+ reloadGui("sort modification");
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ /**
+ * redefine base filter and refresh gui
+ */
+ protected void onBaseFilterChanged(QueryParameter query, String why) {
+ if (query != null) {
+ this.mGalleryQueryParameter.mGalleryContentBaseQuery = query;
+ this.mGalleryQueryParameter.mCurrentSubFilterMode = GalleryQueryParameter.SUB_FILTER_MODE_NONE;
+
+ invalidateDirectories(mDebugPrefix + "#filter changed " + why);
+
+ reloadGui("basefilter changed " + why);
+ setTitle();
+ }
+ }
+
+ /** GalleryFragment tells the Owning Activity that querying data has finisched */
+ public void setResultCount(int count) {
+ this.mTitleResultCount = (count > 0) ? ("(" + count + ")") : "";
+ setTitle();
+
+ // current path does not contain photo => refreshLocal witout current path
+ if ((count == 0) &&(mGalleryQueryParameter.clearPathIfActive())) {
+ setTitle();
+ reloadGui("query changed");
+ }
+ }
+
+ protected void setTitle() {
+ Intent intent = getIntent();
+ CharSequence title = (intent == null) ? null : intent.getStringExtra(EXTRA_TITLE);
+
+ if (title == null) {
+ title = mGalleryQueryParameter.getValueAsTitle(false);
+ if (StringUtils.isNullOrEmpty(title)) title = getString(R.string.gallery_title);
+ }
+ if (title != null) {
+ this.setTitle(mTitleResultCount + title);
+ }
+ }
+ /*********************** search view *******************/
+ private SearchViewWithHistory searchView = null;
+
+ protected void initSearchView(MenuItem item) {
+ if (item != null) {
+ final SearchViewWithHistory searchView = (SearchViewWithHistory) item.getActionView();
+ this.searchView = searchView;
+ if (searchView != null) {
+ searchView.setMenuItem(item);
+ // searchView.setCursorDrawable(R.drawable.custom_cursor);
+ searchView.setEllipsize(true);
+ searchView.setOnQueryTextListener(new SearchViewWithHistory.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ showSearchbarResult(query, "search bar submit");
+ // Toast.makeText(FotoGalleryActivity.this, "Query: " + query, Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ sendDelayed(HANDLER_FILTER_TEXT_CHANGED, HANDLER_FILTER_TEXT_DELAY);
+ return false;
+ }
+ });
+
+ searchView.setOnSearchViewListener(new SearchViewWithHistory.SearchViewListener() {
+ @Override
+ public void onSearchViewShown() {
+ showSearchbarResult("onSearchViewShown");
+ }
+
+ @Override
+ public void onSearchViewClosed() {
+
+ showSearchbarResult("onSearchViewClosed");
+ searchView.hideKeyboard(BaseQueryActivity.this.searchView.getRootView());
+ }
+ });
+ if (Global.debugEnabledViewItem) {
+ Log.i(Global.LOG_CONTEXT, mDebugPrefix + " initSearchView " + searchView);
+ }
+
+ }
+ }
+ }
+
+ private void showSearchbarResult(String why) {
+ if ((searchView != null) && (searchView.isSearchOpen()) ) {
+ showSearchbarResult(searchView.getFilterValue(), why);
+ }
+ }
+ private void showSearchbarResult(String query, String why) {
+ if (Global.debugEnabledViewItem) {
+ Log.i(Global.LOG_CONTEXT, mDebugPrefix + " showSearchbarResult(" +
+ why + ") " + searchView);
+ }
+ final GalleryFilterParameter currentSubFilterSettings = mGalleryQueryParameter.getCurrentSubFilterSettings();
+ if ((mGalleryQueryParameter.mCurrentSubFilterMode != GalleryQueryParameter.SUB_FILTER_MODE_SEARCH_BAR)
+ || (0 != StringUtils.compare(query, currentSubFilterSettings.getInAnyField()))) {
+
+ mGalleryQueryParameter.mCurrentSubFilterMode = GalleryQueryParameter.SUB_FILTER_MODE_SEARCH_BAR;
+ currentSubFilterSettings.setInAnyField(query);
+ if (Global.debugEnabledSql) {
+ Log.i(Global.LOG_CONTEXT, why + ": search " + query);
+ }
+
+ reloadGui(why);
+ } else {
+ if (Global.debugEnabledSql) {
+ Log.i(Global.LOG_CONTEXT, why + ": ignore " + query);
+ }
+
+ }
+ }
+
+ // char(s) typing in filter is active
+ private static final int HANDLER_FILTER_TEXT_CHANGED = 0;
+ private static final int HANDLER_FILTER_TEXT_DELAY = 500;
+
+ private final Handler delayProcessor = new Handler() {
+ @Override
+ public void handleMessage(final Message msg) {
+ clearDelayProcessor();
+ switch (msg.what) {
+ case HANDLER_FILTER_TEXT_CHANGED:
+ showSearchbarResult( "onQueryTextChange");
+ break;
+ default:
+ // not implemented
+ throw new IllegalStateException();
+ }
+ }
+
+ };
+
+ private void clearDelayProcessor() {
+ this.delayProcessor
+ .removeMessages(HANDLER_FILTER_TEXT_CHANGED);
+ }
+
+ private void sendDelayed(final int messageID, final int delayInMilliSec) {
+ this.clearDelayProcessor();
+
+ final Message msg = Message
+ .obtain(this.delayProcessor, messageID, null);
+ delayProcessor.sendMessageDelayed(msg,
+ delayInMilliSec);
+ }
+ @Override
+ public void onBackPressed() {
+ if ((searchView != null) && searchView.isSearchOpen()) {
+ searchView.closeSearch();
+
+ // ??bug?? : with back-key on my android-4.2 the soft keyboard does not close
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/k3b/android/widget/HistoryEditText.java b/app/src/main/java/de/k3b/android/widget/HistoryEditText.java
index de01a613..289ad0f9 100644
--- a/app/src/main/java/de/k3b/android/widget/HistoryEditText.java
+++ b/app/src/main/java/de/k3b/android/widget/HistoryEditText.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder.
*
@@ -20,11 +20,10 @@
package de.k3b.android.widget;
import android.annotation.TargetApi;
-import android.app.Activity;
+import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
-import android.support.annotation.NonNull;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -39,9 +38,10 @@
import de.k3b.io.ListUtils;
/**
- * Add history-popup to EditText.
- * invoke via Clipboard ContextActionBar: star (history) to open a popupmenu with previous values.
+ * Add history-popup to a list of EditText+ImageButton pairs.
+ * The ImageButton opens a popupmenu with previous values.
* Long-Press if no selection => popup with previous values.
+ * Use HistoryEditText.saveHistory() to persist the previous values in shared preferences.
*
* Popup-menu requires at least api 11 (HONEYCOMB)
* Created by k3b on 26.08.2015.
@@ -49,7 +49,7 @@
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class HistoryEditText {
private static final int NO_ID = -1;
- private final Activity mContext;
+ private final Context mContext;
private final String mDelimiter;
private final int mMaxHisotrySize;
private final EditorHandler[] mEditorHandlers;
@@ -60,11 +60,11 @@ protected class EditorHandler implements View.OnLongClickListener, View.OnClickL
private final ImageButton mCmd;
private final String mId;
- public EditorHandler(String id, EditText editor, int cmdId) {
+ public EditorHandler(String id, EditText editor, int imageButtonResourceId) {
mId = id;
mEditor = editor;
- mCmd = (cmdId != NO_ID) ? (ImageButton) mContext.findViewById(cmdId) : null;
+ mCmd = (imageButtonResourceId != NO_ID) ? (ImageButton) editor.getRootView().findViewById(imageButtonResourceId) : null;
if (mCmd == null) {
mEditor.setOnLongClickListener(this);
@@ -107,7 +107,6 @@ public boolean onMenuItemClick(MenuItem item) {
popup.show();
}
- @NonNull
private List getHistoryItems() {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mContext);
return getHistory(sharedPref);
@@ -176,12 +175,12 @@ public boolean onLongClick(View v) {
}
/** define history function for these editors */
- public HistoryEditText(Activity context, int[] cmdIds, EditText... editors) {
+ public HistoryEditText(Context context, int[] cmdIds, EditText... editors) {
this(context, context.getClass().getSimpleName() + "_history_","';'", 8, cmdIds, editors);
}
/** define history function for these editors */
- public HistoryEditText(Activity context, String settingsPrefix, String delimiter, int maxHisotrySize, int[] cmdIds, EditText... editors) {
+ public HistoryEditText(Context context, String settingsPrefix, String delimiter, int maxHisotrySize, int[] cmdIds, EditText... editors) {
this.mContext = context;
this.mDelimiter = delimiter;
diff --git a/app/src/main/java/de/k3b/android/widget/LocalizedActivity.java b/app/src/main/java/de/k3b/android/widget/LocalizedActivity.java
index c47f2882..fdba14db 100644
--- a/app/src/main/java/de/k3b/android/widget/LocalizedActivity.java
+++ b/app/src/main/java/de/k3b/android/widget/LocalizedActivity.java
@@ -106,4 +106,7 @@ public static void recreate(Activity child) {
}
}
+ @Override public String toString() {
+ return getClass().getSimpleName();
+ }
}
diff --git a/app/src/main/java/de/k3b/android/widget/SearchViewWithHistory.java b/app/src/main/java/de/k3b/android/widget/SearchViewWithHistory.java
new file mode 100644
index 00000000..4e11f25f
--- /dev/null
+++ b/app/src/main/java/de/k3b/android/widget/SearchViewWithHistory.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2015-2016 by Miguel Catalan Bañuls
+ * Copyright (c) 2018 by k3b.
+ *
+ * This file is part of AndroFotoFinder / #APhotoManager.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see
+ */
+
+package de.k3b.android.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.Filter;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import java.lang.reflect.Field;
+
+import de.k3b.android.androFotoFinder.R;
+import de.k3b.io.StringUtils;
+
+/**
+ * This is the implementation of a Searchview based on
+ * code from com.miguelcatalan.materialsearchview.MaterialSearchView
+ * from https://github.com/MiguelCatalan/MaterialSearchView/
+ *
+ * originally created (c) 2015-2016 by Miguel Catalan Bañuls via "Apache License"
+ *
+ * ------
+ *
+ * Changes: (c) 2018 by k3b
+ * * removed voice, styling, ListView with corresponding Adapter;
+ * * No more dependency to AppCmpat lib
+ * * Removed animation (required AppCompat)
+ * * Can be used with MenuItem (even without AppCompatActivity)
+ *
+ *
+ *
+ * without embedded toolbar in client qui.
+ *
+ */
+public class SearchViewWithHistory extends FrameLayout implements Filter.FilterListener, MenuItem.OnActionExpandListener {
+ private MenuItem mMenuItem;
+ private boolean mIsSearchOpen = false;
+ private boolean mClearingFocus;
+
+ //Views
+ private View mSearchLayout;
+ private View mTintView;
+ private EditText mSearchSrcTextView;
+ private ImageButton mBackBtn;
+ private ImageButton mEmptyBtn;
+ private View mSearchTopBar;
+ private HistoryEditText mHistory;
+
+ private CharSequence mOldQueryText;
+ private CharSequence mUserQuery;
+
+ private OnQueryTextListener mOnQueryChangeListener;
+ private SearchViewListener mSearchViewListener;
+
+ private SavedState mSavedState;
+ private boolean submit = false;
+
+ private boolean ellipsize = false;
+
+ private Context mContext;
+
+ public SearchViewWithHistory(Context context) {
+ this(context, null);
+ }
+
+ public SearchViewWithHistory(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SearchViewWithHistory(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs);
+
+ mContext = context;
+
+ initiateView();
+
+ initStyle(attrs, defStyleAttr);
+ }
+
+ // for debugging
+ @Override
+ public String toString() {
+ return StringUtils.appendMessage(null,
+ this.getClass().getSimpleName(),": open",
+ mIsSearchOpen, mUserQuery, mSavedState).toString();
+ }
+
+ private void initStyle(AttributeSet attrs, int defStyleAttr) {
+ TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.SearchViewWithHistory, defStyleAttr, 0);
+
+ if (a != null) {
+ if (a.hasValue(R.styleable.SearchViewWithHistory_searchBackground)) {
+ setBackground(a.getDrawable(R.styleable.SearchViewWithHistory_searchBackground));
+ }
+
+ if (a.hasValue(R.styleable.SearchViewWithHistory_searchCloseIcon)) {
+ setCloseIcon(a.getDrawable(R.styleable.SearchViewWithHistory_searchCloseIcon));
+ }
+
+ if (a.hasValue(R.styleable.SearchViewWithHistory_searchBackIcon)) {
+ setBackIcon(a.getDrawable(R.styleable.SearchViewWithHistory_searchBackIcon));
+ }
+
+ a.recycle();
+ }
+ }
+
+ private void initiateView() {
+ LayoutInflater.from(mContext).inflate(R.layout.search_view, this, true);
+ mSearchLayout = findViewById(R.id.search_layout);
+
+ mSearchTopBar = mSearchLayout.findViewById(R.id.search_top_bar);
+ mSearchSrcTextView = (EditText) mSearchLayout.findViewById(R.id.searchTextView);
+ mBackBtn = (ImageButton) mSearchLayout.findViewById(R.id.action_up_btn);
+ mEmptyBtn = (ImageButton) mSearchLayout.findViewById(R.id.action_empty_btn);
+ mTintView = mSearchLayout.findViewById(R.id.transparent_view);
+
+
+ mSearchSrcTextView.setOnClickListener(mOnClickListener);
+ mBackBtn.setOnClickListener(mOnClickListener);
+ mEmptyBtn.setOnClickListener(mOnClickListener);
+ mTintView.setOnClickListener(mOnClickListener);
+
+ initSearchView();
+
+ mHistory = new HistoryEditText(mContext, new int[] {
+ R.id.action_history_btn},
+ mSearchSrcTextView );
+ }
+
+ private void initSearchView() {
+ mSearchSrcTextView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ onSubmitQuery();
+ return true;
+ }
+ });
+
+ mSearchSrcTextView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mUserQuery = s;
+ startFilter(s);
+ SearchViewWithHistory.this.onTextChanged(s);
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mSearchSrcTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ showKeyboard(mSearchSrcTextView);
+ }
+ }
+ });
+ }
+
+ /** called every time the text changes */
+ private void startFilter(CharSequence s) {
+ /*
+ if (mAdapter != null && mAdapter instanceof Filterable) {
+ ((Filterable) mAdapter).getFilter().filter(s, SearchViewWithHistory.this);
+ }
+ */
+ }
+
+ private final OnClickListener mOnClickListener = new OnClickListener() {
+
+ public void onClick(View v) {
+ if (v == mBackBtn) {
+ closeSearch();
+ } else if (v == mEmptyBtn) {
+ mSearchSrcTextView.setText(null);
+ } else if (v == mTintView) {
+ closeSearch();
+ }
+ }
+ };
+
+ private void onTextChanged(CharSequence newText) {
+ CharSequence text = mSearchSrcTextView.getText();
+ mUserQuery = text;
+ boolean hasText = !TextUtils.isEmpty(text);
+ if (hasText) {
+ mEmptyBtn.setVisibility(VISIBLE);
+ } else {
+ mEmptyBtn.setVisibility(GONE);
+ }
+
+ if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)) {
+ mOnQueryChangeListener.onQueryTextChange(newText.toString());
+ }
+ mOldQueryText = newText.toString();
+ }
+
+ private void onSubmitQuery() {
+ CharSequence query = mSearchSrcTextView.getText();
+ if (query != null && TextUtils.getTrimmedLength(query) > 0) {
+ if (mOnQueryChangeListener == null || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
+ closeSearch();
+ mSearchSrcTextView.setText(null);
+ }
+ }
+ }
+
+ public void hideKeyboard(View view) {
+ InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+
+ public void showKeyboard(View view) {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1 && view.hasFocus()) {
+ view.clearFocus();
+ }
+ view.requestFocus();
+ InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(view, 0);
+ }
+
+ //Public Attributes
+
+ @Override
+ public void setBackground(Drawable background) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ mSearchTopBar.setBackground(background);
+ } else {
+ mSearchTopBar.setBackgroundDrawable(background);
+ }
+ }
+
+ @Override
+ public void setBackgroundColor(int color) {
+ mSearchTopBar.setBackgroundColor(color);
+ }
+
+ public void setTextColor(int color) {
+ mSearchSrcTextView.setTextColor(color);
+ }
+
+ public void setHintTextColor(int color) {
+ mSearchSrcTextView.setHintTextColor(color);
+ }
+
+ public void setHint(CharSequence hint) {
+ mSearchSrcTextView.setHint(hint);
+ }
+
+ public void setCloseIcon(Drawable drawable) {
+ mEmptyBtn.setImageDrawable(drawable);
+ }
+
+ public void setBackIcon(Drawable drawable) {
+ mBackBtn.setImageDrawable(drawable);
+ }
+
+ public void setCursorDrawable(int drawable) {
+ try {
+ // https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564
+ Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
+ f.setAccessible(true);
+ f.set(mSearchSrcTextView, drawable);
+ } catch (Exception ignored) {
+ Log.e("MaterialSearchView", ignored.toString());
+ }
+ }
+
+ //Public Methods
+
+ /**
+ * Submit the query as soon as the user clicks the item.
+ *
+ * @param submit submit state
+ */
+ public void setSubmitOnClick(boolean submit) {
+ this.submit = submit;
+ }
+
+ /**
+ * Calling this will set the query to search text box. if submit is true, it'll submit the query.
+ *
+ * @param query
+ * @param submit
+ */
+ public void setQuery(CharSequence query, boolean submit) {
+ mSearchSrcTextView.setText(query);
+ if (query != null) {
+ mSearchSrcTextView.setSelection(mSearchSrcTextView.length());
+ mUserQuery = query;
+ }
+ if (submit && !TextUtils.isEmpty(query)) {
+ onSubmitQuery();
+ }
+ }
+
+ /**
+ * Call this method and pass the menu item so this class can handle click events for the Menu Item.
+ *
+ * @param menuItem
+ */
+ public void setMenuItem(final MenuItem menuItem) {
+ this.mMenuItem = menuItem;
+ mMenuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (menuItem.getActionView() != null) {
+ menuItem.expandActionView();
+ }
+ showSearch();
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Return true if search is open
+ *
+ * @return
+ */
+ public boolean isSearchOpen() {
+ return mIsSearchOpen;
+ }
+
+ /**
+ * Open Search View. This will animate the showing of the view.
+ */
+ public void showSearch() {
+ showSearch(false);
+ }
+
+ /**
+ * Open Search View. If animate is true, Animate the showing of the view.
+ *
+ * @param animate true for animate
+ */
+ public void showSearch(boolean animate) {
+ if (isSearchOpen()) {
+ return;
+ }
+
+ //Request Focus
+ mSearchSrcTextView.setText(null);
+ mSearchSrcTextView.requestFocus();
+
+ if (animate) {
+ setVisibleWithAnimation();
+
+ } else {
+ mSearchLayout.setVisibility(VISIBLE);
+ if (mSearchViewListener != null) {
+ mSearchViewListener.onSearchViewShown();
+ }
+ }
+ mIsSearchOpen = true;
+ }
+
+ private void setVisibleWithAnimation() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mSearchLayout.setVisibility(View.VISIBLE);
+ // AnimationUtil.reveal(mSearchTopBar, animationListener);
+
+ } else {
+ // AnimationUtil.fadeInView(mSearchLayout, mAnimationDuration, animationListener);
+ }
+ }
+
+ /**
+ * Close search view.
+ */
+ public void closeSearch() {
+ if (mHistory != null) mHistory.saveHistory();
+
+ clearFocus();
+ if (!isSearchOpen()) {
+ return;
+ }
+
+ mIsSearchOpen = false;
+ if (mSearchViewListener != null) {
+ mSearchViewListener.onSearchViewClosed();
+ }
+ // mSearchSrcTextView.setText(null);
+
+ if ((mMenuItem != null) && (mMenuItem.getActionView() != null)) {
+ mMenuItem.collapseActionView();
+ }
+
+ mSearchLayout.setVisibility(GONE);
+
+ }
+
+ /**
+ * Set this listener to listen to Query Change events.
+ *
+ * @param listener
+ */
+ public void setOnQueryTextListener(OnQueryTextListener listener) {
+ mOnQueryChangeListener = listener;
+ }
+
+ /**
+ * Set this listener to listen to Search View open and close events
+ *
+ * @param listener
+ */
+ public void setOnSearchViewListener(SearchViewListener listener) {
+ mSearchViewListener = listener;
+ }
+
+ /**
+ * Ellipsize suggestions longer than one line.
+ *
+ * @param ellipsize
+ */
+ public void setEllipsize(boolean ellipsize) {
+ this.ellipsize = ellipsize;
+ }
+
+ @Override
+ public void onFilterComplete(int count) {
+ if (count > 0) {
+ //showSuggestions();
+ } else {
+ //dismissSuggestions();
+ }
+ }
+
+ @Override
+ public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+ // Don't accept focus if in the middle of clearing focus
+ if (mClearingFocus) return false;
+ // Check if SearchView is focusable.
+ if (!isFocusable()) return false;
+ return mSearchSrcTextView.requestFocus(direction, previouslyFocusedRect);
+ }
+
+ @Override
+ public void clearFocus() {
+ mClearingFocus = true;
+ hideKeyboard(this);
+ super.clearFocus();
+ mSearchSrcTextView.clearFocus();
+ mClearingFocus = false;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+
+ mSavedState = new SavedState(superState);
+ mSavedState.query = mUserQuery != null ? mUserQuery.toString() : null;
+ mSavedState.isSearchOpen = this.mIsSearchOpen;
+
+ return mSavedState;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (!(state instanceof SavedState)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ mSavedState = (SavedState) state;
+
+ if (mSavedState.isSearchOpen) {
+ showSearch(false);
+ setQuery(mSavedState.query, false);
+ }
+
+ super.onRestoreInstanceState(mSavedState.getSuperState());
+ }
+
+ /**
+ * Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}
+ * is expanded.
+ *
+ * @param item Item that was expanded
+ * @return true if the item should expand, false if expansion should be suppressed.
+ */
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ return true;
+ }
+
+ /**
+ * Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}
+ * is collapsed.
+ *
+ * @param item Item that was collapsed
+ * @return true if the item should collapse, false if collapsing should be suppressed.
+ */
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ return true;
+ }
+
+ public String getFilterValue() {
+ return (mSearchSrcTextView == null) ? "" : mSearchSrcTextView.getText().toString();
+ }
+
+ static class SavedState extends BaseSavedState {
+ String query;
+ boolean isSearchOpen;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ this.query = in.readString();
+ this.isSearchOpen = in.readInt() == 1;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeString(query);
+ out.writeInt(isSearchOpen ? 1 : 0);
+ }
+
+ //required field that makes Parcelables from a Parcel
+ public static final Creator CREATOR =
+ new Creator() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+
+ // for debugging
+ @Override
+ public String toString() {
+ return StringUtils.appendMessage(null,
+ this.getClass().getSimpleName(),": open",isSearchOpen, query).toString();
+ }
+
+ }
+
+ public interface OnQueryTextListener {
+
+ /**
+ * Called when the user submits the query. This could be due to a key press on the
+ * keyboard or due to pressing a submit button.
+ * The listener can override the standard behavior by returning true
+ * to indicate that it has handled the submit request. Otherwise return false to
+ * let the SearchView handle the submission by launching any associated intent.
+ *
+ * @param query the query text that is to be submitted
+ * @return true if the query has been handled by the listener, false to let the
+ * SearchView perform the default action.
+ */
+ boolean onQueryTextSubmit(String query);
+
+ /**
+ * Called when the query text is changed by the user.
+ *
+ * @param newText the new content of the query text field.
+ * @return false if the SearchView should perform the default action of showing any
+ * suggestions if available, true if the action was handled by the listener.
+ */
+ boolean onQueryTextChange(String newText);
+ }
+
+ public interface SearchViewListener {
+ void onSearchViewShown();
+
+ void onSearchViewClosed();
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-hdpi/album.png b/app/src/main/res/drawable-hdpi/album.png
new file mode 100644
index 00000000..a7a44345
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/album.png differ
diff --git a/app/src/main/res/drawable-hdpi/exif_edit.png b/app/src/main/res/drawable-hdpi/exif_edit.png
index ba88b32d..724a8b01 100644
Binary files a/app/src/main/res/drawable-hdpi/exif_edit.png and b/app/src/main/res/drawable-hdpi/exif_edit.png differ
diff --git a/app/src/main/res/drawable-hdpi/foto_gallery.png b/app/src/main/res/drawable-hdpi/foto_gallery.png
index d6bf8a3e..ec30a2c2 100644
Binary files a/app/src/main/res/drawable-hdpi/foto_gallery.png and b/app/src/main/res/drawable-hdpi/foto_gallery.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_navigation_arrow_back.png b/app/src/main/res/drawable-hdpi/ic_action_navigation_arrow_back.png
new file mode 100644
index 00000000..ffc68b4d
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_navigation_arrow_back.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_menu_find_holo_dark.png b/app/src/main/res/drawable-hdpi/ic_menu_find_holo_dark.png
new file mode 100644
index 00000000..b981a4da
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_menu_find_holo_dark.png differ
diff --git a/app/src/main/res/drawable-hdpi/rule.png b/app/src/main/res/drawable-hdpi/rule.png
new file mode 100644
index 00000000..6e7348d9
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/rule.png differ
diff --git a/app/src/main/res/drawable-mdpi/album.png b/app/src/main/res/drawable-mdpi/album.png
new file mode 100644
index 00000000..d68ed22b
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/album.png differ
diff --git a/app/src/main/res/drawable-mdpi/exif_edit.png b/app/src/main/res/drawable-mdpi/exif_edit.png
index 1b89aa16..727b4065 100644
Binary files a/app/src/main/res/drawable-mdpi/exif_edit.png and b/app/src/main/res/drawable-mdpi/exif_edit.png differ
diff --git a/app/src/main/res/drawable-mdpi/foto_gallery.png b/app/src/main/res/drawable-mdpi/foto_gallery.png
index c0ed8506..1d502de0 100644
Binary files a/app/src/main/res/drawable-mdpi/foto_gallery.png and b/app/src/main/res/drawable-mdpi/foto_gallery.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_navigation_arrow_back.png b/app/src/main/res/drawable-mdpi/ic_action_navigation_arrow_back.png
new file mode 100644
index 00000000..b2948bf0
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_navigation_arrow_back.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_menu_find_holo_dark.png b/app/src/main/res/drawable-mdpi/ic_menu_find_holo_dark.png
new file mode 100644
index 00000000..45f8fd3f
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_menu_find_holo_dark.png differ
diff --git a/app/src/main/res/drawable-mdpi/rule.png b/app/src/main/res/drawable-mdpi/rule.png
new file mode 100644
index 00000000..ade07834
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/rule.png differ
diff --git a/app/src/main/res/drawable-xhdpi/album.png b/app/src/main/res/drawable-xhdpi/album.png
new file mode 100644
index 00000000..2f5f52eb
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/album.png differ
diff --git a/app/src/main/res/drawable-xhdpi/exif_edit.png b/app/src/main/res/drawable-xhdpi/exif_edit.png
index 3dc4f36f..d2a6e5f9 100644
Binary files a/app/src/main/res/drawable-xhdpi/exif_edit.png and b/app/src/main/res/drawable-xhdpi/exif_edit.png differ
diff --git a/app/src/main/res/drawable-xhdpi/foto_gallery.png b/app/src/main/res/drawable-xhdpi/foto_gallery.png
index ca88f4a3..f1189a49 100644
Binary files a/app/src/main/res/drawable-xhdpi/foto_gallery.png and b/app/src/main/res/drawable-xhdpi/foto_gallery.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_navigation_arrow_back.png b/app/src/main/res/drawable-xhdpi/ic_action_navigation_arrow_back.png
new file mode 100644
index 00000000..42fb563e
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_navigation_arrow_back.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_menu_find_holo_dark.png b/app/src/main/res/drawable-xhdpi/ic_menu_find_holo_dark.png
new file mode 100644
index 00000000..3ede9e23
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_menu_find_holo_dark.png differ
diff --git a/app/src/main/res/drawable-xhdpi/rule.png b/app/src/main/res/drawable-xhdpi/rule.png
new file mode 100644
index 00000000..c9d33dcc
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/rule.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/album.png b/app/src/main/res/drawable-xxhdpi/album.png
new file mode 100644
index 00000000..790a79a7
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/album.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/exif_edit.png b/app/src/main/res/drawable-xxhdpi/exif_edit.png
index 0be28450..1441b26d 100644
Binary files a/app/src/main/res/drawable-xxhdpi/exif_edit.png and b/app/src/main/res/drawable-xxhdpi/exif_edit.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/foto_gallery.png b/app/src/main/res/drawable-xxhdpi/foto_gallery.png
index 4c8591bb..ea18e5df 100644
Binary files a/app/src/main/res/drawable-xxhdpi/foto_gallery.png and b/app/src/main/res/drawable-xxhdpi/foto_gallery.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_navigation_arrow_back.png b/app/src/main/res/drawable-xxhdpi/ic_action_navigation_arrow_back.png
new file mode 100644
index 00000000..5f11c473
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_navigation_arrow_back.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/rule.png b/app/src/main/res/drawable-xxhdpi/rule.png
new file mode 100644
index 00000000..734d19de
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/rule.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_navigation_arrow_back.png b/app/src/main/res/drawable-xxxhdpi/ic_action_navigation_arrow_back.png
new file mode 100644
index 00000000..877f43e5
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_navigation_arrow_back.png differ
diff --git a/app/src/main/res/drawable/color_cursor_white.xml b/app/src/main/res/drawable/color_cursor_white.xml
new file mode 100644
index 00000000..c752e965
--- /dev/null
+++ b/app/src/main/res/drawable/color_cursor_white.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_geo_edit.png b/app/src/main/res/drawable/ic_geo_edit.png
index afa0be32..c9c711ed 100644
Binary files a/app/src/main/res/drawable/ic_geo_edit.png and b/app/src/main/res/drawable/ic_geo_edit.png differ
diff --git a/app/src/main/res/layout-sw600dp/fragment_directory.xml b/app/src/main/res/layout-sw600dp/fragment_directory.xml
index 0bdec786..d03fd0d4 100644
--- a/app/src/main/res/layout-sw600dp/fragment_directory.xml
+++ b/app/src/main/res/layout-sw600dp/fragment_directory.xml
@@ -1,7 +1,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_context_dirpicker.xml b/app/src/main/res/menu/menu_context_dirpicker.xml
index cede6a66..12f2e79e 100644
--- a/app/src/main/res/menu/menu_context_dirpicker.xml
+++ b/app/src/main/res/menu/menu_context_dirpicker.xml
@@ -25,6 +25,10 @@
android:visible="true"
android:orderInCategory="10"
android:showAsAction="never" />
+
+
+
+
+
-
-
+
diff --git a/app/src/main/res/menu/menu_gallery_locked.xml b/app/src/main/res/menu/menu_gallery_locked.xml
deleted file mode 100644
index 6c8fb653..00000000
--- a/app/src/main/res/menu/menu_gallery_locked.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/menu/menu_gallery_non_multiselect.xml b/app/src/main/res/menu/menu_gallery_non_multiselect.xml
index 473451f0..aa23a469 100644
--- a/app/src/main/res/menu/menu_gallery_non_multiselect.xml
+++ b/app/src/main/res/menu/menu_gallery_non_multiselect.xml
@@ -19,16 +19,29 @@
* this program. If not, see
*/
-->
-
+
+
+ android:showAsAction="ifRoom"
+ />
-
-
-
+
-
+ android:orderInCategory="2" android:showAsAction="always" android:visible="true" />
+ android:orderInCategory="2" android:showAsAction="always" android:visible="true" />
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/menu_map_context.xml b/app/src/main/res/menu/menu_map_context.xml
index 97cc2fa0..391e96f3 100644
--- a/app/src/main/res/menu/menu_map_context.xml
+++ b/app/src/main/res/menu/menu_map_context.xml
@@ -1,7 +1,7 @@
+
+
+ A Photo Managerwith 'A Photo Map' in 'AndroFotoFinder'
+
+
+
+
+مدير للصور المحلية يقوم بـ: بحث ونسخ وتعديل الصور ووضعها في معرض صور او في خريطة
+
+
+
+Features:
+
+
+ - Fast
+ بحث عن الصور by
+
+ علامات(الكلمات الرئيسية),
+
+ مجلد, التاريخ,
+
+ الخريطة الجغرافية, العنوان, التقييم, ...
+
- عرض النتيجة في
+
+ المعرض or
+
+ الخريطة الجغرافية من openstreetmap.
+
-
+ عرض بالتفاصيل يحتوي على ميزة تكبير الصور, والسحب للصورة التالية والسابقة.
+
- مدير ملفات مدمج للبحث وفرز, عرض, نسخ, ارسال, وحذف الصور, ... .
+
+
-
+ تعديل ملف بيانات exif: التاريخ, والعنوان, والوصف,
+
+ علامات(الكلمات الرئيسية), geo, التقييم, ....
+
- Optional Folder-Rule based autoprocessing:
+ القيام بمعالجة الصور باعادة التسمية, وضع العلامات, والموقع الجغرافي, والعنوان تلقائيا ... عند نسخهم وقصهم .
+
- وضع "خاص" في الصور لاخفائهم من بقية برامج عرض الصور.
+
- In Protected/Pinned Mode
+ الأوامر الحساسة مثل تعديل ونسخ, حذف, مشاركة, الاعدادات, وتغيير اختيار الصور, موقفة فبامكانك اعطاء هاتفك للغير بدون خوف.
+
- يمكنه تحمل مجموعة كبيرة من الصور (فوق 20000 صورة و 1000 مجلد).
+
- يستخدم مزود محتويات Android. لا يحتاج الى العثور على الصور .
+
- كاشف اضافي على ملفات Exif, IPTC, XMP
+
+
+يتطلب صلاحيات النظام الاتية:
+
+
+ - الانترنت: لتحميل ملفات الخرائط من سيرفر openstreetmap
+
- ACCESS_NETWORK_STATE and ACCESS_WIFI_STATE: لمعرفة امكانية اتصال الجهاز
+
- WRITE_EXTERNAL_STORAGE لتخزين ملفات الخريطة لفرز الصور لها
+
- READ_LOGS لقراءة وتخزين ملف سجلات الانهيار.
+
+
+"]]>
+
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 0ea47a74..180a35b2 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -2,9 +2,7 @@
- \"A Photo Manager%1$s\" الم
+ عن \"A Photo Manager%1$s\"
مرشح الخريطة
الخلفية
- علامات مرجعية تمت إزالتها %1$s.
+ الألبوم
+ علامة مرجعية تمت إزالتها %1$s.
لا يمكن إزالة العلامة المرجعية %s$1.
هل تريد إزالة هذه العلامة المرجعية؟
تم توليده في %3$s بـ %1$s إصدار %2$s شاهد
https://github.com/k3b/APhotoManager/wiki/intentapi#sql لمزيد من التفاصيل
- تحميل العلامات المرجعية من…
حفظ العلامة المرجعية كـ …
تغيير
ايقاف مؤقت
تم نسخ %1$d من %2$d ملفات
إزالة
"هل أنت متأكد من أنك تود إزالة هذه الملفات كليًا؟
-%1$s"
- إزالة صورة(صور)؟
+%1$s
+هذه العملية لا يمكن استرجاعها
+"
+ إزالة الصورة(الصور)؟
إزالة %1$d من %2$d ملفات
نسخ المكان المقصود
تحريك المكان المقصود
@@ -63,7 +63,7 @@ https://github.com/k3b/APhotoManager/wiki/intentapi#sql لمزيد من التف
تغير المعلومات الجغرافية للصور المختارة
لم يتم إيجاد المنتقي الجغرافي
اختيار جغرافي جديد للصور
- "خطأ SQL %1$s\n\n%2$s"
+ خطأ SQL %1$s\n\n%2$s
Sql غير صالح. إعادة تحميل الافتراضي
خطأ في النظام. موجود
الملف %1$s موجود بالفعل. .\n هل بالفعل تريد استبداله؟
@@ -73,10 +73,10 @@ https://github.com/k3b/APhotoManager/wiki/intentapi#sql لمزيد من التف
"تحميل (%1$d) …"
صور %1$d المحدثة.
التاريخ
- لات
- لون
+ خط العرض
+ خط الطول
المسار
- بدون معلومات جيو
+ بدون معلومات جغرافية
مجلد_جديد
إنشاء مجلد
لا يمكن إنشاء %1$s
@@ -97,11 +97,12 @@ https://github.com/k3b/APhotoManager/wiki/intentapi#sql لمزيد من التف
المختارة فقط
الاختيار: أضف كل المرئي
الاختيار: إزالة كل المرئي
- مجلد العلامات المرجعية
التشخيصات
... محتويات SQL التي تم تنفيذها.
... محتويات عناصر gridview/listview.
استهلاك ذاكرة LogCat
+ قراءة وكتابة بيانات xmp و jpg بواسطة LogCat
+ … of 3rd party libraries.
متفرقات LogCat.
... محتويات رسائل السجل الأخرى.
محو LogCat
@@ -114,8 +115,8 @@ https://github.com/k3b/APhotoManager/wiki/intentapi#sql لمزيد من التف
اللغة
افتراضيات النظام
الحد الأقصي لعلامات التحديد بالخريطة
- تعيين عداد تاريخ الجيو
- تعيين ملف تاريخ الجيو
+ تعيين عداد التاريخ الجغرافي
+ تعيين ملف التاريخ الجغرافي
إزالة الاختيارات المتعددة بعد الإجراء
نسخ، تحريك، إعادة تسمية، إلخ.
الإعدادات
@@ -169,4 +170,58 @@ https://github.com/k3b/APhotoManager/wiki/intentapi#sql لمزيد من التف
تحديث الوسوم في الصور المتأثرة
الشريط-الطويل في قائمة فتح اسماء الوسوم
النشاطات، الاشخاص، الاماكن، الموضوعات، المشروعات
+ كتابة التغييرات إلى
+ jpg و xmp (إنشاء إذا لم يتم العثور عليه)
+ jpg (و xmp ان لم يجد)
+ ملف jpg فقط
+ ملف xmp فقط
+ اسم ملف بيانات Xmp طويل؟
+ طويل: ملف.jpg.xmp; قصير: ملف.xmp
+
+ تحميل السياق ...
+
+ تعديل Exif
+ العنوان
+ الوصف
+ التقييم
+
+ التقييم
+ آخر تعديل
+
+ تعديل معالجة الصور التلقائية
+ نمط اسم الملف
+ Exif
+
+ "%1$s %2$d Photos\n\t%3$s (%4$s), …\nTo %5$s\n\t%6$s"
+
+ الحجم
+ العرض
+
+ الوضوح
+
+ التطبيق محمي/مثبت
+ التطبيق غير محمي/مثبت
+
+ اعادة تسمية الصور الخاصة من .jpg الى .jpg-p
+ مسح
+ فتح في مدير الملفات
+
+ شريط البحث
+ منتقي التاريخ: استخدام العقود
+ أي وضع 2010، 2011،... 2019 اسفل 2010 *
+ مرشح التاريخ
+
+ تحرير الألبوم الافتراضي
+
+ مجلد العلامات المرجعية
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 5bf52bfa..e0e2f8d4 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -24,12 +24,13 @@
Über \"A Photo Manager%1$s\"
Karten-Filter
Hintergrund
- Lesezeichen %1$s gelöscht.
- Kann Lesezeichen %1$s nicht löschen.
- Lesezeichen löschen?
+ Album
+ Album %1$s gelöscht.
+ Kann Album %1$s nicht löschen.
+ Album löschen?
Erstellt am %3$s mit \'%1$s\' Version %2$s. weitere Informationen finden Sie unter https://github.com/k3b/APhotoManager/wiki/intentapi#sql
- Lade Lesezeichen aus…
- Lesezeichen speichern unter...
+ Lade Album aus…
+ Album speichern unter...
Ändern
Anhalten
%1$d/%2$d Dateien kopiert
@@ -94,7 +95,6 @@
Nur ausgewählte
Auswahl: alle sichtbaren
Auswahl: alle sichtbaren abwählen
- Ordner für Lesezeichen
… Fremdkomponenten.
LogCat Speicherverbrauch
@@ -234,13 +234,30 @@ Verstecken kann über das 'Media-Scanner' Gallery-Menü rückgängig gemacht wer
Schutz/Fixierung aufheben
- PRIVATE ...jpg wird zu ....jpg-p
+ Private *.jpg wird zu *.jpg-p
Leeren
In Filemanager öffnen
+
+ Schnellsuche
+
+ Datum Picker: nach Jahrzehnten gruppieren.
+ Bsp: 2010, 2011,...2019 unterhalb von 2010*
+ Datum Filter
+
+
+ Virtelles Album bearbeiten
-
+
+
+ Ordner für Lesezeichen
+
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 5dbc3930..f173eb01 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -2,7 +2,7 @@
Aplicación protegida/marcada
Aplicación desprotegida/desmarcada
-
+
+ Cambiar extensión *.jpg a *.jpg-p
+ Borrar
+ Abrir en Administrador de archivos
+
+ Buscador
+ Fechas: use décadas
+ Ejemplo escriba 2010, 2011,...2019 a continuación 2010*
+ Filtro de fechas
+
+ Editar Album Virtual
+
+ Carpeta de marcadores
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 31c79cc4..eee80cb8 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -24,11 +24,11 @@
À propos de \"A Photo Manager%1$s\"
Sélectionnez une zone sur la carte
Arrière plan
+ Album
Supprimer le signet %1$s.
Impossible de supprimer le signet %1$s.
Supprimer ce signet ?
Générer sur %3$s avec \'%1$s\' Version %2$s. Allez sur https://github.com/k3b/APhotoManager/wiki/intentapi#sql pour plus de détails
- Charger le Signet de …
Enregistrer le signet sous …
Modifier
Suspendre
@@ -93,7 +93,6 @@
Uniquement les Sélectionnée(s)
Sélection : Ajouter tous les visibles
Sélection : Supprimer tous les visibles
- Dossier de signet
Diagnostiques
Enregistrer les requêtes SQL.
Enregistrer les évènements de la grille et des vues.
@@ -167,7 +166,7 @@
jpg (et xmp si existant)
fichier jpg uniquement
fichier xmp uniquement
- Taille du fichier pour les métadonnées Xmp ?
+ Longueur du nom de fichier Xmp?
Long : file.jpg.xmp; Court : file.xmp
Charger le Contexte...
@@ -198,10 +197,22 @@
Appli protégée/épinglée
Appli non-protégée/non-épinglée
- Renommez les images JPG privés de .jpg à jpg-p
-
+ Barre de recherche
+ Sélecteur de dates : utilisez des décennies
+ Par ex. mettez 2010, 2011,...2019 dessous 2010*
+ Filtre par date
+
+ Modifier l’Album Virtuel
+
- Effacer
- Ouvert dans le gestionnaire de fichiers
+ Dossier de signet
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index bc516dbf..9b1497db 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1,8 +1,8 @@
-
+
- Info su \"A Photo Manager%1$s\"
- Seleziona l\'area sulla mappa
- Sfondo
- Segnalibro eliminato %1$s.
- Impossibile eliminare il segnalibro %1$s.
- Eliminare questo segnalibro?
- Generato su %3$s con \'%1$s\' Versione %2$s. Vedi https://github.com/k3b/APhotoManager/wiki/intentapi#sql per dettagli
- Carica segnalibro da …
- Salva segnalibro come …
- Cambia
- Pausa
- Copiati %1$d/%2$d file
- Elimina
- "Sei sicuro di volere eliminare in modo definitivo questi file? %1$sQuesta operazione non può essere annullata."
- Eliminare la/le foto?
- Eliminati %1$d/%2$d file
- Copia destinazione
- Sposta destinazione
- Dettagli
- Scegli un editor foto
- Nessun editor foto trovato
- Modifica
- protezione da scrittura per \'%1$s\'.\n\n \'%2$s\' non è possibile.
- \'%1$s\' non è una data valida.
- \'%1$s\' non è una coordinata valida.
- Filtro
- Seleziona %1$s
- Caricamento cartella %1$s fallito
- Filtra cartella
- Foto
- Mostra sulla mappa
- vedi in app geo esterna
- Imposta geo
- Modifica delle informazioni geo sulle foto selezionate
- Nessun selettore geo trovato
- Seleziona nuova geo per la foto
- Errore SQL \'%1$s\'\n\n%2$s
- SQL non valido. Ripristino
- Errore di sistema. Uscita
- Il file \'%1$s\' esiste già.\n Vuoi veramente sovrascriverlo?
- Impossibile rinominare il file \'%1$s\'.
- Nessuna foto trovata per %1$s.
- Foto non ancora trovata nel database %1$s. Scansione di %2$d nuovi file. La foto mancante sarà disponibile a breve.
- "Caricamento (%1$d) …"
- Aggiornate %1$d foto.
- Data
- Lat
- Lon
- Percorso
- Senza info geo
- nuova_cartella
- Crea cartella
- Impossibile creare %1$s
- Creato %1$s
- Altro ...
- Sposta
- Spostati %1$d/%2$d file
- Tile Mappe (c)
+ Info su \"A Photo Manager%1$s\"
+ Seleziona l\'area sulla mappa
+ Sfondo
+ Album
+ Segnalibro eliminato %1$s.
+ Impossibile eliminare il segnalibro %1$s.
+ Eliminare questo segnalibro?
+ Generato su %3$s con \'%1$s\' Versione %2$s. Vedi https://github.com/k3b/APhotoManager/wiki/intentapi#sql per dettagli
+ Salva segnalibro come …
+ Cambia
+ Pausa
+ Copiati %1$d/%2$d file
+ Elimina
+ Sei sicuro di volere eliminare in modo definitivo questi file? %1$sQuesta operazione non può essere annullata.
+ Eliminare la/le foto?
+ Eliminati %1$d/%2$d file
+ Copia destinazione
+ Sposta destinazione
+ Dettagli
+ Scegli un editor foto
+ Nessun editor foto trovato
+ Modifica
+ protezione da scrittura per \'%1$s\'.\n\n \'%2$s\' non è possibile.
+ \'%1$s\' non è una data valida.
+ \'%1$s\' non è una coordinata valida.
+ Filtro
+ Seleziona %1$s
+ Caricamento cartella %1$s fallito
+ Filtra cartella
+ Foto
+ Mostra sulla mappa
+ vedi in app geo esterna
+ Imposta geo
+ Modifica delle informazioni geo sulle foto selezionate
+ Nessun selettore geo trovato
+ Seleziona nuova geo per la foto
+ Errore SQL \'%1$s\'\n\n%2$s
+ SQL non valido. Ripristino
+ Errore di sistema. Uscita
+ Il file \'%1$s\' esiste già.\n Vuoi veramente sovrascriverlo?
+ Impossibile rinominare il file \'%1$s\'.
+ Nessuna foto trovata per %1$s.
+ Foto non ancora trovata nel database %1$s. Scansione di %2$d nuovi file. La foto mancante sarà disponibile a breve.
+ "Caricamento (%1$d) …"
+ Aggiornate %1$d foto.
+ Data
+ Lat
+ Lon
+ Percorso
+ Senza info geo
+ nuova_cartella
+ Crea cartella
+ Impossibile creare %1$s
+ Creato %1$s
+ Altro ...
+ Sposta
+ Spostati %1$d/%2$d file
+ Tile Mappe (c)
OpenStreetMap
- Sovrascrivere?
- Rinomina
- Rinominati %1$d/%2$d file
- Dove inizio la scansione?
- Impossibile elaborare le foto mentre lo Scanner Multimediale di Android è occupato. Riprova più tardi.
- Scanner multimediale
- Aggiornati %1$d elementi del Database multimediale
- %1$d selezionati
- Seleziona una cartella con immagini per attivare l\'OK
- Solo selezionati
- Selezione: aggiungi tutte quelle visibili
- Selezione: rimuovi tutte quelle visibili
- Cartella di segnalibro
- Diagnostica
- … contenente SQL eseguito.
- ... contenente elementi gridview/listview.
- LogCat consumo di memoria
- LogCat misc.
- … contenente altri messaggi di log.
- Svuota LogCat
- Salva LogCat in un file
- Cartella Errorlog
- Auto-nascondi barra azioni dopo tot millisecondi
- Visualizzazione foto iniziale con qualità migliore (lento, usa più memoria)
- Migliora qualità vista foto iniziale
- Intervallo presentazione in millisecondi
- Language
- (device language)
- Max selettori sulla mappa
- Imposta conteggio cronologia geo
- Imposta file cronologia geo
- Cancella la selezione
- … dopo Copia/Sposta/Rinomina/… .
- Impostazioni
- Nessun provider Invia/Condividi trovato
- Invio multiplo: numero di elementi eccessivo
- Condividi/Invia
- Mostra in nuova galleria
- Presentazione
- Data
- Cartella
- Nome
- Lunghezza
- Non ordinato
- Luogo
- Ordina
- Aggiornati %1$d/%2$d file
- Adatta alla finestra
- Foto grande larghezza/altezza
- se la larghezza-altezza della foto è maggiore di questo, mostra miniatura nella visualizzazione dettagliata. (migliore memoria, veloce, ma scarsa qualità)
- Cartella miniatura
- Usa i file di mappa locali *.map
- che hai scaricato manualmente tramite pc
- Cartella file di mappa *.map
- Mostra foto
- Ripara duplicati
- Scegli geo dalla foto
- Scegli geo dalla mappa
- Nascondi immagini
- "Sei sicuro?Vuoi rendere tutti i file multimediali (foto, video, audio) sotto %1$sinvisibili ai database, app galleria e media scanner?Puoi annullare l\'azione avviando il media scanner dal menu galleria."
- Parte della cartella o nome file. % = Carattere jolly
- Trova
- Parte del tag, titolo, descrizione o percorso
- Immagini pubbliche
- Immagini private
- Senza tag
- + Tag
- -Tag
- Tag
- Filtro tag
- Digita qui per filtrare...
- Nessun tag corrispondente trovato
- Imposta tag
- Crea nuovo Tag
- nuovo_tag
- Eliminazione dei contenuti
- Aggiorna i tag nelle foto coinvolte
- Premendo a lungo il nome del tag apre il menu
- Attività,Persone,Luoghi,Temi,Progetti
+ Sovrascrivere?
+ Rinomina
+ Rinominati %1$d/%2$d file
+ Dove inizio la scansione?
+ Impossibile elaborare le foto mentre lo Scanner Multimediale di Android è occupato. Riprova più tardi.
+ Scanner multimediale
+ Aggiornati %1$d elementi del Database multimediale
+ %1$d selezionati
+ Seleziona una cartella con immagini per attivare l\'OK
+ Solo selezionati
+ Selezione: aggiungi tutte quelle visibili
+ Selezione: rimuovi tutte quelle visibili
+ Diagnostica
+ … contenente SQL eseguito.
+ ... contenente elementi gridview/listview.
+ LogCat consumo di memoria
+ LogCat jpg/xmp metadata read/write
+ … of 3rd party libraries.
+ LogCat misc.
+ … contenente altri messaggi di log.
+ Svuota LogCat
+ Salva LogCat in un file
+ Cartella Errorlog
+ Auto-nascondi barra azioni dopo tot millisecondi
+ Visualizzazione foto iniziale con qualità migliore (lento, usa più memoria)
+ Migliora qualità vista foto iniziale
+ Intervallo presentazione in millisecondi
+ Language
+ (device language)
+ Max selettori sulla mappa
+ Imposta conteggio cronologia geo
+ Imposta file cronologia geo
+ Cancella la selezione
+ … dopo Copia/Sposta/Rinomina/… .
+ Impostazioni
+ Nessun provider Invia/Condividi trovato
+ Invio multiplo: numero di elementi eccessivo
+ Condividi/Invia
+ Mostra in nuova galleria
+ Presentazione
+ Data
+ Cartella
+ Nome
+ Lunghezza
+ Non ordinato
+ Luogo
+ Ordina
+ Aggiornati %1$d/%2$d file
+ Adatta alla finestra
+ Foto grande larghezza/altezza
+ se la larghezza-altezza della foto è maggiore di questo, mostra miniatura nella visualizzazione dettagliata. (migliore memoria, veloce, ma scarsa qualità)
+ Cartella miniatura
+ Usa i file di mappa locali *.map
+ che hai scaricato manualmente tramite pc
+ Cartella file di mappa *.map
+ Mostra foto
+ Ripara duplicati
+ Scegli geo dalla foto
+ Scegli geo dalla mappa
+ Nascondi immagini
+ Sei sicuro?Vuoi rendere tutti i file multimediali (foto, video, audio) sotto %1$sinvisibili ai database, app galleria e media scanner?Puoi annullare l\'azione avviando il media scanner dal menu galleria.
+ Parte della cartella o nome file. % = Carattere jolly
+ Trova
+ Parte del tag, titolo, descrizione o percorso
+ Immagini pubbliche
+ Immagini private
+ Senza tag
+ + Tag
+ -Tag
+ Tag
+ Filtro tag
+ Digita qui per filtrare...
+ Nessun tag corrispondente trovato
+ Imposta tag
+ Crea nuovo Tag
+ nuovo_tag
+ Eliminazione dei contenuti
+ Aggiorna i tag nelle foto coinvolte
+ Premendo a lungo il nome del tag apre il menu
+ Attività,Persone,Luoghi,Temi,Progetti
+ Write changes to
+ jpg and xmp (Create if not found)
+ jpg (and xmp if exists)
+ jpg file only
+ xmp file only
+ Long Xmp Sidecar File Name?
+ Long: file.jpg.xmp; Short: file.xmp
+
+ Load Context ...
+
+ Edit Exif
+ Title
+ Description
+ Rating
+
+ Rating
+ Last Modified
+
+ Edit Photo Autoprocessing
+ Filename Pattern
+ Exif
+
+ "%1$s %2$d Photos\n\t%3$s (%4$s), …\nTo %5$s\n\t%6$s"
+
+ Size
+ Width
+
+ Visibility
+
+ App protected/pinned
+ App unprotect/unpinned
+
+ Rename private *.jpg to *.jpg-p
+ Clear
+ Open in Filemanager
+
+ Searchbar
+ Date Picker: use decades
+ I.E. Put 2010, 2011,...2019 below 2010*
+ Date Filter
+
+ Edit Virtual Album
+
+ Cartella di segnalibro
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 35e73890..4cf38310 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -2,7 +2,7 @@
+ Рейтинг
+ Последнее изменение
+
+ Размер
+
+ Видимость
+
+ Очистить
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index c4a50868..92d2bae2 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -2,7 +2,9 @@
固定应用窗口
应用窗口取消保护/固定
-
+
+ 重命名私人 *.jpg 至 *.jpg-p
+ 清除
+ 在文件管理器中打开
+
+ 搜索栏
+ 日期选取器: 使用十年
+ 例如: 将 2010、2011···2019 放置在 2010* 下
+ 日期筛选
+
+ 编辑虚拟相册
+
+ 书签文件夹
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 7ce840eb..efd38f41 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -8,5 +8,13 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 8b56346d..985a132a 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -24,4 +24,5 @@
#ffffff
#70ffffff
@android:color/black
+ #50000000
\ No newline at end of file
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index 1c2776ef..7a6a85d6 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -105,11 +105,12 @@ this program. If not, see
The code uses
History
@@ -178,8 +173,8 @@ this program. If not, see
Legal stuff
A Photo Manager (A(ndro)FotoFinder)
-
Copyright (c) 2015-2016 by k3b
-
Licensed under GPL, Version 3.0 or later.
+
Copyright (c) 2015-2018 by k3b
+
Licensed under GPL, Verion 3.0 or later.
Translations supported
crowdin.com
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a0faaa45..045453af 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -22,12 +22,12 @@
About \"A Photo Manager%1$s\"
Map Area Filter
Background
- Deleted bookmark %1$s.
- Cannot delete bookmark %1$s.
- Delete this bookmark?
+ Album
+ Deleted album %1$s.
+ Cannot delete album %1$s.
+ Delete this album?
Generated on %3$s with \'%1$s\' Version %2$s. See https://github.com/k3b/APhotoManager/wiki/intentapi#sql for details
- Load bookmark from …
- Save bookmark as …
+ Save album as …
Change
Pause
Copied %1$d/%2$d files
@@ -92,11 +92,10 @@
Media scanner
Updated %1$d Media Database Items
%1$d Selected
- Select folder with images to enable OK
+ Select folder to load pictures from and click ok
Selected only
Selection: Add all visible
Selection: Remove all visible
- Bookmark folder
Diagnostics
… containing executed SQL.
@@ -118,7 +117,7 @@
Slideshow interval in millisecs
Language
System default
- Max. sel-markers in map
+ Max. selected markers in map
Set geo history count
Set geo history file
Clear multi-selection after action
@@ -238,14 +237,30 @@ You can undo hiding by calling the mediascanner from gallery-menu."
App unprotect/unpinned
- Rename PRIVATE ...jpg to ....jpg-p
+ Rename private *.jpg to *.jpg-p
Clear
Open in Filemanager
+
+ Searchbar
+
+ Date Picker: use decades
+ I.E. Put 2010, 2011,...2019 below 2010*
+ Date Filter
+
+
+ Edit Virtual Album
+
+ Bookmark folder
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 846fc91b..a7f42402 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -25,5 +25,10 @@
+
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index ce0020fc..0fe13098 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -66,6 +66,10 @@ this program. If not, see
+
diff --git a/app/src/test/java/de/k3b/android/androFotoFinder/tagDB/TagSqlQueryParserTests.java b/app/src/test/java/de/k3b/android/androFotoFinder/tagDB/TagSqlQueryParserTests.java
index 1e21505c..6d0988d9 100644
--- a/app/src/test/java/de/k3b/android/androFotoFinder/tagDB/TagSqlQueryParserTests.java
+++ b/app/src/test/java/de/k3b/android/androFotoFinder/tagDB/TagSqlQueryParserTests.java
@@ -67,6 +67,11 @@ public void shouldParsePublic() throws Exception {
assertFilterQueryFilter(VISIBILITY.PUBLIC);
}
+ @Test
+ public void shouldFilterFind() throws Exception {
+ assertFilterFind("hello world", "shouldFilterFind");
+ }
+
// assert that input-string==output-string in input-string -> filter -> query -> filter -> output-string
private QueryParameter assertFilterQueryFilter(VISIBILITY visibility) {
String FILTER_STRING = ";;;;;;;;;" + visibility.value;
@@ -79,29 +84,75 @@ private QueryParameter assertFilterQueryFilter(String filterString) {
}
// assert that input-string==output-string in input-string -> filter -> query -> filter -> output-string
- private QueryParameter assertFilterQueryFilter(String filterString, String printSql) {
- GalleryFilterParameter initialFilter = GalleryFilterParameter.parse(filterString, new GalleryFilterParameter());
+ private QueryParameter assertFilterQueryFilter(String expectedFilterString, String printSql) {
+ GalleryFilterParameter initialFilter = GalleryFilterParameter.parse(expectedFilterString, new GalleryFilterParameter());
+
+ QueryParameter query = new QueryParameter();
+ GalleryFilterParameter parsedFilter = getParsedGalleryFilterParameter(query, initialFilter, printSql);
+
+ assertEquals(expectedFilterString, parsedFilter.toString());
+ return query;
+ }
+
+ @Test
+ public void assertGFilterQueryGFilter() {
+ assertGFilterQueryGFilter("InAnyField", createPublicGalleryFilterParameter().setInAnyField("%1% %2%"));
+ assertGFilterQueryGFilter("Date", createPublicGalleryFilterParameter().setDate("1997-12-24","2005-11-30"));
+ assertGFilterQueryGFilter("Path", createPublicGalleryFilterParameter().setPath("%1%"));
+ GalleryFilterParameter gfLL = createPublicGalleryFilterParameter();
+ gfLL.setLatitude("12.34", "34.56").setLogitude("45.67", "56.78");
+ assertGFilterQueryGFilter("Latitude Logitude", gfLL);
+ assertGFilterQueryGFilter("Rating", createPublicGalleryFilterParameter().setRatingMin(4));
+ }
+ private GalleryFilterParameter createPublicGalleryFilterParameter() {
+ return new GalleryFilterParameter().setVisibility(VISIBILITY.PUBLIC);
+ }
+
+ static private void assertGFilterQueryGFilter(String msg, GalleryFilterParameter original) {
QueryParameter query = new QueryParameter();
- TagSql.filter2QueryEx(query, initialFilter, true);
+ TagSql.filter2QueryEx(query, original, true);
+ String sql = query.toReParseableString();
+
+ // query is destroyed by parse so use a clone
+ QueryParameter queryToParse = QueryParameter.parse(sql); // new QueryParameter(query);
+ GalleryFilterParameter parsedFilter = (GalleryFilterParameter) TagSql.parseQueryEx(queryToParse, true);
- if (printSql != null) {
- String sql = query.toSqlString();
+ assertEquals(msg, original.toString(), parsedFilter.toString());
+ }
+
+ // assert that input-string==output-string in input-string -> filter -> query -> filter -> output-string
+ private QueryParameter assertFilterFind(String expectedFilterFindValue, String debugPrefixPrintSql) {
+ GalleryFilterParameter initialFilter = new GalleryFilterParameter().setInAnyField(expectedFilterFindValue);
+
+ QueryParameter query = new QueryParameter();
+ GalleryFilterParameter parsedFilter = getParsedGalleryFilterParameter(query, initialFilter, debugPrefixPrintSql);
+
+ assertEquals(query.toSqlString(), expectedFilterFindValue, parsedFilter.getInAnyField().replaceAll("%",""));
+ return query;
+ }
+
+ private GalleryFilterParameter getParsedGalleryFilterParameter(QueryParameter resultQuery, GalleryFilterParameter initialFilter, String debugPrefixPrintSql) {
+ TagSql.filter2QueryEx(resultQuery, initialFilter, true);
+
+ if (debugPrefixPrintSql != null) {
+ String sql = resultQuery.toSqlString();
int start = sql.indexOf("WHERE");
- System.out.println(printSql + ": " + sql.substring(start));
+ System.out.println(debugPrefixPrintSql + ": " + sql.substring(start));
}
- GalleryFilterParameter parsedFilter = (GalleryFilterParameter) TagSql.parseQueryEx(query, true);
+ // query is destroyed by parse so use a clone
+ QueryParameter queryToParse = new QueryParameter(resultQuery);
+ GalleryFilterParameter parsedFilter = (GalleryFilterParameter) TagSql.parseQueryEx(queryToParse, true);
parsedFilter.setSort(initialFilter.getSortID(), initialFilter.isSortAscending());
// compensate that query might automatically add visibility
if (initialFilter.getVisibility() == VISIBILITY.DEFAULT) {
parsedFilter.setVisibility(VISIBILITY.DEFAULT);
}
-
- assertEquals(filterString, parsedFilter.toString());
- return query;
+ return parsedFilter;
}
+
//################ tag filter support
@Test
public void shouldTagsNoneOnly() throws Exception {
diff --git a/fastlane/metadata/android/ar-SA/full_description.txt b/fastlane/metadata/android/ar-SA/full_description.txt
new file mode 100644
index 00000000..e3cbec04
--- /dev/null
+++ b/fastlane/metadata/android/ar-SA/full_description.txt
@@ -0,0 +1,23 @@
+مدير للصور المحلية يقوم بـ: بحث ونسخ وتعديل الصور ووضعها في معرض صور او في خريطة
+
+ المميزات:
+
+ * سرعة البحث على الصور بالعناوين (الكلمات الرئيسية)
+ * عرض النتائج في معرض او خريطة جغرافية من openstreetmap.
+ * عرض بالتفاصيل. يحتوي على ميزة تكبير الصور, والسحب للصورة التالية والسابقة.
+ * مدير ملفات مدمج للبحث وفرز, عرض, نسخ, ارسال, وحذف الصور, ... .
+ * تعديل ملف بيانات exif :التاريخ, والعنوان, والوصف, والعناوين (الكلمات الرئيسية), والمكان الجغرافي, والتقييم
+ * القيام بمعالجة الصور باعادة التسمية, وضع العلامات, والموقع الجغرافي, والعنوان تلقائيا ... عند نسخهم وقصهم .
+ * وضع "خاص" في الصور لاخفائهم من بقية برامج عرض الصور.
+ * في الوضع "المحمي والمثبت" الأوامر الحساسة مثل تعديل ونسخ, حذف, مشاركة, الاعدادات, وتغيير اختيار الصور, موقفة فبامكانك اعطاء هاتفك للغير بدون خوف.
+ * يمكنه تحمل مجموعة كبيرة من الصور (فوق 20000 صورة و 1000 مجلد).
+ * يستخدم مزود محتويات Android. لا يحتاج الى العثور على الصور .
+ * كاشف اضافي على ملفات Exif, IPTC, XMP
+
+ يتطلب صلاحيات النظام الاتية:
+
+ * الانترنت: لتحميل ملفات الخرائط من سيرفر openstreetmap
+ * ACCESS_NETWORK_STATE and ACCESS_WIFI_STATE: لمعرفة امكانية اتصال الجهاز
+ * WRITE_EXTERNAL_STORAGE لتخزين ملفات الخريطة لفرز الصور لها
+ * READ_LOGS لقراءة وتخزين ملف سجلات الانهيار..
+.
\ No newline at end of file
diff --git a/fastlane/metadata/android/ar-SA/short_description.txt b/fastlane/metadata/android/ar-SA/short_description.txt
new file mode 100644
index 00000000..9be09f54
--- /dev/null
+++ b/fastlane/metadata/android/ar-SA/short_description.txt
@@ -0,0 +1 @@
+مدير للصور المحلية يقوم بـ: بحث ونسخ وتعديل الصور ووضعها في معرض صور او في خريطة.
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/38.txt b/fastlane/metadata/android/en-US/changelogs/38.txt
new file mode 100644
index 00000000..850d2802
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/38.txt
@@ -0,0 +1,7 @@
+* New Logo/Icons by Md Nazmul Hasan.
+* New Virutal Albums/".album" files: edit with Filter-View; open with Dirpicker for Gallery-View and Geographic-Map.
+* Gallery-View: Added "Filter by Date"; Added search bar.
+* Openstreemap-Display: added optional path/date/tag-filter; fixed: works with zoomfactor > 19; Fixed initial zoom region.
+* Filter-View: In field "Find": allow to search for subexpressions seperated by " "
+* Fixed: Open image via filemanager with app specific content urls (i.e. OI File Manager)
+* Translation updates: ar,de,en,es,fr,it,ja,ru,tr,zh-CN (7 = 100%, 5 > 90%, 5 < 65%
\ No newline at end of file
diff --git a/fotolib2/src/main/java/de/k3b/FotoLibGlobal.java b/fotolib2/src/main/java/de/k3b/FotoLibGlobal.java
index 14d3e386..ab81f6ac 100644
--- a/fotolib2/src/main/java/de/k3b/FotoLibGlobal.java
+++ b/fotolib2/src/main/java/de/k3b/FotoLibGlobal.java
@@ -41,6 +41,9 @@ public class FotoLibGlobal {
/** false do not follow symlinks when scanning Directories. */
public static final boolean ignoreSymLinks = false;
+ /** datePickerUseDecade true add decade in date picker */
+ public static boolean datePickerUseDecade = false;
+
/** #100: true: private images get the extension ".jpg-p" which hides them from other gallery-apps and image pickers. */
public static boolean renamePrivateJpg = true;
diff --git a/fotolib2/src/main/java/de/k3b/database/QueryParameter.java b/fotolib2/src/main/java/de/k3b/database/QueryParameter.java
index ac8b8a40..5963f9d3 100644
--- a/fotolib2/src/main/java/de/k3b/database/QueryParameter.java
+++ b/fotolib2/src/main/java/de/k3b/database/QueryParameter.java
@@ -19,9 +19,16 @@
package de.k3b.database;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import de.k3b.io.FileUtils;
+import de.k3b.io.StringUtils;
+
/**
* Utility to collect query parameters for content-provider.
* No dependencies to android so it can be unittested
@@ -33,6 +40,9 @@
* Created by k3b on 04.06.2015.
*/
public class QueryParameter {
+ public static final String SUFFIX_VALBUM = ".album";
+ public static final String SUFFIX_QUERY = ".query";
+
/** added to every serialized item if != null. Example "Generated on 2015-10-19 with myApp Version 0815." */
public static String sFileComment = null;
@@ -140,6 +150,22 @@ public QueryParameter clearWhere() {
return this;
}
+ public QueryParameter clear() {
+ clearWhere();
+ clearColumns();
+ mFrom.clear();
+ mGroupBy.clear();
+ mHaving.clear();
+ mOrderBy.clear();
+ mHavingParameters.clear();
+ return this;
+ }
+
+ public QueryParameter clearColumns() {
+ mColumns.clear();
+ return this;
+ }
+
public QueryParameter addWhere(String where, String... parameters) {
mWhere.add(where);
return addToList(mParameters, true, parameters);
@@ -201,7 +227,7 @@ private static int getParamCount(String sqlWhereWithParameters, List par
* Therefore this sql is added to the WHERE part.
* [select ... from ... where (] [[mWhere][) GROUP BY (mGroupBy][) HAVING (mHaving]] [) ORDER BY ] [mOrderBy]*/
public String toAndroidWhere() {
- boolean hasWhere = Helper.isNotEmpty(mWhere);
+ boolean hasWhere = hasWhere();
boolean hasGroup = Helper.isNotEmpty(mGroupBy);
boolean hasHaving = Helper.isNotEmpty(mHaving);
if (!hasWhere && !hasGroup && !hasHaving) return null;
@@ -218,6 +244,10 @@ public String toAndroidWhere() {
return result.toString();
}
+ public boolean hasWhere() {
+ return Helper.isNotEmpty(mWhere);
+ }
+
public String[] toAndroidParameters() {
return Helper.toList(mParameters, mHavingParameters);
}
@@ -256,6 +286,27 @@ public String toOrderBy() {
}
/************************** end properties *********************/
+
+ public void save(OutputStream _out) throws IOException {
+ PrintWriter writer = null;
+ try {
+ writer = new PrintWriter(_out);
+ writer.println(this.toReParseableString());
+ writer.flush();
+ } finally {
+ writer.close();
+ }
+ writer = null;
+ }
+
+ public static QueryParameter load(InputStream input) throws IOException {
+ String sql = FileUtils.readFile(input);
+ if (!StringUtils.isNullOrEmpty(sql)) {
+ return QueryParameter.parse(sql);
+ }
+ return null;
+ }
+
public String toReParseableString() {
StringBuilder result = new StringBuilder();
if (sFileComment != null) result.append("# ").append(sFileComment).append("\n");
diff --git a/fotolib2/src/main/java/de/k3b/io/AlbumFile.java b/fotolib2/src/main/java/de/k3b/io/AlbumFile.java
new file mode 100644
index 00000000..1c51cbb3
--- /dev/null
+++ b/fotolib2/src/main/java/de/k3b/io/AlbumFile.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018 by k3b.
+ *
+ * This file is part of AndroFotoFinder / #APhotoManager
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see
+ */
+package de.k3b.io;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by k3b on 17.04.2018.
+ */
+
+public class AlbumFile {
+ public static final String SUFFIX_VALBUM = ".album";
+ public static final String SUFFIX_QUERY = ".query";
+
+ public static boolean isQueryFile(String uri) {
+ if (uri != null) {
+ return uri.endsWith(SUFFIX_VALBUM) || uri.endsWith(SUFFIX_QUERY);
+ }
+ return false;
+ }
+ public static boolean isQueryFile(File uri) {
+ if (uri != null) {
+ return isQueryFile(uri.getName());
+ }
+ return false;
+ }
+ public static File getExistingQueryFileOrNull(String uri) {
+ if (isQueryFile(uri)) {
+ File result = new File(FileUtils.fixPath(uri));
+ if ((result != null) && result.isFile() && result.exists()) return result;
+ }
+ return null;
+ }
+
+ /** return all album files as absolute path */
+ public static List getFilePaths(List result, File root, int subDirLevels) {
+ if (result == null) result = new ArrayList();
+
+ if ((root != null) && !FileUtils.isSymlinkDir(root,false) && root.isDirectory()) {
+ for (File file : root.listFiles()) {
+ if (file.isDirectory() && (subDirLevels > 1)) {
+ getFilePaths(result, file, subDirLevels - 1);
+ } else if (isQueryFile((file))) {
+ String path = FileUtils.tryGetCanonicalPath(file, null);
+ result.add(path);
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/fotolib2/src/main/java/de/k3b/io/DateUtil.java b/fotolib2/src/main/java/de/k3b/io/DateUtil.java
index 086b44f0..fc7af937 100644
--- a/fotolib2/src/main/java/de/k3b/io/DateUtil.java
+++ b/fotolib2/src/main/java/de/k3b/io/DateUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017 by k3b.
+ * Copyright (c) 2016-2018 by k3b.
*
* This file is part of AndroFotoFinder / #APhotoManager.
*
@@ -57,11 +57,13 @@ public static Date parseIsoDate(String dateString) {
private static Date parseDateTime(String dateString, DateFormat... formatCandidates) {
Date result = null;
- for (DateFormat formatCandidate : formatCandidates) {
- try {
- result = formatCandidate.parse(dateString);
- if (result != null) break;
- } catch (ParseException e) {
+ if (dateString != null) {
+ for (DateFormat formatCandidate : formatCandidates) {
+ try {
+ result = formatCandidate.parse(dateString);
+ if (result != null) break;
+ } catch (ParseException e) {
+ }
}
}
return result;
diff --git a/fotolib2/src/main/java/de/k3b/io/Directory.java b/fotolib2/src/main/java/de/k3b/io/Directory.java
index 42998a0b..4f05ce8d 100644
--- a/fotolib2/src/main/java/de/k3b/io/Directory.java
+++ b/fotolib2/src/main/java/de/k3b/io/Directory.java
@@ -42,7 +42,7 @@ public class Directory implements IDirectory {
private String relPath = null;
private Boolean apmDir = null;
- private IDirectory parent = null;
+ private Directory parent = null;
private List children = null;
private int nonDirItemCount = 0;
@@ -52,16 +52,28 @@ public class Directory implements IDirectory {
private int iconID = 0;
- public Directory(String relPath, Directory parent, int nonDirItemCount) {
+ public Directory(String relPath, IDirectory parent, int nonDirItemCount) {
this.setRelPath(relPath);
this.setParent(parent);
// this.setHasNonDirElements(hasNonDirElements);
if (parent != null) {
- parent.addChild(this);
+ ((Directory) parent).addChild(this);
}
setNonDirItemCount(nonDirItemCount);
}
+ /** factory method to be overwrittern by derived classes, if tree should consist of derived classes. */
+ public IDirectory createOsDirectory(File file, IDirectory parent, List children) {
+ final Directory result = new Directory(file.getName(), parent, 0);
+
+ if (children != null) {
+ for (IDirectory child : children) {
+ addChild(child);
+ }
+ }
+ return result;
+ }
+
@Override
public void destroy() {
if (children != null) {
@@ -103,8 +115,8 @@ public IDirectory getParent() {
return parent;
}
- public void setParent(Directory parent) {
- this.parent = parent;
+ public void setParent(IDirectory parent) {
+ this.parent = (Directory) parent;
}
@Override
@@ -270,6 +282,7 @@ public int getSelectionIconID() {
@Override
public int getDirFlags() {
+ if (AlbumFile.isQueryFile(this.getRelPath())) return IDirectory.DIR_FLAG_VIRTUAL_DIR;
return isApmDir() ? IDirectory.DIR_FLAG_APM_DIR : IDirectory.DIR_FLAG_NONE;
}
diff --git a/fotolib2/src/main/java/de/k3b/io/DirectoryFormatter.java b/fotolib2/src/main/java/de/k3b/io/DirectoryFormatter.java
index f56fc63a..375815b3 100644
--- a/fotolib2/src/main/java/de/k3b/io/DirectoryFormatter.java
+++ b/fotolib2/src/main/java/de/k3b/io/DirectoryFormatter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder.
*
@@ -21,14 +21,20 @@
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
+import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
+import java.util.TimeZone;
+
+import de.k3b.FotoLibGlobal;
/**
* Created by k3b on 12.07.2015.
*/
public class DirectoryFormatter {
+ private static final int INTDECADE_LEN = 3;
+
/**
* "/2001/01/16" => 2001-01-16 - 2001-01-17
* "/2001/01/" => 2001-01-01 - 2001-02-01
@@ -38,14 +44,19 @@ public static void getDates(String selectedAbsolutePath, Date from, Date to) {
Integer year = null;
Integer month = null;
Integer day = null;
+ Integer decade = (FotoLibGlobal.datePickerUseDecade) ? null : Integer.MIN_VALUE;
String parts[] = selectedAbsolutePath.split(Directory.PATH_DELIMITER);
for (String part : parts) {
if ((part != null) && ((part.length() > 0))) {
try {
+ if (decade == null) {
+ part = part.substring(0,part.length() - 1); // remove trailing "*"
+ }
Integer value = Integer.parseInt(part);
- if (year == null) year = value;
+ if (decade == null) decade = value;
+ else if (year == null) year = value;
else if (month == null) month = value;
else if (day == null) day = value;
} catch (NumberFormatException ex) {
@@ -54,27 +65,29 @@ public static void getDates(String selectedAbsolutePath, Date from, Date to) {
}
}
- if (year != null) {
- int yearFrom = year.intValue();
+ int yearFrom = 0;
+ if ((FotoLibGlobal.datePickerUseDecade) && (decade != null)) yearFrom = decade.intValue();
+ if (year != null) yearFrom = year.intValue();
- if (yearFrom == 1970) {
+ if (yearFrom != 0) {
+ if ((year != null) && (yearFrom == 1970)) {
from.setTime(0);
to.setTime(0);
} else {
int monthFrom = (month != null) ? month.intValue() : 1;
int dayFrom = (day != null) ? day.intValue() : 1;
- GregorianCalendar _from = new GregorianCalendar(yearFrom, monthFrom - 1, dayFrom, 0, 0, 0);
- _from.setTimeInMillis(_from.getTimeInMillis());
+ GregorianCalendar cal = new GregorianCalendar(yearFrom, monthFrom - 1, dayFrom, 0, 0, 0);
+ from.setTime(cal.getTimeInMillis());
+
int field = GregorianCalendar.YEAR;
+ int increment = 10;
+ if (year != null) increment = 1;
if (month != null) field = GregorianCalendar.MONTH;
if (day != null) field = GregorianCalendar.DAY_OF_MONTH;
- GregorianCalendar _to = new GregorianCalendar();
- _to.setTimeInMillis(_from.getTimeInMillis());
- _to.add(field, 1);
- to.setTime(_to.getTimeInMillis());
- from.setTime(_from.getTimeInMillis());
+ cal.add(field, increment);
+ to.setTime(cal.getTimeInMillis());
}
}
}
@@ -104,6 +117,12 @@ public static String formatLatLon(Double latOrLon) {
return formatLatLon(latOrLon.doubleValue());
}
+ public static CharSequence formatLatLon(Double... latOrLons) {
+ StringBuilder result = new StringBuilder();
+ for (Double latOrLon : latOrLons)
+ result.append(formatLatLon(latOrLon)).append(" ");
+ return result;
+ }
public static String formatLatLon(double latOrLon) {
if ((latOrLon <= 0.0000005) && (latOrLon >= -0.0000005)) return "0";
@@ -183,4 +202,54 @@ private static double getLatLon(String latOrLon) {
return Double.parseDouble(latOrLon);
}
+ public static String getDecade(String stringWithYear, int posOfYear) {
+ if ((stringWithYear == null) || (stringWithYear.length() - posOfYear < INTDECADE_LEN)) {
+ return null;
+ }
+ return stringWithYear.substring(posOfYear, posOfYear + INTDECADE_LEN) + "0*";
+ }
+
+ public static String getDatePath(final boolean withDecade, long dateFrom, long dateTo) {
+ // special cases if null, empty or only one value
+ if (dateFrom == 0) dateFrom = dateTo;
+ if (dateTo == 0) dateTo = dateFrom;
+ if (dateTo == 0) return null;
+
+ final long diffTage = Math.abs (dateTo - dateFrom) / (1000 * 60 * 60 * 24);
+
+ final Calendar date = Calendar.getInstance(); // TimeZone.getTimeZone("UTC"));
+ date.setTimeInMillis(dateFrom);
+ final int year = date.get(Calendar.YEAR);
+
+ final StringBuilder result = new StringBuilder();
+ if ((withDecade) && (diffTage < 3800)) {
+ result.append("/").append(year / 10).append("0*");
+ }
+ if (diffTage < 380) {
+ result.append("/").append(year);
+ }
+ if (diffTage < 40) {
+ final int month = date.get(Calendar.MONTH) + 1;
+ result.append("/").append(n2(month) );
+ }
+ if (diffTage <= 2) {
+ final int day = date.get(Calendar.DAY_OF_MONTH);
+ result.append("/").append(n2(day));
+ }
+ if (result.length() == 0) return null;
+ return result.toString();
+ }
+
+ private static int getYear(Date dateTo) {
+ int year = dateTo.getYear();
+ if ((year >= 0) && (year < 1000)) year += 1900;
+ return year;
+ }
+
+
+ private static String n2(int i) {
+ if (i < 10) return "0"+i;
+
+ return ""+i;
+ }
}
diff --git a/fotolib2/src/main/java/de/k3b/io/FileUtils.java b/fotolib2/src/main/java/de/k3b/io/FileUtils.java
index b367f8ca..8f2b2103 100644
--- a/fotolib2/src/main/java/de/k3b/io/FileUtils.java
+++ b/fotolib2/src/main/java/de/k3b/io/FileUtils.java
@@ -29,6 +29,7 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.regex.Pattern;
@@ -52,8 +53,15 @@ public static InputStream streamFromStringContent(String data) {
return s;
}
+ public static String readFile(InputStream file) throws IOException {
+ return internalReadFile(new BufferedReader(new InputStreamReader(file)), file);
+ }
+
public static String readFile(File file) throws IOException {
- BufferedReader br = new BufferedReader(new FileReader(file));
+ return internalReadFile(new BufferedReader(new FileReader(file)), file);
+ }
+
+ public static String internalReadFile(BufferedReader br, Object source) throws IOException {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
@@ -62,11 +70,11 @@ public static String readFile(File file) throws IOException {
sb.append("\n");
line = br.readLine();
}
- close(br, file);
+ close(br, source);
return sb.toString();
}
- public static void close(Closeable stream, Object source) {
+ public static void close(Closeable stream, Object source) {
if (stream != null) {
try {
stream.close();
@@ -286,4 +294,33 @@ public static void copy(InputStream is, OutputStream os) throws IOException {
}
+ // #118 app specific content uri convert
+ // from {content://approvider}//storage/emulated/0/DCIM/... to /storage/emulated/0/DCIM/
+ public static String fixPath(String path) {
+ if (path != null) {
+ while (path.startsWith("//")) {
+ path = path.substring(1);
+ }
+ }
+ return path;
+ }
+
+ public static File getFirstExistingDir(File root) {
+ while ((root != null) && (!root.exists() || !root.isDirectory())) {
+ root = root.getParentFile();
+ }
+ return root;
+ }
+
+ public static File getFirstNonExistingFile(File parentDir, String newFilePrefix, int number, String newFileSuffix) {
+ if (parentDir == null) return null;
+
+ parentDir.mkdirs();
+ File candidate = new File(parentDir, newFilePrefix + newFileSuffix);
+ while (candidate.exists()) {
+ number ++;
+ candidate = new File(parentDir, newFilePrefix + number + newFileSuffix);
+ }
+ return candidate;
+ }
}
diff --git a/fotolib2/src/main/java/de/k3b/io/GalleryFilterParameter.java b/fotolib2/src/main/java/de/k3b/io/GalleryFilterParameter.java
index 1203f497..22104b72 100644
--- a/fotolib2/src/main/java/de/k3b/io/GalleryFilterParameter.java
+++ b/fotolib2/src/main/java/de/k3b/io/GalleryFilterParameter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder / #APhotoManager.
*
@@ -19,12 +19,14 @@
package de.k3b.io;
+import java.io.File;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import de.k3b.FotoLibGlobal;
import de.k3b.io.collections.SelectedItems;
/**
@@ -39,12 +41,13 @@ public class GalleryFilterParameter extends GeoRectangle implements IGalleryFilt
private List tagsAllIncluded;
private List tagsAllExcluded;
+
+ /** match if the text is in path, filename, title, description, tags. Wildcard "%" is allowed. Sub-expressions are seperated by " " */
private String inAnyField;
private long dateMin = 0;
private long dateMax = 0;
- private boolean nonGeoOnly = false;
private boolean withNoTags = false;
/** one of the VISIBILITY_.XXXX values */
@@ -60,7 +63,6 @@ public GalleryFilterParameter get(IGalleryFilter src) {
this.setDateMax(src.getDateMax());
this.setDateMin(src.getDateMin());
this.setPath(src.getPath());
- this.setNonGeoOnly(src.isNonGeoOnly());
this.setWithNoTags(src.isWithNoTags());
this.setVisibility(src.getVisibility());
@@ -83,6 +85,11 @@ public GalleryFilterParameter setPath(String path) {
this.path = path;return this;
}
+ public File getPathFile() {
+ if (StringUtils.isNullOrEmpty(getPath())) return null;
+ return new File(getPath());
+ }
+
@Override
public long getDateMin() {
return dateMin;
@@ -109,16 +116,6 @@ public GalleryFilterParameter setDate(long min, long max) {
return setDateMin(min).setDateMax(max);
}
- @Override
- public boolean isNonGeoOnly() {
- return nonGeoOnly;
- }
-
- public GalleryFilterParameter setNonGeoOnly(boolean nonGeoOnly) {
- this.nonGeoOnly = nonGeoOnly;
- return this;
- }
-
@Override
public boolean isWithNoTags() {
return withNoTags;
@@ -152,12 +149,13 @@ public GalleryFilterParameter setTagsAllExcluded(List tagsAllExcluded) {
return this;
}
- /** match if the text is in path, filename, title, description, tags */
+ /** match if the text is in path, filename, title, description, tags. Wildcard "%" is allowed. Sub-expressions are seperated by " " */
@Override
public String getInAnyField() {
return inAnyField;
}
+ /** match if the text is in path, filename, title, description, tags. Wildcard "%" is allowed. Sub-expressions are seperated by " " */
public GalleryFilterParameter setInAnyField(String inAnyField) {
this.inAnyField = inAnyField;
return this;
@@ -389,12 +387,18 @@ public static String convertList(List strings) {
return SelectedItems.toString(strings.iterator());
}
- public void setRatingMin(int ratingMin) {
+ public GalleryFilterParameter setRatingMin(int ratingMin) {
this.ratingMin = ratingMin;
+ return this;
}
@Override
public int getRatingMin() {
return ratingMin;
}
+
+ /** get Date Min/Max in date picker compatible format */
+ public String getDatePath() {
+ return DirectoryFormatter.getDatePath(FotoLibGlobal.datePickerUseDecade, getDateMin(), getDateMax());
+ }
}
diff --git a/fotolib2/src/main/java/de/k3b/io/GeoRectangle.java b/fotolib2/src/main/java/de/k3b/io/GeoRectangle.java
index b0204c93..5bc6b062 100644
--- a/fotolib2/src/main/java/de/k3b/io/GeoRectangle.java
+++ b/fotolib2/src/main/java/de/k3b/io/GeoRectangle.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder.
*
@@ -33,6 +33,8 @@ public class GeoRectangle implements IGeoRectangle {
private double logituedMin = Double.NaN;
private double logituedMax = Double.NaN;
+ private boolean nonGeoOnly = false;
+
protected static double parseLatLon(String value) {
if ((value == null) || value.isEmpty()) return Double.NaN;
try {
@@ -48,6 +50,7 @@ public GeoRectangle get(IGeoRectangle src) {
this.setLatitudeMin(src.getLatitudeMin());
this.setLatitudeMax(src.getLatitudeMax());
this.setLogituedMax(src.getLogituedMax());
+ this.setNonGeoOnly(src.isNonGeoOnly());
}
return this;
}
@@ -198,4 +201,22 @@ private static String format(double d) {
return Double.toString(d);
}
+ @Override
+ public boolean isNonGeoOnly() {
+ return nonGeoOnly;
+ }
+
+ public GeoRectangle setNonGeoOnly(boolean nonGeoOnly) {
+ this.nonGeoOnly = nonGeoOnly;
+ return this;
+ }
+
+ public void setHasGeo() {
+ if (isNonGeoOnly() || isEmpty((IGeoRectangle) this)) {
+ setNonGeoOnly(false);
+ setLogitude(-180.0, +180);
+ setLatitude(-90.0, +90.0);
+ }
+ }
+
}
diff --git a/fotolib2/src/main/java/de/k3b/io/IDirectory.java b/fotolib2/src/main/java/de/k3b/io/IDirectory.java
index 8d5438c4..63c497a6 100644
--- a/fotolib2/src/main/java/de/k3b/io/IDirectory.java
+++ b/fotolib2/src/main/java/de/k3b/io/IDirectory.java
@@ -19,6 +19,7 @@
package de.k3b.io;
+import java.io.File;
import java.util.List;
/**
@@ -32,11 +33,14 @@ public interface IDirectory {
public static final int DIR_FLAG_NOMEDIA_ROOT = 2; // containing ".nomedia"
public static final int DIR_FLAG_APM_DIR = 8; // containing ".apm"
+ public static final int DIR_FLAG_VIRTUAL_DIR = 9; // containing "*.album"
String APM_DIR_PREFIX = "§ ";
String getRelPath();
String getAbsolute();
+ IDirectory createOsDirectory(File file, IDirectory parent, List children);
+
IDirectory getParent();
List getChildren();
diff --git a/fotolib2/src/main/java/de/k3b/io/IGalleryFilter.java b/fotolib2/src/main/java/de/k3b/io/IGalleryFilter.java
index 842c0c23..6f27d8fb 100644
--- a/fotolib2/src/main/java/de/k3b/io/IGalleryFilter.java
+++ b/fotolib2/src/main/java/de/k3b/io/IGalleryFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder / #APhotoManager.
*
@@ -38,9 +38,6 @@ public interface IGalleryFilter extends IGeoRectangle {
long getDateMax();
- /** true: only photos whith no geo info (lat==lon==null) */
- boolean isNonGeoOnly();
-
/** number defining current sorting */
int getSortID();
@@ -57,7 +54,7 @@ public interface IGalleryFilter extends IGeoRectangle {
/** None of the Tags/Keywords/Categories/VirtualAlbum that the image must NOT contain. ("AND NOT") */
List getTagsAllExcluded();
- /** match if the text is in path, filename, title, description, tags */
+ /** match if the text is in path, filename, title, description, tags. Wildcard "%" is allowed. Sub-expressions are seperated by " " */
String getInAnyField();
/** one of the VISIBILITY_XXXX values for public/private images */
diff --git a/fotolib2/src/main/java/de/k3b/io/IGeoRectangle.java b/fotolib2/src/main/java/de/k3b/io/IGeoRectangle.java
index 5e054045..752fdad3 100644
--- a/fotolib2/src/main/java/de/k3b/io/IGeoRectangle.java
+++ b/fotolib2/src/main/java/de/k3b/io/IGeoRectangle.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder.
*
@@ -35,5 +35,8 @@ public interface IGeoRectangle {
/** maximum longitude, in degrees east. -180..+180 */
double getLogituedMax();
+ /** true: only photos whith no geo info (lat==lon==null) */
+ boolean isNonGeoOnly();
+
IGeoRectangle get(IGeoRectangle src);
}
diff --git a/fotolib2/src/main/java/de/k3b/io/ListUtils.java b/fotolib2/src/main/java/de/k3b/io/ListUtils.java
index af4b8b96..b7fb4009 100644
--- a/fotolib2/src/main/java/de/k3b/io/ListUtils.java
+++ b/fotolib2/src/main/java/de/k3b/io/ListUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 by k3b.
+ * Copyright (c) 2017-2018 by k3b.
*
* This file is part of AndroFotoFinder / #APhotoManager.
*
@@ -52,8 +52,11 @@ public static List toStringList(Iterable> list) {
public static List toStringList(Object... list) {
ArrayList result = new ArrayList();
- for (Object item : list) {
- if (item != null) result.add(item.toString());
+
+ if (list != null) {
+ for (Object item : list) {
+ if (item != null) result.add(item.toString());
+ }
}
return result;
}
diff --git a/fotolib2/src/main/java/de/k3b/io/OSDirOrVirtualAlbumFile.java b/fotolib2/src/main/java/de/k3b/io/OSDirOrVirtualAlbumFile.java
new file mode 100644
index 00000000..fe091b43
--- /dev/null
+++ b/fotolib2/src/main/java/de/k3b/io/OSDirOrVirtualAlbumFile.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018 by k3b.
+ *
+ * This file is part of AndroFotoFinder / #APhotoManager
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see
+ */
+
+package de.k3b.io;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Also add *.album files to sub-directories.
+ *
+ * Created by k3b on 17.04.2018.
+ */
+
+public class OSDirOrVirtualAlbumFile extends OSDirectory {
+ public OSDirOrVirtualAlbumFile(File current, OSDirectory parent, List childen) {
+ super(current, parent, childen);
+ if (isAlbum(current)) {
+ setDirFlags(DIR_FLAG_VIRTUAL_DIR);
+ }
+ }
+
+ private boolean isAlbum(File candidate) {
+ return (candidate != null) && AlbumFile.isQueryFile(candidate.getName());
+ }
+
+ @Override
+ protected int getCalculateFlags(File directory) {
+ int result;
+ if (isAlbum(directory)) {
+ result = DIR_FLAG_VIRTUAL_DIR;
+ } else {
+ result = super.getCalculateFlags(directory);
+ }
+ return result;
+ }
+
+ @Override
+ protected boolean isDirectory(File candidate) {
+ if (super.isDirectory(candidate)) return true;
+ return isAlbum(candidate);
+ }
+
+ /** factory method to be overwrittern by derived classes, if tree should consist of derived classes. */
+ @Override
+ public OSDirectory createOsDirectory(File file, IDirectory parent, List children) {
+ return new OSDirOrVirtualAlbumFile(file, (OSDirectory) parent, children);
+ }
+}
\ No newline at end of file
diff --git a/fotolib2/src/main/java/de/k3b/io/OSDirectory.java b/fotolib2/src/main/java/de/k3b/io/OSDirectory.java
index 8fe8fa19..5c19ed3f 100644
--- a/fotolib2/src/main/java/de/k3b/io/OSDirectory.java
+++ b/fotolib2/src/main/java/de/k3b/io/OSDirectory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017 by k3b.
+ * Copyright (c) 2015-2018 by k3b.
*
* This file is part of AndroFotoFinder / #APhotoManager
*
@@ -43,16 +43,8 @@ public class OSDirectory implements IDirectory {
private int mDirFlags = DIR_FLAG_NONE;
- public OSDirectory(String current, OSDirectory parent) {
- this(FileUtils.tryGetCanonicalFile(current), parent);
- }
-
- protected OSDirectory(File current, OSDirectory parent) {
- this(current, parent, null);
- }
-
// protected constructor to allow unittesting with fake children
- protected OSDirectory(File current, OSDirectory parent, List childen) {
+ public OSDirectory(File current, OSDirectory parent, List childen) {
setCurrent(current);
mParent = parent;
mChilden = childen;
@@ -64,6 +56,11 @@ protected OSDirectory(File current, OSDirectory parent, List childen
}
}
+ /** factory method to be overwrittern by derived classes, if tree should consist of derived classes. */
+ public OSDirectory createOsDirectory(File file, IDirectory parent, List children) {
+ return new OSDirectory(file, (OSDirectory) parent, children);
+ }
+
public OSDirectory setCurrent(File current) {
destroy();
mCurrent = current;
@@ -71,7 +68,7 @@ public OSDirectory setCurrent(File current) {
return this;
}
- private int getCalculateFlags(File directory) {
+ protected int getCalculateFlags(File directory) {
int result = 0;
if ((directory != null) && (directory.isDirectory())) {
if (new File(directory, FileUtils.MEDIA_IGNORE_FILENAME).exists()) {
@@ -133,12 +130,14 @@ public List getChildren() {
if (files != null) {
for (File file : files) {
if ((file != null)
- && file.isDirectory()
&& !file.isHidden()
&& !file.getName().startsWith(".")
+ && !FileUtils.isSymlinkDir(file,true)
// && file.canWrite() // bugfix: must be visible because writeprotected parentdir may contain writeenabled subdirs
- && !FileUtils.isSymlinkDir(file,true)) {
- mChilden.add(new OSDirectory(file, this));
+ ) {
+ if (isDirectory(file)) {
+ mChilden.add(createOsDirectory(file, this, null));
+ }
// } else if (FotoLibGlobal.debugEnabled) {
// logger.debug(FileUtils.getDebugString("OSDirectory.getChildren() rejected ", file));
}
@@ -148,6 +147,10 @@ public List getChildren() {
return mChilden;
}
+ protected boolean isDirectory(File file) {
+ return file.isDirectory();
+ }
+
// package to allow unit testing
protected IDirectory find(OSDirectory root, String path) {
return find(root, FileUtils.tryGetCanonicalFile(path));
@@ -168,7 +171,7 @@ protected static IDirectory find(OSDirectory root, File file) {
OSDirectory result = (OSDirectory) findChildByRelPath(children, name);
if (result == null) {
- result = new OSDirectory(file, (OSDirectory) parentDir);
+ result = root.createOsDirectory(file, (OSDirectory) parentDir, null);
children.add(result);
}
return result;
@@ -270,7 +273,7 @@ private OSDirectory addChildFolder(String newCildFolderName, List gr
if (result == null) {
File newChildFile = FileUtils.tryGetCanonicalFile(new File(mCurrent, newCildFolderName), null);
- result = new OSDirectory(newChildFile, this, grandChilden);
+ result = createOsDirectory(newChildFile, this, grandChilden);
if (result != null) {
children.add(result);
}
diff --git a/fotolib2/src/main/java/de/k3b/io/StringUtils.java b/fotolib2/src/main/java/de/k3b/io/StringUtils.java
index 371722bb..1bff81aa 100644
--- a/fotolib2/src/main/java/de/k3b/io/StringUtils.java
+++ b/fotolib2/src/main/java/de/k3b/io/StringUtils.java
@@ -35,6 +35,7 @@ public static boolean equals(Object lhs, Object rhs) {
return (rhs == null);
}
+ /** return pos of '#' before {start} or {-1} if not found */
public static int getTagStart(CharSequence s, int start) {
if (start <= s.length()) {
int i = start -1;
@@ -44,6 +45,8 @@ public static int getTagStart(CharSequence s, int start) {
}
return -1;
}
+
+ /** return first char after tagword or {-1} if not a tag-word-end */
public static int getTagEnd(CharSequence s, int start) {
int len = s.length();
int i = start;
@@ -55,10 +58,12 @@ public static int getTagEnd(CharSequence s, int start) {
private static boolean isTagChar(char c) {
return Character.isJavaIdentifierPart(c) || (c == '-');
}
+
private static boolean isTagDelimiterChar(char c) {
return Character.isSpaceChar(c) || (",;(){}".indexOf(c) >= 0);
}
- /** words surrounded by blank and starting with '#' */
+
+ /** a tag is a word surrounded by blank and starting with '#' */
public static CharSequence getTag(CharSequence s, int start) {
int tagStart = getTagStart(s,start);
int tagEnd = getTagEnd(s,start);
@@ -68,10 +73,29 @@ public static CharSequence getTag(CharSequence s, int start) {
return null;
}
+ public static String trim(CharSequence str) {
+ if (str == null) return null;
+ return str.toString().trim();
+
+ }
public static int length(CharSequence str) {
return (str != null) ? str.length() : 0;
}
+ public static int charCount(CharSequence str, char c) {
+ int result = 0;
+ if (str != null) {
+ int len = length(str);
+ for (int i=0; i < len;i++) {
+ if (str.charAt(i) == c) {
+ result++;
+ }
+ }
+ }
+
+ return result;
+ }
+
public static boolean isNullOrEmpty(CharSequence str) {
return (0 == length(str));
}
@@ -89,4 +113,43 @@ public static String merge(String lhs, String rhs) {
return result.toString();
}
+
+ public static StringBuilder createDebugMessage(boolean enabled, final Object... parameters) {
+ if (enabled) return appendMessage(null, parameters);
+ return null;
+ }
+
+ /**
+ * append 0..n parameters to stringbuilder
+ *
+ * @param resultOrNull where the data is appended to. if null a StringBuilder is created
+ * @param parameters all non null param-values will be appended seperated by " "
+ * @return either result or newly created StringBuilder if result was null
+ */
+ public static StringBuilder appendMessage(StringBuilder resultOrNull, final Object... parameters) {
+ StringBuilder result = (resultOrNull == null) ? new StringBuilder() : resultOrNull;
+
+ append(result, parameters);
+ return result;
+ }
+
+ private static void append(StringBuilder result, Object[] parameters) {
+ if ((result != null) && (parameters != null) && (parameters.length > 0)) {
+ result.append("(");
+ for (final Object param : parameters) {
+ if (param != null) {
+ if (param instanceof Object[]) {
+ append(result, (Object[]) param);
+ } else if (param instanceof Exception) {
+ Exception ex = (Exception) param;
+ result.append(ex.getClass().getSimpleName()).append("=").append(ex.getMessage()).append(" ");
+ } else {
+ result.append(param.toString()).append(" ");
+ }
+ }
+ }
+ result.append(") ");
+ }
+ }
+
}
diff --git a/fotolib2/src/main/java/de/k3b/io/VISIBILITY.java b/fotolib2/src/main/java/de/k3b/io/VISIBILITY.java
index a9674666..39e57db2 100644
--- a/fotolib2/src/main/java/de/k3b/io/VISIBILITY.java
+++ b/fotolib2/src/main/java/de/k3b/io/VISIBILITY.java
@@ -28,9 +28,13 @@
import de.k3b.FotoLibGlobal;
public enum VISIBILITY {
+ /** take from current settings */
DEFAULT(0),
+ /** private only */
PRIVATE(1),
+ /** public only */
PUBLIC(2),
+ /** private and public images but not other files like album-files */
PRIVATE_PUBLIC(3);
// #100: if photo has this tag it has visibility PRIVATE
diff --git a/fotolib2/src/main/java/de/k3b/media/MetaWriterExifXml.java b/fotolib2/src/main/java/de/k3b/media/MetaWriterExifXml.java
index 423bf487..8f1fa239 100644
--- a/fotolib2/src/main/java/de/k3b/media/MetaWriterExifXml.java
+++ b/fotolib2/src/main/java/de/k3b/media/MetaWriterExifXml.java
@@ -99,17 +99,32 @@ public static MetaWriterExifXml create(String absoluteJpgInPath, String absolute
startTimestamp = new Date().getTime();
}
MediaXmpSegment xmp = MediaXmpSegment.loadXmpSidecarContentOrNull(absoluteJpgInPath, dbg_context);
- if ((createXmpIfNotExist) && (xmp == null)) {
+ if ((xmp == null) && (createXmpIfNotExist || MediaUtil.isImage(absoluteJpgInPath,MediaUtil.IMG_TYPE_NON_JPG))) {
ImageMetaReader jpg = new ImageMetaReader().load(absoluteJpgInPath,null,null,
dbg_context + " xmp-file not found. create/extract from jpg ");
- xmp = jpg.getImternalXmp();
+ // #124: fix can be null for gif/png
+ xmp = (jpg == null) ? null : jpg.getImternalXmp();
// jpg has no embedded xmp create
- if (xmp == null) xmp = new MediaXmpSegment();
+ if (xmp == null) {
+ xmp = new MediaXmpSegment();
+ }
// xmp should have the same data as exif/iptc
MediaUtil.copyNonEmpty(xmp, jpg);
+ if ((absoluteJpgInPath != null) && (xmp.getDateTimeTaken() == null)) {
+ File in = new File(absoluteJpgInPath);
+ if (in.exists() && in.isFile()) {
+ long lastModified = in.lastModified();
+ if (lastModified != 0) {
+ final Date newDate = new Date(lastModified);
+ xmp.setDateTimeTaken(newDate);
+ if (xmp.getFilelastModified() == 0) xmp.setFilelastModified(in);
+ }
+ }
+
+ }
}
ExifInterfaceEx exif = new ExifInterfaceEx(absoluteJpgInPath, null, xmp, dbg_context);
if (exif.isValidJpgExifFormat()) {
diff --git a/fotolib2/src/main/java/de/k3b/tagDB/TagProcessor.java b/fotolib2/src/main/java/de/k3b/tagDB/TagProcessor.java
index eb0118e3..6d672c37 100644
--- a/fotolib2/src/main/java/de/k3b/tagDB/TagProcessor.java
+++ b/fotolib2/src/main/java/de/k3b/tagDB/TagProcessor.java
@@ -110,7 +110,7 @@ public static int getDiff(List _original, List _changed, List _original, List _changed, List {
+ // +2 lowercase letters sorunded by non-word-char or begine-of-line/end-of-line
+ Pattern languagePattern = Pattern.compile("[\\W^]([a-z][a-z])[$\\W]") ;
+
+ public int add(String timmed) {
+ int found = 0;
+ Matcher match = languagePattern.matcher(timmed);
+
+ if (match.find()) {
+ String lan = match.group(1);
+ if ("by".compareTo(lan) != 0) {
+ StringBuilder buff = this.get(lan);
+ if (buff != null) {
+ buff.append(nl).append(timmed);
+ } else {
+ buff = new StringBuilder();
+ buff.append(timmed);
+ this.put(lan,buff);
+ }
+ }
+ found++;
+ }
+
+ return found;
+ }
+
+ @Override public String toString() {
+ String[] keys = this.keySet().toArray(new String[this.size()]);
+ Arrays.sort(keys);
+ StringBuilder result = new StringBuilder();
+
+ for(String lan : keys) {
+ result.append(lan).append(nl).append(this.get(lan)).append(nl).append(nl);
+ }
+
+ return result.toString();
+ }
+ }
+ private static String nl = "\n";
+ private static int dateExampleLen = "2018-07-28".length();
+ public int readCountryTimeStatistics(Country2History country2History, File file) throws IOException {
+ int found = 0;
+ BufferedReader br = null;
+ String firstDate = "#";
+
+ try {
+ br = new BufferedReader(new FileReader(file));
+ for (String line; (line = br.readLine()) != null; ) {
+ String timmed = line.trim();
+ int length = timmed.length();
+ if (length == dateExampleLen) {
+ firstDate = timmed;
+ } else if ((length > dateExampleLen) && ( timmed.compareTo(firstDate) > 0)) {
+ found += country2History.add(timmed);
+ }
+ }
+ return found;
+ } finally {
+ FileUtils.close(br, file);
+ }
+ }
+
protected String getCommentProperty(String name) {
Object val = (lastLocales == null) ? null : lastLocales.get(name);
String[] parts = (val != null) ? val.toString().split(";") : null;
diff --git a/fotolib2/src/test/java/de/k3b/translations/TranslationStatisticsTests.java b/fotolib2/src/test/java/de/k3b/translations/TranslationStatisticsTests.java
index f5ca8781..2d9dfd21 100644
--- a/fotolib2/src/test/java/de/k3b/translations/TranslationStatisticsTests.java
+++ b/fotolib2/src/test/java/de/k3b/translations/TranslationStatisticsTests.java
@@ -22,9 +22,12 @@
import org.junit.Test;
import java.io.File;
+import java.util.Date;
+
+import de.k3b.io.DateUtil;
/**
- * Created by EVE on 15.01.2018.
+ * Created by k3b on 15.01.2018.
*/
public class TranslationStatisticsTests {
@@ -41,15 +44,20 @@ public void shouldMatchString_de() {
@Test
public void dumpAsMD() {
final TranslationStatistics translationStatistics = new TranslationStatistics();
- System.out.println("\n" +
+ System.out.println("\n" +
translationStatistics.formatterMarkdown.toString(translationStatistics.getLocaleInfos(), translationStatistics.english));
}
@Test
public void dupmpAsIni() {
final TranslationStatistics translationStatistics = new TranslationStatistics();
- System.out.println("# generated with TranslationStatisticsTests#dupmpAsIni\n" +
+ System.out.println("# generated on " + DateUtil.toIsoDateString(new Date()) +
+ " with de.k3b.translations.TranslationStatisticsTests#dupmpAsIni\n" +
translationStatistics.formatterIni.toString(translationStatistics.getLocaleInfos(), translationStatistics.english));
+
+ System.out.println("\n\n\n" +
+ translationStatistics.country2History.toString());
}
diff --git a/translation-git-entries.lis b/translation-git-entries.lis
new file mode 100644
index 00000000..6c21c3d0
--- /dev/null
+++ b/translation-git-entries.lis
@@ -0,0 +1,53 @@
+# translation-git-entries.lis
+# textfile containing git history for ticket #21 (language update)
+# generated by
+# git log "--grep=#21" "--pretty=format:%ci %s"
+# sort by date desc
+
+2011-08-21
+
+2018-08-20 08:21:36 +0200 #21: ar by Vitality
+2018-08-20 08:18:22 +0200 #21: zh-CN) by Forbidden (cptbl00dra1n)
+2018-08-02 11:18:20 +0200 #21: es by Andreaevangelina
+2018-08-02 11:16:24 +0200 #21: it .Rogue.
+2018-08-02 11:13:34 +0200 #21: fr Poussinou
+2018-07-28 10:50:52 +0200 #21: fr by Tuuux and Poussinou
+2018-07-28 10:48:26 +0200 #21: ru by Dmitry Zhgun (trupizzza)
+2018-03-12 11:29:35 +0100 #21: tr Boyut, pt-BR Lucas Magalhães (whoisroot), ja naofu, fr Tuuux, fr Poussinou, de k3b
+2018-02-08 15:36:44 +0100 #21: ja by naofum
+2018-01-22 22:41:11 +0100 #21: ru by ku ku(2ku)
+2018-01-15 08:39:31 +0100 #21: es (100%) by Dani Certad (daniconejito)
+2018-01-09 22:49:15 +0100 #21: es, fr, ja, zh-CN
+2017-11-22 00:52:26 +0100 #21: zh by Liu Feng (pitumaomao)
+2017-11-22 00:48:42 +0100 #21: zh by Liu Feng (pitumaomao)
+2017-11-19 15:44:15 +0100 #21: in by "isaideureka"
+2017-11-16 20:38:56 +0100 #21: ja (by naofum" )
+2017-11-13 21:29:25 +0100 #21: zh by rosatravels .
+2017-09-11 22:52:13 +0200 #21: nl by ookikgavertalen
+2017-07-27 16:02:16 +0200 #21: : zh (zh) by Liu Feng (pitumaomao)
+2017-07-22 09:20:33 +0200 #21: ja / tr
+2017-05-15 08:09:58 +0200 #21 #86: tr srkns stringlate
+2017-04-01 16:10:21 +0200 #21: for 0.6.0 : ar by medowill
+2017-03-27 08:15:03 +0200 #21: : it by random_r, fr by Tuuux
+2017-03-18 14:32:03 +0100 #21: for 0.6.0 : tr by erdenerr
+2017-03-18 13:34:14 +0100 #21: for 0.6.0 : pt-BR by Nana13
+2017-03-06 08:44:38 +0100 #21: for 0.5.5 : ja by "naofum"
+2017-02-27 23:40:11 +0100 #21: translations: ru by "divizdev" and special translators with ids
+2017-01-19 20:00:17 +0100 #21: more fr by tuuux
+2016-12-08 14:42:05 +0100 #21: for 0.5.5 : it by "random_r", ja by "naofum"
+2016-12-05 19:42:28 +0100 #21: more fr by tuuux
+2016-11-28 23:06:25 +0100 #21: more fr by tuuux
+2016-09-23 10:25:09 +0200 #21: more : it by "random_r", ja by "naofum",nl by keunes
+2016-09-10 18:53:39 +0200 #21: more nl by keunes
+2016-08-09 18:20:34 +0200 #21 added pl "Maselkowicz".
+2016-07-12 20:16:47 +0200 #21: merged app from crowdwin. it by "random_r"; ja by naofum
+2016-06-20 15:03:09 +0200 #21 it "random_r"
+2016-05-18 18:58:06 +0200 #21 d ja and pt-BR
+2016-05-17 16:22:35 +0200 #21 added nl keunes
+2016-05-09 10:00:14 +0200 #21 added it "random_r"
+2016-03-04 12:11:17 +0100 #21: merged app with crowdwin. Added es
+2016-01-20 17:26:49 +0100 #21 added ro language by mironeasav
+2016-01-11 16:14:22 +0100 #21: fr by tuuux
+2016-01-08 10:46:11 +0100 #21: fr by tuuux
+2016-01-07 17:23:00 +0100 #21 ja by naofum
+
diff --git a/translation-history.ini b/translation-history.ini
index 16e88bd6..d7c8da07 100644
--- a/translation-history.ini
+++ b/translation-history.ini
@@ -6,22 +6,26 @@
# format: locale=lastUpdate;comments;strings.xml;about.xml;fdrod;missing-translations
#
# ignore filedates before this.
-ignore=2018-02-12
+ignore=2099-08-20
+
+# generated on 2018-08-21 with de.k3b.translations.TranslationStatisticsTests#dupmpAsIni
+# language;changed;translated by;app;fdroid;aboutbox;missing
+ar=2018-08-20;Vitality, medowill;100%;100%;1;
+de=2018-04-08;k3b;100%;100%;1;
+en=2018-08-13;;100%;100%;1;
+es=2018-08-02;Andreaevangelina, Dani Certad (daniconejito);100%;100%;1;
+fr=2018-08-02;Poussinou, tuuux;100%;100%;1;
+hi=2018-03-12;jznsamuel (jasonsamuel88);3% (6/170);0% (0/3);0;about_summary, area_menu_title, background, bookmark, bookmark_delete_answer_format, bookmark_delete_error_format, bookmark_delete_question, bookmark_file_comment_format, bookmark_save_as_menu_title, btn_change, btn_pause, clear_menu_title, copy_result_format, date_picker_menu_title, delete_menu_title, delete_question_message_format, delete_question_title, delete_result_format, destination_copy, destination_move, details_menu_title, edit_chooser_title, edit_err_editor_not_found, edit_menu_title, exif_menu_title, file_err_writeprotected, filter_any_hint, filter_err_invalid_date_format, filter_err_invalid_location_format, filter_menu_title, filter_menu_title_album, filter_path_hint, fix_link_menu_title, folder_dialog_title_format, folder_err_load_failed_format, folder_hide_images_menu_title, folder_hide_images_question_message_format, folder_menu_title, gallery_title, geo_picker_err_not_found, geo_picker_title, geo_show_as_menu_title, global_err_sql_message_format, global_err_sql_title_reload, global_err_system, image_err_file_exists_format, image_err_file_rename_format, image_err_not_found_format, image_err_not_in_db_format, image_loading_at_position_format, image_success_update_format, lbl_any, lbl_date, lbl_description, lbl_exif, lbl_file_name_pattern, lbl_image_visibility, lbl_images_private, lbl_images_public, lbl_latitude_short, lbl_longitude_short, lbl_path, lbl_rating, lbl_tag, lbl_tags_exclude, lbl_tags_include, lbl_title, lbl_with_no_tags, menu_title_app_pinnend, menu_title_app_unpinnend, mk_dir_default, mk_dir_menu_title, mk_err_failed_format, mk_success_format, more_menu_title, move_menu_title, move_result_format, osm_cright_title, overwrite_question_title, photo_autoprocessing_edit_menu_title, preview_message_format, rename_menu_title, rename_result_format, scanner_dir_question, scanner_err_busy, scanner_menu_title, scanner_update_result_format, searchbar_menu_title, selected_only_menu_title, selection_add_all_menu_title, selection_none_hint, selection_remove_menu_title, selection_status_format, settings_bookmark_dir_title, settings_debug_clear_title, settings_debug_libs_summary, settings_debug_memory_title, settings_debug_meta_io, settings_debug_save_title, settings_debug_sql_summary, settings_debug_summary, settings_debug_title, settings_debug_view_item_summary, settings_geo_history_file_title, settings_geo_history_max_title, settings_group_debug_title, settings_image_hide_time_title, settings_image_initialImageDetailResolutionHigh_summary, settings_image_initialImageDetailResolutionHigh_title, settings_image_slideshow_intervall_title, settings_image_thumb_dir_title, settings_image_thumb_if_bigger_than_summary, settings_image_thumb_if_bigger_than_title, settings_locale_os_language, settings_locale_title, settings_log_folder_title, settings_map_selmarker_max_title, settings_maps_forge_dir_title, settings_maps_forge_enable_summary, settings_maps_forge_enable_tile, settings_media_update_strategy_jpg, settings_media_update_strategy_jpg_xmp_create, settings_media_update_strategy_jpg_xmp_update, settings_media_update_strategy_title, settings_media_update_strategy_xmp, settings_multisel_clear_summary, settings_multisel_clear_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, settings_rename_private_jpg_title, settings_title, settings_xmp_file_schema_summary, settings_xmp_file_schema_title, share_err_not_found, share_err_to_many, share_menu_title, show_in_filemanager_menu_title, show_in_gallery_menu_title, show_photo, slideshow_menu_title, sort_by_date, sort_by_file_size, sort_by_folder, sort_by_modification, sort_by_name, sort_by_name_len, sort_by_none, sort_by_place, sort_by_rating, sort_by_width, sort_menu_title, tags_activity_title, tags_add_default, tags_add_menu_title, tags_defaults, tags_delete_children_title, tags_edit_menu_title, tags_hint, tags_search_hint, tags_search_no_matching_items_found, tags_update_photos, update_result_format, view_context_menu_title, zoom_to_fit_menu_title
+in=2017-11-19;isaideureka;92% (157/170);100%;1;bookmark, clear_menu_title, date_picker_menu_title, filter_menu_title_album, lbl_image_visibility, menu_title_app_pinnend, menu_title_app_unpinnend, searchbar_menu_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, settings_rename_private_jpg_title, show_in_filemanager_menu_title, sort_by_file_size, sort_by_width
+it=2018-08-02;.Rogue.,random_r;100%;0% (0/3);1;
+ja=2018-03-12;naofum;97% (165/170);100%;1;bookmark, date_picker_menu_title, filter_menu_title_album, searchbar_menu_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title
+nl=2017-09-11;ookikgavertalen, keunes;90% (153/170);0% (0/3);0;bookmark, clear_menu_title, date_picker_menu_title, filter_menu_title_album, lbl_exif, lbl_file_name_pattern, lbl_image_visibility, menu_title_app_pinnend, menu_title_app_unpinnend, photo_autoprocessing_edit_menu_title, preview_message_format, searchbar_menu_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, settings_rename_private_jpg_title, show_in_filemanager_menu_title, sort_by_file_size, sort_by_width
+pl=2018-03-12;Maselkowicz;64% (109/170);0% (0/3);0;bookmark, clear_menu_title, date_picker_menu_title, exif_menu_title, filter_any_hint, filter_menu_title_album, filter_path_hint, fix_link_menu_title, folder_hide_images_menu_title, folder_hide_images_question_message_format, geo_picker_from_map_title, geo_picker_from_photo_title, lbl_any, lbl_description, lbl_exif, lbl_file_name_pattern, lbl_image_visibility, lbl_images_private, lbl_images_public, lbl_rating, lbl_tag, lbl_tags_exclude, lbl_tags_include, lbl_title, lbl_with_no_tags, menu_title_app_pinnend, menu_title_app_unpinnend, photo_autoprocessing_edit_menu_title, preview_message_format, searchbar_menu_title, settings_debug_libs_summary, settings_debug_meta_io, settings_maps_forge_dir_title, settings_maps_forge_enable_summary, settings_maps_forge_enable_tile, settings_media_update_strategy_jpg, settings_media_update_strategy_jpg_xmp_create, settings_media_update_strategy_jpg_xmp_update, settings_media_update_strategy_title, settings_media_update_strategy_xmp, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, settings_rename_private_jpg_title, settings_xmp_file_schema_summary, settings_xmp_file_schema_title, show_in_filemanager_menu_title, show_photo, sort_by_file_size, sort_by_modification, sort_by_rating, sort_by_width, tags_activity_title, tags_add_default, tags_add_menu_title, tags_defaults, tags_delete_children_title, tags_edit_menu_title, tags_hint, tags_search_hint, tags_search_no_matching_items_found, tags_update_photos, view_context_menu_title
+pt-rBR=2017-03-18;thiagonkami, Nana13;22% (38/170);0% (0/3);0;bookmark, clear_menu_title, date_picker_menu_title, exif_menu_title, filter_any_hint, filter_menu_title_album, filter_path_hint, fix_link_menu_title, folder_hide_images_menu_title, folder_hide_images_question_message_format, geo_picker_from_map_title, geo_picker_from_photo_title, geo_picker_title, global_err_sql_message_format, global_err_sql_title_reload, image_err_file_exists_format, image_err_not_found_format, image_err_not_in_db_format, image_success_update_format, lbl_any, lbl_date, lbl_description, lbl_exif, lbl_file_name_pattern, lbl_image_visibility, lbl_images_private, lbl_images_public, lbl_latitude_short, lbl_longitude_short, lbl_path, lbl_rating, lbl_tag, lbl_tags_exclude, lbl_tags_include, lbl_title, lbl_with_no_geo, lbl_with_no_tags, menu_title_app_pinnend, menu_title_app_unpinnend, mk_dir_default, mk_dir_menu_title, mk_err_failed_format, mk_success_format, more_menu_title, move_menu_title, move_result_format, osm_cright_title, overwrite_question_title, photo_autoprocessing_edit_menu_title, preview_message_format, rename_menu_title, rename_result_format, scanner_dir_question, scanner_err_busy, scanner_menu_title, scanner_update_result_format, searchbar_menu_title, selected_only_menu_title, selection_add_all_menu_title, selection_none_hint, selection_remove_menu_title, selection_status_format, settings_bookmark_dir_title, settings_debug_clear_title, settings_debug_libs_summary, settings_debug_memory_title, settings_debug_meta_io, settings_debug_save_title, settings_debug_sql_summary, settings_debug_summary, settings_debug_title, settings_debug_view_item_summary, settings_geo_history_file_title, settings_geo_history_max_title, settings_group_debug_title, settings_image_hide_time_title, settings_image_initialImageDetailResolutionHigh_summary, settings_image_initialImageDetailResolutionHigh_title, settings_image_slideshow_intervall_title, settings_image_thumb_dir_title, settings_image_thumb_if_bigger_than_summary, settings_image_thumb_if_bigger_than_title, settings_locale_os_language, settings_locale_title, settings_log_folder_title, settings_map_selmarker_max_title, settings_maps_forge_dir_title, settings_maps_forge_enable_summary, settings_maps_forge_enable_tile, settings_media_update_strategy_jpg, settings_media_update_strategy_jpg_xmp_create, settings_media_update_strategy_jpg_xmp_update, settings_media_update_strategy_title, settings_media_update_strategy_xmp, settings_multisel_clear_summary, settings_multisel_clear_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, settings_rename_private_jpg_title, settings_title, settings_xmp_file_schema_summary, settings_xmp_file_schema_title, share_err_not_found, share_err_to_many, share_menu_title, show_in_filemanager_menu_title, show_in_gallery_menu_title, show_photo, slideshow_menu_title, sort_by_date, sort_by_file_size, sort_by_folder, sort_by_modification, sort_by_name, sort_by_name_len, sort_by_none, sort_by_place, sort_by_rating, sort_by_width, sort_menu_title, tags_activity_title, tags_add_default, tags_add_menu_title, tags_defaults, tags_delete_children_title, tags_edit_menu_title, tags_hint, tags_search_hint, tags_search_no_matching_items_found, tags_update_photos, update_result_format, view_context_menu_title, zoom_to_fit_menu_title
+ro=2016-01-20;mironeasav;30% (52/170);0% (0/3);1;bookmark, clear_menu_title, date_picker_menu_title, exif_menu_title, file_err_writeprotected, filter_any_hint, filter_menu_title_album, filter_path_hint, fix_link_menu_title, folder_hide_images_menu_title, folder_hide_images_question_message_format, geo_picker_from_map_title, geo_picker_from_photo_title, geo_show_as_menu_title, lbl_any, lbl_description, lbl_exif, lbl_file_name_pattern, lbl_image_visibility, lbl_images_private, lbl_images_public, lbl_latitude_short, lbl_longitude_short, lbl_path, lbl_rating, lbl_tag, lbl_tags_exclude, lbl_tags_include, lbl_title, lbl_with_no_geo, lbl_with_no_tags, menu_title_app_pinnend, menu_title_app_unpinnend, mk_dir_default, mk_dir_menu_title, mk_err_failed_format, mk_success_format, more_menu_title, move_menu_title, move_result_format, osm_cright_title, overwrite_question_title, photo_autoprocessing_edit_menu_title, preview_message_format, rename_menu_title, rename_result_format, scanner_dir_question, scanner_err_busy, scanner_menu_title, scanner_update_result_format, searchbar_menu_title, selected_only_menu_title, selection_add_all_menu_title, selection_none_hint, selection_remove_menu_title, selection_status_format, settings_bookmark_dir_title, settings_debug_clear_title, settings_debug_libs_summary, settings_debug_memory_title, settings_debug_meta_io, settings_debug_save_title, settings_debug_sql_summary, settings_debug_summary, settings_debug_title, settings_debug_view_item_summary, settings_geo_history_file_title, settings_geo_history_max_title, settings_group_debug_title, settings_image_hide_time_title, settings_image_initialImageDetailResolutionHigh_summary, settings_image_initialImageDetailResolutionHigh_title, settings_image_slideshow_intervall_title, settings_image_thumb_dir_title, settings_image_thumb_if_bigger_than_summary, settings_image_thumb_if_bigger_than_title, settings_locale_os_language, settings_locale_title, settings_log_folder_title, settings_map_selmarker_max_title, settings_maps_forge_dir_title, settings_maps_forge_enable_summary, settings_maps_forge_enable_tile, settings_media_update_strategy_jpg, settings_media_update_strategy_jpg_xmp_create, settings_media_update_strategy_jpg_xmp_update, settings_media_update_strategy_title, settings_media_update_strategy_xmp, settings_multisel_clear_summary, settings_multisel_clear_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, settings_rename_private_jpg_title, settings_title, settings_xmp_file_schema_summary, settings_xmp_file_schema_title, share_err_not_found, share_err_to_many, share_menu_title, show_in_filemanager_menu_title, show_in_gallery_menu_title, show_photo, slideshow_menu_title, sort_by_date, sort_by_file_size, sort_by_modification, sort_by_rating, sort_by_width, tags_activity_title, tags_add_default, tags_add_menu_title, tags_defaults, tags_delete_children_title, tags_edit_menu_title, tags_hint, tags_search_hint, tags_search_no_matching_items_found, tags_update_photos, view_context_menu_title
+ru=2018-07-28;Dmitry Zhgun (trupizzza), ku ku(2ku), "divizdev";34% (58/170);0% (0/3);0;bookmark, date_picker_menu_title, destination_copy, destination_move, exif_menu_title, file_err_writeprotected, filter_any_hint, filter_err_invalid_date_format, filter_err_invalid_location_format, filter_menu_title_album, filter_path_hint, fix_link_menu_title, folder_dialog_title_format, folder_err_load_failed_format, folder_hide_images_menu_title, folder_hide_images_question_message_format, folder_menu_title, geo_edit_menu_title, geo_edit_update_in_progress, geo_picker_err_not_found, geo_picker_from_map_title, geo_picker_from_photo_title, geo_picker_title, geo_show_as_menu_title, global_err_sql_title_reload, global_err_system, image_err_not_found_format, image_err_not_in_db_format, image_success_update_format, lbl_description, lbl_exif, lbl_file_name_pattern, lbl_images_private, lbl_images_public, lbl_path, lbl_tag, lbl_tags_exclude, lbl_tags_include, lbl_title, lbl_with_no_geo, menu_title_app_pinnend, menu_title_app_unpinnend, mk_err_failed_format, mk_success_format, more_menu_title, move_result_format, osm_cright_title, photo_autoprocessing_edit_menu_title, preview_message_format, rename_result_format, scanner_dir_question, scanner_err_busy, scanner_menu_title, scanner_update_result_format, searchbar_menu_title, selected_only_menu_title, selection_add_all_menu_title, selection_none_hint, selection_remove_menu_title, selection_status_format, settings_debug_libs_summary, settings_debug_meta_io, settings_debug_sql_summary, settings_debug_summary, settings_debug_title, settings_debug_view_item_summary, settings_geo_history_file_title, settings_geo_history_max_title, settings_image_hide_time_title, settings_image_initialImageDetailResolutionHigh_summary, settings_image_initialImageDetailResolutionHigh_title, settings_image_slideshow_intervall_title, settings_image_thumb_dir_title, settings_image_thumb_if_bigger_than_summary, settings_image_thumb_if_bigger_than_title, settings_map_selmarker_max_title, settings_maps_forge_dir_title, settings_media_update_strategy_jpg, settings_media_update_strategy_jpg_xmp_create, settings_media_update_strategy_jpg_xmp_update, settings_media_update_strategy_title, settings_media_update_strategy_xmp, settings_multisel_clear_summary, settings_multisel_clear_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, settings_rename_private_jpg_title, settings_xmp_file_schema_summary, settings_xmp_file_schema_title, share_err_not_found, share_err_to_many, share_menu_title, show_in_filemanager_menu_title, show_in_gallery_menu_title, show_photo, sort_by_folder, sort_by_name, sort_by_name_len, sort_by_none, sort_by_place, sort_by_width, sort_menu_title, tags_activity_title, tags_defaults, tags_delete_children_title, tags_edit_menu_title, tags_hint, tags_search_hint, tags_search_no_matching_items_found, tags_update_photos, update_result_format, view_context_menu_title, zoom_to_fit_menu_title
+tr=2018-03-12;Boyut, srkns, erdenerr;90% (154/170);0% (0/3);1;bookmark, clear_menu_title, date_picker_menu_title, filter_menu_title_album, lbl_exif, lbl_file_name_pattern, lbl_image_visibility, menu_title_app_pinnend, menu_title_app_unpinnend, photo_autoprocessing_edit_menu_title, preview_message_format, searchbar_menu_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, settings_rename_private_jpg_title, show_in_filemanager_menu_title, sort_by_width
+zh-rCN=2018-08-20;Forbidden (cptbl00dra1n), Liu Feng (pitumaomao);100%;100%;1;
+zh-rTW=2017-11-22;Liu Feng (pitumaomao), rosatravels;92% (157/170);100%;1;bookmark, clear_menu_title, date_picker_menu_title, filter_menu_title_album, lbl_image_visibility, menu_title_app_pinnend, menu_title_app_unpinnend, searchbar_menu_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, settings_rename_private_jpg_title, show_in_filemanager_menu_title, sort_by_file_size, sort_by_width
+zz=2018-03-12;k3b;95% (163/170);0% (0/3);0;bookmark, clear_menu_title, date_picker_menu_title, filter_menu_title_album, searchbar_menu_title, settings_pick_date_decade4year_summary, settings_pick_date_decade4year_title, show_in_filemanager_menu_title
-ar=2017-04-01;medowill
-de=2018-01-31;k3b
-es=2018-01-15;Dani Certad (daniconejito)
-en=2018-01-31;k3b
-fr=2018-01-31;Tuuux, Poussinou
-hi=2018-01-09;jznsamuel (jasonsamuel88)
-in=2017-11-19;isaideureka
-it=2017-03-27;random_r
-ja=2018-02-08;naofum
-nl=2017-09-11;keunes, ookikgavertalen
-pl=2016-08-09;Maselkowicz
-pt-rBR=2017-03-18;thiagonkami, Nana13
-ro=2016-01-20;mironeasav
-ru=2018-01-22;divizdev, ku ku(2ku)
-tr=2017-07-22;erdenerr, srkns
-zh-rCN=2018-01-09;Liu Feng (pitumaomao)
-zh-rTW=2017-11-13;Liu Feng (pitumaomao), rosatravels
diff --git a/wiki/table-of-content.md b/wiki/table-of-content.md
deleted file mode 100644
index c2ccff06..00000000
--- a/wiki/table-of-content.md
+++ /dev/null
@@ -1,52 +0,0 @@
-[table-of-content](table-of-content)
-
-
-
-![](https://raw.githubusercontent.com/k3b/AndroFotoFinder/master/wiki/png/s_unchecked.png)
-![](https://raw.githubusercontent.com/k3b/AndroFotoFinder/master/wiki/png/s_cancel.png)
-![](https://raw.githubusercontent.com/k3b/AndroFotoFinder/master/wiki/png/s_checked.png)
-![](https://raw.githubusercontent.com/k3b/AndroFotoFinder/master/wiki/png/s_filter.png)
-![](https://raw.githubusercontent.com/k3b/AndroFotoFinder/master/wiki/png/s_folder.png)
-![](https://raw.githubusercontent.com/k3b/AndroFotoFinder/master/wiki/png/s_map.png)
-![](https://raw.githubusercontent.com/k3b/AndroFotoFinder/master/wiki/png/s_share.png)
-
-![](https://raw.githubusercontent.com/k3b/AndroFotoFinder/master/wiki/png/Gallery.png)
-
-
-* [Features](https://github.com/k3b/AndroFotoFinder/wiki/features)
-* [Geographic-Map](https://github.com/k3b/AndroFotoFinder/wiki/geographic-map)
- * ["geo:" picker](https://github.com/k3b/AndroFotoFinder/wiki/geographic-map#picker)
-* [Gallery-View](https://github.com/k3b/AndroFotoFinder/wiki/Gallery-View)
- * [multi selection mode](Gallery-View#Multiselection)
- * [Gallery Navigation](Gallery-View#Navigation)
- * [Current Visible Photos](Gallery-View#CurrentSet)
-* [Image-View](https://github.com/k3b/AndroFotoFinder/wiki/Image-View)
- * [Image-View Intent-API](Image-View#api)
-* [Folder-Picker](https://github.com/k3b/AndroFotoFinder/wiki/Folder-Picker)
-* [Intent API](https://github.com/k3b/AndroFotoFinder/wiki/intentapi)
- * [Uri formats](intentapi#uri)
- * [geo: uri format](intentapi#uri-geo)
- * [android.intent.extra.TITLE string](intentapi#EXTRA_TITLE)
- * [Intent-Extra parameter](intentapi#extra)
- * [de.k3b.extra.FILTER string](intentapi#filter)
- * [de.k3b.extra.SQL string string](intentapi#EXTRA_SQL)
- * [de.k3b.extra.SELECTED_ITEMS string](intentapi#SelectedItems)
- * [de.k3b.extra.SELECTED_ITEMS_PATH string](intentapi#SelectedPaths)
- * [Internal sql format of .query files](intentapi#sql)
-
-* [Filter-View](https://github.com/k3b/AndroFotoFinder/wiki/Filter-View)
-* [Bookmarks](Bookmarks)
-* [Settings](https://github.com/k3b/AndroFotoFinder/wiki/settings)
-* [pc android meta sync](https://github.com/k3b/AndroFotoFinder/wiki/sync)
-* [Metadata](https://github.com/k3b/AndroFotoFinder/wiki/Metadata)
-* [Tags](Tags)
-
-