package de.blau.android.propertyeditor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.acra.ACRA;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import de.blau.android.App;
import de.blau.android.Logic;
import de.blau.android.Main;
import de.blau.android.R;
import de.blau.android.names.Names.TagMap;
import de.blau.android.osm.OsmElement;
import de.blau.android.osm.OsmElement.ElementType;
import de.blau.android.osm.Relation;
import de.blau.android.osm.RelationMemberDescription;
import de.blau.android.osm.StorageDelegator;
import de.blau.android.prefs.Preferences;
import de.blau.android.presets.Preset;
import de.blau.android.presets.Preset.PresetItem;
import de.blau.android.presets.ValueWithCount;
import de.blau.android.propertyeditor.PresetFragment.OnPresetSelectedListener;
import de.blau.android.util.BaseFragment;
import de.blau.android.util.BugFixedAppCompatActivity;
import de.blau.android.util.PlaceTagValueAdapter;
import de.blau.android.util.SavingHelper;
import de.blau.android.util.SelectFile;
import de.blau.android.util.Snack;
import de.blau.android.util.StreetTagValueAdapter;
import de.blau.android.util.ThemeUtils;
import de.blau.android.util.Util;
import de.blau.android.views.ExtendedViewPager;
/**
* An Activity to edit OSM-Tags. Sends the edited Tags as Result to its caller-Activity (normally {@link Main}).
*
* @author mb
* @author simon
*/
public class PropertyEditor extends BugFixedAppCompatActivity implements
OnPresetSelectedListener, EditorUpdate, FormUpdate, PresetFilterUpdate, NameAdapters, OnSaveListener {
private static final String CURRENTITEM = "current_item";
private static final String PANELAYOUT = "pane_layout";
private static final String PRESET_FRAGMENT = "preset_fragment";
static final String RECENTPRESETS_FRAGMENT = "recentpresets_fragment";
public static final String TAGEDIT_DATA = "dataClass";
private static final String TAGEDIT_LAST_ADDRESS_TAGS = "applyLastTags";
private static final String TAGEDIT_SHOW_PRESETS = "showPresets";
private static final String TAGEDIT_ASK_FOR_NAME = "askForName";
private static final String TAGEDIT_EXTRA_TAGS = "extra";
/** The layout containing the edit rows */
LinearLayout rowLayout = null;
PresetFragment presetFragment;
private int presetFragmentPosition = -1;
TagFormFragment tagFormFragment;
private int tagFormFragmentPosition = -1;
TagEditorFragment tagEditorFragment;
private int tagEditorFragmentPosition = -1;
RelationMembershipFragment relationMembershipFragment;
RelationMembersFragment relationMembersFragment;
RecentPresetsFragment recentPresetsFragment;
private PropertyEditorPagerAdapter propertyEditorPagerAdapter;
/**
* The tag we use for Android-logging.
*/
private static final String DEBUG_TAG = PropertyEditor.class.getSimpleName();
private long osmIds[];
private String types[];
Preset[] presets = null;
/**
* The OSM element for reference.
* DO NOT ATTEMPT TO MODIFY IT.
*/
private OsmElement[] elements;
private PropertyEditorData[] loadData;
private boolean applyLastAddressTags = false;
private boolean showPresets = false;
private boolean askForName = false;
private HashMap<String,String> extraTags = null;
/**
* Handles "enter" key presses.
*/
final OnKeyListener myKeyListener = new MyKeyListener();
/**
* True while the activity is between onResume and onPause.
* Used to suppress autocomplete dropdowns while the activity is not running (showing them can lead to crashes).
* Needs to be static to be accessible in TagEditRow.
*/
static boolean running = false;
/**
* Display form based editing
*/
private boolean formEnabled = false;
/**
* Used both in the form and conventional tag editor fragments
*/
private StreetTagValueAdapter streetNameAutocompleteAdapter = null;
private PlaceTagValueAdapter placeNameAutocompleteAdapter = null;
/**
*
*/
private ArrayList<LinkedHashMap<String, String>> originalTags;
/**
* the same for relations
*/
private HashMap<Long,String> originalParents;
private ArrayList<RelationMemberDescription> originalMembers;
static final String COPIED_TAGS_FILE = "copiedtags.dat";
private SavingHelper<LinkedHashMap<String,String>> savingHelper
= new SavingHelper<LinkedHashMap<String,String>>();
private Preferences prefs = null;
private ExtendedViewPager mViewPager;
private boolean usePaneLayout = false;
private boolean isRelation = false;
public static void startForResult(@NonNull Activity activity,
@NonNull PropertyEditorData[] dataClass,
boolean applyLastTags,
boolean showPresets,
boolean askForName,
HashMap<String,String> extraTags,
int requestCode) {
Log.d(DEBUG_TAG, "startForResult");
Intent intent = new Intent(activity, PropertyEditor.class);
intent.putExtra(TAGEDIT_DATA, dataClass);
intent.putExtra(TAGEDIT_LAST_ADDRESS_TAGS, Boolean.valueOf(applyLastTags));
intent.putExtra(TAGEDIT_SHOW_PRESETS, Boolean.valueOf(showPresets));
intent.putExtra(TAGEDIT_ASK_FOR_NAME, Boolean.valueOf(askForName));
intent.putExtra(TAGEDIT_EXTRA_TAGS, extraTags);
activity.startActivityForResult(intent, requestCode);
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
int currentItem = -1; // used when restoring
prefs = new Preferences(this);
if (prefs.lightThemeEnabled()) {
setTheme(R.style.Theme_customTagEditor_Light);
}
super.onCreate(savedInstanceState);
// requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
if (prefs.splitActionBarEnabled()) {
// TODO determine if we want to reinstate the bottom bar
}
// tags
if (savedInstanceState == null) {
// No previous state to restore - get the state from the intent
Log.d(DEBUG_TAG, "Initializing from intent");
loadData = PropertyEditorData.deserializeArray(getIntent().getSerializableExtra(TAGEDIT_DATA));
applyLastAddressTags = (Boolean)getIntent().getSerializableExtra(TAGEDIT_LAST_ADDRESS_TAGS);
showPresets = (Boolean)getIntent().getSerializableExtra(TAGEDIT_SHOW_PRESETS);
askForName = (Boolean)getIntent().getSerializableExtra(TAGEDIT_ASK_FOR_NAME);
extraTags = (HashMap<String,String>)getIntent().getSerializableExtra(TAGEDIT_EXTRA_TAGS);
usePaneLayout = Util.isLandscape(this);
} else {
// Restore activity from saved state
Log.d(DEBUG_TAG, "Restoring from savedInstanceState");
loadData = PropertyEditorData.deserializeArray(getIntent().getSerializableExtra(TAGEDIT_DATA));
currentItem = savedInstanceState.getInt(CURRENTITEM,-1);
usePaneLayout = savedInstanceState.getBoolean(PANELAYOUT); //FIXME this disables layout changes on restarting
Logic logic = App.newLogic(); //
StorageDelegator delegator = App.getDelegator();
if (!delegator.isDirty() && delegator.isEmpty()) { // this can mean: need to load state
Log.d(DEBUG_TAG, "Loading saved state");
logic.syncLoadFromFile(this); // sync load
App.getTaskStorage().readFromFile(this);
}
}
Log.d(DEBUG_TAG, "... done.");
// sanity check
StorageDelegator delegator = App.getDelegator();
if (delegator == null || loadData == null) {
abort("Delegator null");
}
osmIds = new long[loadData.length];
types = new String[loadData.length];
elements = new OsmElement[loadData.length];
for (int i=0;i<loadData.length;i++) {
osmIds[i] = loadData[i].osmId;
types[i] = loadData[i].type;
elements[i] = delegator.getOsmElement(types[i], osmIds[i]);
// and another sanity check
if (elements[i] == null) {
abort("Missing elements");
}
}
presets = App.getCurrentPresets(this);
if (usePaneLayout) {
setContentView(R.layout.pane_view);
Log.d(DEBUG_TAG, "Using layout for large devices");
} else {
setContentView(R.layout.tab_view);
}
// Find the toolbar view inside the activity layout
Toolbar toolbar = (Toolbar) findViewById(R.id.propertyEditorBar);
// Sets the Toolbar to act as the ActionBar for this Activity window.
setSupportActionBar(toolbar);
// tags
ArrayList<LinkedHashMap<String, String>> tags = new ArrayList<LinkedHashMap<String, String>>();
originalTags = new ArrayList<LinkedHashMap<String, String>>();
for (int i=0;i<loadData.length;i++) {
originalTags.add((LinkedHashMap<String, String>) (loadData[i].originalTags != null ? loadData[i].originalTags : loadData[i].tags));
tags.add((LinkedHashMap<String, String>) loadData[i].tags);
}
if (loadData.length == 1) { // for now no support of relations and form for multi-select
// parent relations
originalParents = loadData[0].originalParents != null ? loadData[0].originalParents : loadData[0].parents;
if (types[0].endsWith(Relation.NAME)) {
// members of this relation
originalMembers = loadData[0].originalMembers != null ? loadData[0].originalMembers : loadData[0].members;
isRelation = true;
}
formEnabled = prefs.tagFormEnabled();
}
propertyEditorPagerAdapter =
new PropertyEditorPagerAdapter(getSupportFragmentManager(),tags);
mViewPager = (ExtendedViewPager) findViewById(R.id.pager);
PagerTabStrip pagerTabStrip = (PagerTabStrip) mViewPager.findViewById(R.id.pager_header);
pagerTabStrip.setDrawFullUnderline(true);
pagerTabStrip.setTabIndicatorColor(ThemeUtils.getStyleAttribColorValue(this,R.attr.colorAccent,R.color.dark_grey));
ActionBar actionbar = getSupportActionBar();
actionbar.setDisplayShowTitleEnabled(false);
actionbar.setDisplayHomeAsUpEnabled(true);
if (usePaneLayout) { // add both preset fragments to panes
Log.d(DEBUG_TAG,"Adding MRU prests");
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment recentPresetsFragment = fm.findFragmentByTag(RECENTPRESETS_FRAGMENT);
if (recentPresetsFragment != null) {
ft.remove(recentPresetsFragment);
}
recentPresetsFragment = RecentPresetsFragment.newInstance(elements[0]); // FIXME collect tags
ft.add(R.id.recent_preset_row,recentPresetsFragment,RECENTPRESETS_FRAGMENT);
presetFragment = (PresetFragment) fm.findFragmentByTag(PRESET_FRAGMENT);
if (presetFragment != null) {
ft.remove(presetFragment);
}
presetFragment = PresetFragment.newInstance(elements[0],true); // FIXME collect tags
ft.add(R.id.preset_row,presetFragment,PRESET_FRAGMENT);
ft.commit();
// this essentially has to be hardwired
presetFragmentPosition = 0;
if (formEnabled) {
tagFormFragmentPosition = 0;
tagEditorFragmentPosition = 1; // FIXME
} else {
tagFormFragmentPosition = 0;
tagEditorFragmentPosition = 0; // FIXME
}
} else {
presetFragmentPosition = 0;
if (formEnabled) {
tagFormFragmentPosition = 1;
tagEditorFragmentPosition = 2; // FIXME
} else {
tagFormFragmentPosition = 1;
tagEditorFragmentPosition = 1; // FIXME
}
}
mViewPager.setOffscreenPageLimit(4); // FIXME currently this is required or else some of the logic between the fragments will not work
mViewPager.setAdapter(propertyEditorPagerAdapter);
mViewPager.addOnPageChangeListener(new PageChangeListener());
mViewPager.setCurrentItem(currentItem != -1 ? currentItem : (showPresets ? presetFragmentPosition : (formEnabled ? tagFormFragmentPosition : tagEditorFragmentPosition)));
}
private void abort(String cause) {
Snack.barError(this, R.string.toast_inconsistent_state);
ACRA.getErrorReporter().putCustomData("CAUSE",cause);
ACRA.getErrorReporter().handleException(null);
finish();
}
@Override
protected void onStart() {
Log.d(DEBUG_TAG,"onStart");
super.onStart();
Log.d(DEBUG_TAG,"onStart done");
}
@Override
protected void onResume() {
Log.d(DEBUG_TAG,"onResume");
super.onResume();
running = true;
Address.loadLastAddresses(this);
Log.d(DEBUG_TAG,"onResume done");
}
@Override
protected void onDestroy() {
Log.d(DEBUG_TAG,"onDestroy");
super.onDestroy();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Due to a problem of not being able to intercept android.R.id.home in fragments on older android versions
// we start passing the event to the currently displayed fragment.
// REF: http://stackoverflow.com/questions/21938419/intercepting-actionbar-home-button-in-fragment
Fragment fragment = ((PropertyEditorPagerAdapter) mViewPager.getAdapter()).getItem(false,mViewPager.getCurrentItem());
if (item.getItemId() == android.R.id.home && fragment != null && fragment.getView() != null && fragment.onOptionsItemSelected(item)) {
Log.d(DEBUG_TAG,"called fragment onOptionsItemSelected");
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* {@inheritDoc}
*/
@Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
Log.d(DEBUG_TAG, "onActivityResult");
if ((requestCode == SelectFile.READ_FILE || requestCode == SelectFile.READ_FILE_OLD || requestCode == SelectFile.SAVE_FILE) && resultCode == RESULT_OK) {
SelectFile.handleResult(requestCode, data);
}
}
public class PropertyEditorPagerAdapter extends FragmentPagerAdapter {
private ArrayList<LinkedHashMap<String, String>> tags;
private boolean restoring = false;
public PropertyEditorPagerAdapter(FragmentManager fm, ArrayList<LinkedHashMap<String, String>> tags) {
super(fm);
this.tags = tags;
}
@Override
public int getCount() {
int pages = 0;
if (loadData.length == 1) {
if (types[0].endsWith(Relation.NAME)) {
pages = 4;
} else {
pages = 3;
}
if (formEnabled) {
pages++;
}
} else {
pages = 2;
}
return usePaneLayout ? pages -1 : pages; // preset page not in pager
}
Fragment tagFormFragment(int position, boolean displayRecentPresets) {
tagFormFragmentPosition = position;
tagFormFragment = TagFormFragment.newInstance(displayRecentPresets, applyLastAddressTags, loadData[0].focusOnKey, askForName);
return tagFormFragment;
}
Fragment tagEditorFragment(int position, boolean displayRecentPresets) {
tagEditorFragmentPosition = position;
tagEditorFragment = TagEditorFragment.newInstance(elements, tags, applyLastAddressTags, loadData[0].focusOnKey, displayRecentPresets, extraTags);
return tagEditorFragment;
}
Fragment relationMembershipFragment() {
if (loadData.length == 1) {
relationMembershipFragment = RelationMembershipFragment.newInstance(loadData[0].parents);
return relationMembershipFragment;
}
return null;
}
Fragment relationMembersFragment() {
if (loadData.length == 1 && types[0].endsWith(Relation.NAME)) {
relationMembersFragment = RelationMembersFragment.newInstance(osmIds[0],loadData[0].members);
return relationMembersFragment;
}
return null;
}
@Override
public Fragment getItem(int position) {
return getItem(true, position);
}
public Fragment getItem(boolean instantiate, int position) {
Log.d(DEBUG_TAG, "getItem " + instantiate + " " + position);
if (formEnabled) {
if (!usePaneLayout) {
switch(position) {
case 0:
if (instantiate) {
presetFragment = PresetFragment.newInstance(elements[0], false); //
}
return presetFragment;
case 1:
return instantiate ? tagFormFragment(position, true) : tagFormFragment;
case 2:
return instantiate ? tagEditorFragment(position, false) : tagEditorFragment;
case 3:
return isRelation ? (instantiate ? relationMembersFragment() : relationMembersFragment) : (instantiate ? relationMembershipFragment() : relationMembershipFragment);
case 4:
return instantiate ? relationMembershipFragment() : relationMembershipFragment;
}
} else {
switch(position) {
case 0:
return instantiate ? tagFormFragment(position, false) : tagFormFragment;
case 1:
return instantiate ? tagEditorFragment(position, false) : tagEditorFragment;
case 2:
return isRelation ? (instantiate ? relationMembersFragment() : relationMembersFragment) : (instantiate ? relationMembershipFragment() : relationMembershipFragment);
case 3:
return instantiate ? relationMembershipFragment() : relationMembershipFragment;
}
}
} else {
if (!usePaneLayout) {
switch(position) {
case 0:
if (instantiate) {
presetFragment = PresetFragment.newInstance(elements[0], false); //
}
return presetFragment;
case 1:
return instantiate ? tagEditorFragment(position, true) : tagEditorFragment;
case 2:
return isRelation ? (instantiate ? relationMembersFragment() : relationMembersFragment) : (instantiate ? relationMembershipFragment() : relationMembershipFragment);
case 3:
return instantiate ? relationMembershipFragment() : relationMembershipFragment;
}
} else {
switch(position) {
case 0:
return instantiate ? tagEditorFragment(position, false) :tagEditorFragment;
case 2:
return isRelation ? (instantiate ? relationMembersFragment() : relationMembersFragment) : (instantiate ? relationMembershipFragment() : relationMembershipFragment);
case 3:
return instantiate ? relationMembershipFragment() : relationMembershipFragment;
}
}
}
return null;
}
@Override
public CharSequence getPageTitle(int position) {
if (formEnabled) {
if (!usePaneLayout) {
switch(position) {
case 0: return getString(R.string.tag_menu_preset);
case 1: return getString(R.string.menu_tags);
case 2: return getString(R.string.tag_details);
case 3: return isRelation ? getString(R.string.members) : getString(R.string.relations);
case 4: return getString(R.string.relations);
}
} else {
switch(position) {
case 0: return getString(R.string.menu_tags);
case 1: return getString(R.string.tag_details);
case 2: return isRelation ? getString(R.string.members) : getString(R.string.relations);
case 3: return getString(R.string.relations);
}
}
} else {
if (!usePaneLayout) {
switch(position) {
case 0: return getString(R.string.tag_menu_preset);
case 1: return getString(R.string.menu_tags);
case 2: return isRelation ? getString(R.string.members) : getString(R.string.relations);
case 3: return getString(R.string.relations);
}
} else {
switch(position) {
case 0: return getString(R.string.menu_tags);
case 1: return isRelation ? getString(R.string.members) : getString(R.string.relations);
case 2: return getString(R.string.relations);
}
}
}
return "error";
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
BaseFragment fragment = (BaseFragment) super.instantiateItem(container, position);
// update fragment refs here
if (fragment instanceof TagFormFragment) {
tagFormFragment = (TagFormFragment) fragment;
Log.d(DEBUG_TAG, "Restored ref to TagFormFragment");
} else if (fragment instanceof TagEditorFragment) {
tagEditorFragment = (TagEditorFragment) fragment;
Log.d(DEBUG_TAG, "Restored ref to TagEditorFragment");
} else if (fragment instanceof RelationMembershipFragment) {
relationMembershipFragment = (RelationMembershipFragment) fragment;
Log.d(DEBUG_TAG, "Restored ref to RelationMembershipFragment");
} else if (fragment instanceof RelationMembersFragment) {
relationMembersFragment = (RelationMembersFragment) fragment;
Log.d(DEBUG_TAG, "Restored ref to RelationMembersFragment");
} else if (fragment instanceof PresetFragment) {
presetFragment = (PresetFragment) fragment;
Log.d(DEBUG_TAG, "Restored ref to PresetFragment");
} else {
Log.d(DEBUG_TAG, "Unknown fragment ...");
}
// hack to recreate the form ui when restoring as there is no callback that
// runs after the references here have been recreated
if (restoring && tagFormFragment != null && tagEditorFragment != null) {
tagsUpdated();
}
return fragment;
}
@Override
public void restoreState (Parcelable state, ClassLoader loader) {
Log.d(DEBUG_TAG, "restoreState");
super.restoreState(state, loader);
restoring = true;
Log.d(DEBUG_TAG, "restoreState done");
}
}
private class PageChangeListener extends ViewPager.SimpleOnPageChangeListener {
@Override
public void onPageSelected(int page) {
Log.d(DEBUG_TAG,"page " + page + " selected");
if (formEnabled && page == tagFormFragmentPosition && tagFormFragment != null) {
tagFormFragment.update();
}
}
}
/**
* Removes an old RecentPresetView and replaces it by a new one (to update it)
*/
void recreateRecentPresetView() {
if (usePaneLayout) {
FragmentManager fm = getSupportFragmentManager();
Fragment recentPresetsFragment = fm.findFragmentByTag(RECENTPRESETS_FRAGMENT);
if (recentPresetsFragment != null) {
((RecentPresetsFragment)recentPresetsFragment).recreateRecentPresetView();
}
} else {
if (tagFormFragment != null) {
tagFormFragment.recreateRecentPresetView();
} else {
tagEditorFragment.recreateRecentPresetView();
}
}
}
@Override
public void onBackPressed() {
// sendResultAndFinish();
if (tagFormFragment != null) {
tagFormFragment.updateEditorFromText(); // update any non-synced changes to the editor fragment
}
ArrayList<LinkedHashMap<String, String>> currentTags = tagEditorFragment.getUpdatedTags();
HashMap<Long,String> currentParents = null;
ArrayList<RelationMemberDescription> currentMembers = null;
if (relationMembershipFragment != null) {
currentParents = relationMembershipFragment.getParentRelationMap();
}
if (relationMembersFragment != null) {
currentMembers = new ArrayList<RelationMemberDescription>(); // FIXME
if (types[0].equals(Relation.NAME)) { // FIXME
currentMembers = relationMembersFragment.getMembersList();
}
}
// if we haven't edited just exit
if (!same(currentTags,originalTags) // tags different
|| ((currentParents != null && !currentParents.equals(originalParents)) && !(originalParents==null && (currentParents == null || currentParents.size()==0))) // parents changed
|| (elements[0] != null && elements[0].getName().equals(Relation.NAME) && (currentMembers != null && !sameMembers(currentMembers,originalMembers)))) {
new AlertDialog.Builder(this)
.setNeutralButton(R.string.cancel, null)
.setNegativeButton(R.string.tag_menu_revert,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
doRevert();
}
})
.setPositiveButton(R.string.tag_menu_exit_no_save,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
PropertyEditor.super.onBackPressed();
}
}).create().show();
} else {
PropertyEditor.super.onBackPressed();
}
}
/*
* Revert changes in all fragments
*/
private void doRevert() {
if (tagEditorFragment != null) {
tagEditorFragment.doRevert();
}
if (relationMembershipFragment != null) {
relationMembershipFragment.doRevert();
}
if (relationMembersFragment != null) {
relationMembersFragment.doRevert();
}
}
/**
* Get current values from the fragments and end the activity
*/
void sendResultAndFinish() {
ArrayList<LinkedHashMap<String,String>> currentTags = tagEditorFragment.getUpdatedTags();
// for (LinkedHashMap<String,String>map:currentTags) {
// for (String k:map.keySet()) {
// Log.d(DEBUG_TAG, "current key " + k + " " + map.get(k) );
// }
// }
// for (LinkedHashMap<String,String>map:originalTags) {
// for (String k:map.keySet()) {
// Log.d(DEBUG_TAG, "original key " + k + " " + map.get(k) );
// }
// }
// Save tags to our clipboard
LinkedHashMap<String,String> copiedTags = tagEditorFragment.getCopiedTags();
if (copiedTags != null) {
savingHelper.save(this, COPIED_TAGS_FILE, copiedTags, false);
}
// save any address tags for "last address tags"
if (currentTags != null && currentTags.size() == 1) {
Address.updateLastAddresses(tagEditorFragment, Util.getArrayListMap(currentTags.get(0)));// FIXME
}
Intent intent = new Intent();
HashMap<Long,String> currentParents = null;
ArrayList<RelationMemberDescription> currentMembers = null;
PropertyEditorData[] newData = new PropertyEditorData[currentTags.size()];
if (currentTags != null && currentTags.size() == 1) { // normal single mode, relations might have changed
currentParents = relationMembershipFragment.getParentRelationMap();
currentMembers = new ArrayList<RelationMemberDescription>(); //FIXME
if (types[0].endsWith(Relation.NAME)) {
currentMembers = relationMembersFragment.getMembersList();
}
if (!same(currentTags, originalTags) || !(originalParents==null && currentParents.size()==0) && !currentParents.equals(originalParents)
|| (elements != null && elements[0].getName().equals(Relation.NAME) && !currentMembers.equals(originalMembers))) {
// changes were made
Log.d(DEBUG_TAG, "saving tags");
for (int i=0;i<currentTags.size();i++) {
newData[i] = new PropertyEditorData(osmIds[i], types[i],
currentTags.get(i).equals(originalTags.get(i)) ? null : currentTags.get(i), null,
(originalParents==null && currentParents.size()==0) || currentParents.equals(originalParents)?null:currentParents, null,
currentMembers.equals(originalMembers)?null:currentMembers, null);
}
}
} else { // multi select just tags could have been changed
if (!same(currentTags, originalTags)) {
// changes were made
for (int i=0;i<currentTags.size();i++) {
newData[i] = new PropertyEditorData(osmIds[i], types[i],
currentTags.get(i).equals(originalTags.get(i)) ? null : currentTags.get(i), null, null, null, null, null);
}
}
}
intent.putExtra(TAGEDIT_DATA, newData);
setResult(RESULT_OK, intent);
finish();
}
/**
* Check if two lists of tags are the same
* Note: this considers order relevant
* @return
*/
private boolean same(ArrayList<LinkedHashMap<String, String>> tags1, ArrayList<LinkedHashMap<String, String>> tags2){
// check for tag changes
if (tags1.size() != tags2.size()) { /// serious error
return false;
}
for (int i=0;i<tags1.size();i++) {
if (!tags1.get(i).equals(tags2.get(i))) {
return false;
}
}
return true;
}
/**
* Check if two lists of RelationMembetDescription are the same
* Note: this considers order relevant
* @return
*/
private boolean sameMembers(ArrayList<RelationMemberDescription> rmds1, ArrayList<RelationMemberDescription> rmds2){
if (rmds1==null) {
return rmds2==null;
}
if (rmds2==null) {
return rmds1==null;
}
if (rmds1.size() != rmds2.size()) { /// serious error
return false;
}
for (int i=0;i<rmds1.size();i++) {
RelationMemberDescription rmd1 = rmds1.get(i);
RelationMemberDescription rmd2 = rmds2.get(i);
if (rmd1 == rmd2) {
continue;
}
if (rmd1 != null && !rmd1.equals(rmd2)) {
return false;
}
}
return true;
}
/** Save the state of this activity instance for future restoration.
* @param outState The object to receive the saved state.
*/
@Override
protected void onSaveInstanceState(final Bundle outState) {
Log.d(DEBUG_TAG,"onSaveInstaceState");
super.onSaveInstanceState(outState);
// no call through. We restore our state from scratch, auto-restore messes up the already loaded edit fields.
// outState.putSerializable(TAGEDIT_DATA, new PropertyEditorData(osmId, type, tagEditorFragment.getKeyValueMap(true), originalTags, relationMembershipFragment.getParentRelationMap(), originalParents, relationMembersFragment.getMembersList(), originalMembers));
outState.putInt(CURRENTITEM, mViewPager.getCurrentItem());
outState.putBoolean(PANELAYOUT, usePaneLayout);
}
/** When the Activity is interrupted, save MRUs and address cache*/
@Override
protected void onPause() {
running = false;
Preset[] presets = App.getCurrentPresets(this);
if (presets != null) {
for (Preset p:presets) {
if (p!=null) {
p.saveMRU();
}
}
}
Address.saveLastAddresses(this);
super.onPause();
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
Log.d(DEBUG_TAG,"onRestoreInstanceState");
super.onRestoreInstanceState(savedInstanceState);
Log.d(DEBUG_TAG,"onRestoreInstanceState done");
}
/**
* Insert a new row of key+value -edit-widgets if some text is entered into the current one.
*
* @author <a href="mailto:Marcus@Wolschon.biz">Marcus Wolschon</a>
*/
private class MyKeyListener implements OnKeyListener {
@Override
public boolean onKey(final View view, final int keyCode, final KeyEvent keyEvent) {
if (keyEvent.getAction() == KeyEvent.ACTION_UP || keyEvent.getAction() == KeyEvent.ACTION_MULTIPLE) {
if (view instanceof EditText) {
//on Enter -> goto next EditText
if (keyCode == KeyEvent.KEYCODE_ENTER) {
View nextView = view.focusSearch(View.FOCUS_RIGHT);
if (!(nextView instanceof EditText)) {
nextView = view.focusSearch(View.FOCUS_LEFT);
if (nextView != null) {
nextView = nextView.focusSearch(View.FOCUS_DOWN);
}
}
if (nextView != null && nextView instanceof EditText) {
nextView.requestFocus();
return true;
}
}
}
}
return false;
}
}
@Override
public void onPresetSelected(PresetItem item) {
onPresetSelected(item, false);
}
@Override
public void onPresetSelected(PresetItem item, boolean applyOptional) {
if (item != null) {
tagEditorFragment.applyPreset(item, applyOptional, true);
if (tagFormFragment != null) {
tagFormFragment.update();
mViewPager.setCurrentItem(tagFormFragmentPosition);
} else {
mViewPager.setCurrentItem(tagEditorFragmentPosition);
}
recreateRecentPresetView();
}
}
/**
* Allow ViewPager to work
*/
public void enablePaging() {
mViewPager.setPagingEnabled(true);
}
/**
* Disallow ViewPAger to work
*/
public void disablePaging() {
mViewPager.setPagingEnabled(false);
}
/**
* Allow presets to be applied
*/
public void enablePresets() {
if (usePaneLayout) {
FragmentManager fm = getSupportFragmentManager();
Fragment recentPresetsFragment = fm.findFragmentByTag(RECENTPRESETS_FRAGMENT);
if (recentPresetsFragment != null) {
((RecentPresetsFragment)recentPresetsFragment).enable();
}
} else {
tagEditorFragment.enableRecentPresets();
}
}
/**
* Disallow presets to be applied
*/
public void disablePresets() {
if (usePaneLayout) {
FragmentManager fm = getSupportFragmentManager();
Fragment recentPresetsFragment = fm.findFragmentByTag(RECENTPRESETS_FRAGMENT);
if (recentPresetsFragment != null) {
((RecentPresetsFragment)recentPresetsFragment).disable();
}
presetFragment.disable();
} else {
tagEditorFragment.disableRecentPresets();
}
}
@Override
public void updateSingleValue(String key, String value) {
if (tagEditorFragment != null) {
tagEditorFragment.updateSingleValue(key, value);
} else {
Log.e(DEBUG_TAG,"updateSingleValue tagEditorFragment is null");
}
}
@Override
public void updateTags(Map<String, String> tags, boolean flush) {
if (tagEditorFragment != null) {
tagEditorFragment.updateTags(tags, flush);
} else {
Log.e(DEBUG_TAG,"updateSingleValue tagEditorFragment is null");
}
}
@Override
public void revertTags() {
if (tagEditorFragment != null) {
tagEditorFragment.revertTags();
} else {
Log.e(DEBUG_TAG,"revertTags tagEditorFragment is null");
}
}
@Override
public void deleteTag(final String key) {
if (tagEditorFragment != null) {
tagEditorFragment.deleteTag(key);
} else {
Log.e(DEBUG_TAG,"deleteTag tagEditorFragment is null");
}
}
public ArrayList<LinkedHashMap<String, String>> getUpdatedTags() {
if (tagEditorFragment != null) {
return tagEditorFragment.getUpdatedTags();
} else {
Log.e(DEBUG_TAG,"getUpdatedTags tagEditorFragment is null");
return null;
}
}
@Override
public LinkedHashMap<String, String> getKeyValueMapSingle(
boolean allowBlanks) {
if (tagEditorFragment != null) {
return tagEditorFragment.getKeyValueMapSingle(allowBlanks);
} else {
Log.e(DEBUG_TAG,"getUpdatedTags tagEditorFragment is null");
return null;
}
}
@Override
public PresetItem getBestPreset() {
if (tagEditorFragment != null) {
return tagEditorFragment.getBestPreset();
} else {
Log.e(DEBUG_TAG,"getBestPreset tagEditorFragment is null");
return null;
}
}
@Override
public List<PresetItem> getSecondaryPresets() {
if (tagEditorFragment != null) {
return tagEditorFragment.getSecondaryPresets();
} else {
Log.e(DEBUG_TAG,"getSecondaryPresets tagEditorFragment is null");
return null;
}
}
@Override
public Map<String,PresetItem> getAllPresets() {
if (tagEditorFragment != null) {
return tagEditorFragment.getAllPresets();
} else {
Log.e(DEBUG_TAG,"getAllPresets tagEditorFragment is null");
return null;
}
}
@Override
public void updatePresets() {
if (tagEditorFragment != null) {
tagEditorFragment.updatePresets();
} else {
Log.e(DEBUG_TAG,"updatePresets tagEditorFragment is null");
}
}
@Override
public void predictAddressTags(boolean allowBlanks) {
if (tagEditorFragment != null) {
tagEditorFragment.predictAddressTags(allowBlanks);
} else {
Log.e(DEBUG_TAG,"predictAddressTags tagEditorFragment is null");
}
}
@Override
public void applyTagSuggestions(TagMap tags) {
if (tagEditorFragment != null) {
tagEditorFragment.applyTagSuggestions(tags);
} else {
Log.e(DEBUG_TAG,"applyTagSuggestions tagEditorFragment is null");
}
}
@Override
public boolean pasteIsPossible() {
if (tagEditorFragment != null) {
return tagEditorFragment.pasteIsPossible();
} else {
Log.e(DEBUG_TAG,"pasteIsPossible tagEditorFragment is null");
}
return false;
}
@Override
public boolean paste(boolean replace) {
if (tagEditorFragment != null) {
return tagEditorFragment.paste(replace);
} else {
Log.e(DEBUG_TAG,"paste tagEditorFragment is null");
}
return false;
}
@Override
public boolean pasteFromClipboardIsPossible() {
if (tagEditorFragment != null) {
return tagEditorFragment.pasteFromClipboardIsPossible();
} else {
Log.e(DEBUG_TAG,"pasteFromClipboardIsPossible tagEditorFragment is null");
}
return false;
}
@Override
public boolean pasteFromClipboard(boolean replace) {
if (tagEditorFragment != null) {
return tagEditorFragment.pasteFromClipboard(replace);
} else {
Log.e(DEBUG_TAG,"pasteFromClipboard tagEditorFragment is null");
}
return false;
}
@Override
public void copyTags(Map<String, String> tags) {
if (tagEditorFragment != null) {
tagEditorFragment.copyTags(tags);
} else {
Log.e(DEBUG_TAG,"copyTags tagEditorFragment is null");
}
}
@Override
public void tagsUpdated() {
if (tagFormFragment != null) {
tagFormFragment.tagsUpdated();
} else {
Log.e(DEBUG_TAG,"tagFormFragment is null");
}
}
@Override
public void typeUpdated(ElementType type) {
if (presetFragment != null) {
presetFragment.typeUpdated(type);
} else {
Log.e(DEBUG_TAG,"presetFragment is null");
}
}
/**
* Gets an adapter for the autocompletion of street names based on the neighborhood of the edited item.
* @param tagValues
* @return
*/
public ArrayAdapter<ValueWithCount> getStreetNameAdapter(ArrayList<String> tagValues) {
if (App.getDelegator() == null) {
return null;
}
if (streetNameAutocompleteAdapter == null) {
streetNameAutocompleteAdapter = new StreetTagValueAdapter(this,
R.layout.autocomplete_row, App.getDelegator(),
types[0], osmIds[0], tagValues); // FIXME
}
return streetNameAutocompleteAdapter;
}
/**
* Gets an adapter for the autocompletion of place names based on the neighborhood of the edited item.
* @return
*/
public ArrayAdapter<ValueWithCount> getPlaceNameAdapter(ArrayList<String> tagValues) {
if (App.getDelegator() == null) {
return null;
}
if (placeNameAutocompleteAdapter == null) {
placeNameAutocompleteAdapter = new PlaceTagValueAdapter(this,
R.layout.autocomplete_row, App.getDelegator(),
types[0], osmIds[0], tagValues); // FIXME
}
return placeNameAutocompleteAdapter;
}
OsmElement getElement() {
return elements[0]; // FIXME validate
}
/**
* Return if we are using the pave/tablet layout
*
* @return true is in pane mode
*/
boolean paneLayout() {
return usePaneLayout;
}
@Override
public void onSupportActionModeFinished(@NonNull ActionMode mode) {
super.onSupportActionModeFinished(mode);
}
@Override
/**
* Workaround for bug mentioned below
*/
public ActionMode startSupportActionMode(@NonNull final ActionMode.Callback callback) {
// Fix for bug https://code.google.com/p/android/issues/detail?id=159527
final ActionMode mode = super.startSupportActionMode(callback);
if (mode != null) {
mode.invalidate();
}
return mode;
}
@Override
/**
* A tag has been updated, reflect this in both editors
*/
public void save(String key, String value) {
updateSingleValue(key,value);
tagsUpdated();
}
@Override
public void applyPreset(PresetItem preset, boolean addOptional) {
tagEditorFragment.applyPreset(preset, true);
}
}