/*
* Geopaparazzi - Digital field mapping on Android based devices
* Copyright (C) 2016 HydroloGIS (www.hydrologis.com)
*
* 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 <http://www.gnu.org/licenses/>.
*/
package eu.geopaparazzi.core.ui.activities;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import eu.geopaparazzi.library.core.ResourcesManager;
import eu.geopaparazzi.library.database.ANote;
import eu.geopaparazzi.library.database.DefaultHelperClasses;
import eu.geopaparazzi.library.database.GPLog;
import eu.geopaparazzi.library.database.IImagesDbHelper;
import eu.geopaparazzi.library.database.Image;
import eu.geopaparazzi.library.forms.FormActivity;
import eu.geopaparazzi.library.forms.FormInfoHolder;
import eu.geopaparazzi.library.forms.FormUtilities;
import eu.geopaparazzi.library.images.ImageUtilities;
import eu.geopaparazzi.library.share.ShareUtilities;
import eu.geopaparazzi.library.util.AppsUtilities;
import eu.geopaparazzi.library.util.GPDialogs;
import eu.geopaparazzi.library.util.LibraryConstants;
import eu.geopaparazzi.library.util.PositionUtilities;
import eu.geopaparazzi.library.util.StringAsyncTask;
import eu.geopaparazzi.library.util.UrlUtilities;
import eu.geopaparazzi.core.GeopaparazziApplication;
import eu.geopaparazzi.core.R;
import eu.geopaparazzi.core.database.DaoImages;
import eu.geopaparazzi.core.database.DaoNotes;
import eu.geopaparazzi.core.database.objects.ItemComparators;
import eu.geopaparazzi.core.database.objects.Note;
import eu.geopaparazzi.core.mapview.MapviewActivity;
/**
* Notes listing activity.
*
* @author Andrea Antonello (www.hydrologis.com)
*/
public class NotesListActivity extends AppCompatActivity {
public static final String CURRENT_NOTES_COMPARATOR_INDEX = "CURRENT_NOTES_COMPARATOR_INDEX";
private String SHARE_NOTE_WITH = "";
private List<ANote> allNotesList = new ArrayList<>();
private List<ANote> visibleNotesList = new ArrayList<>();
// private Comparator<ANote> notesSorter = new ItemComparators.NotesComparator(false);
private ArrayAdapter<ANote> arrayAdapter;
private EditText filterText;
private String share;
private String edit;
private String delete;
private String asSelection;
private String allNotesSubmenu;
private String selectAll;
private String invertSelection;
private String deleteSelected;
private String view;
private ListView listView;
private String[] checkItems;
private Comparator<ANote>[] comparators;
private int currentComparatorIndex = 0;
private SharedPreferences mPreferences;
private StringAsyncTask deletionTask;
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_noteslist);
Toolbar toolbar = (Toolbar) findViewById(eu.geopaparazzi.mapsforge.R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
double[] mapCenterFromPreferences = PositionUtilities.getMapCenterFromPreferences(mPreferences, true, true);
double lon = mapCenterFromPreferences[0];
double lat = mapCenterFromPreferences[1];
currentComparatorIndex = mPreferences.getInt(CURRENT_NOTES_COMPARATOR_INDEX, 0);
checkItems = new String[]{//
getString(R.string.sortby_none),//
getString(R.string.sortby_name),//
getString(R.string.sortby_name_inverse),//
getString(R.string.sortby_mapcenterdist), //
getString(R.string.sortby_mapcenterdist_inverse)//
};
comparators = new Comparator[]{//
null, //
new ItemComparators.NotesComparator(),//
new ItemComparators.NotesComparator(true),//
new ItemComparators.NotesDistanceFromPointComparator(lon, lat),//
new ItemComparators.NotesDistanceFromPointComparator(lon, lat, true),//
};
listView = (ListView) findViewById(R.id.notesList);
SHARE_NOTE_WITH = getString(eu.geopaparazzi.library.R.string.share_note_with);
share = getString(R.string.share);
edit = getString(R.string.edit);
view = getString(R.string.view);
delete = getString(R.string.delete);
asSelection = getString(R.string.use_as_selection);
allNotesSubmenu = getString(R.string.all_notes_submenu);
selectAll = getString(R.string.select_all);
invertSelection = getString(R.string.invert_selection);
deleteSelected = getString(R.string.delete_selected);
refreshList();
filterText = (EditText) findViewById(R.id.search_box);
filterText.addTextChangedListener(filterTextWatcher);
}
@Override
protected void onResume() {
super.onResume();
refreshList();
}
protected void onDestroy() {
if (deletionTask != null) deletionTask.dispose();
filterText.removeTextChangedListener(filterTextWatcher);
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_noteslist, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_sort) {
DialogInterface.OnClickListener dialogListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
currentComparatorIndex = which;
SharedPreferences.Editor editor = mPreferences.edit();
editor.putInt(CURRENT_NOTES_COMPARATOR_INDEX, which);
editor.apply();
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.select_sort_mode);
builder.setSingleChoiceItems(checkItems, currentComparatorIndex, dialogListener);
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
try {
refreshList();
} catch (Exception e) {
GPLog.error(this, null, e);
}
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
return super.onOptionsItemSelected(item);
}
private void refreshList() {
if (GPLog.LOG_HEAVY)
GPLog.addLogEntry(this, "refreshing notes list"); //$NON-NLS-1$
try {
visibleNotesList.clear();
collectAllNotes();
visibleNotesList.addAll(allNotesList);
if (comparators[currentComparatorIndex] != null) {
Collections.sort(visibleNotesList, comparators[currentComparatorIndex]);
}
} catch (IOException e) {
GPLog.error(this, e.getLocalizedMessage(), e);
e.printStackTrace();
}
redoAdapter();
}
private void collectAllNotes() throws IOException {
allNotesList.clear();
List<Note> tmpNotesList = DaoNotes.getNotesList(null, false);
allNotesList.addAll(tmpNotesList);
List<Image> imagesList = DaoImages.getImagesList(false, true);
allNotesList.addAll(imagesList);
}
private void filterList(String filterText) {
if (GPLog.LOG_HEAVY)
GPLog.addLogEntry(this, "filter notes list"); //$NON-NLS-1$
try {
collectAllNotes();
visibleNotesList.clear();
filterText = filterText.toLowerCase();
for (ANote note : allNotesList) {
String name = note.getName();
String nameLower = name.toLowerCase();
if (nameLower.contains(filterText)) {
visibleNotesList.add(note);
}
}
} catch (IOException e) {
GPLog.error(this, e.getLocalizedMessage(), e);
e.printStackTrace();
}
redoAdapter();
}
private void redoAdapter() {
arrayAdapter = new ArrayAdapter<ANote>(this, R.layout.activity_noteslist_row, visibleNotesList) {
class ViewHolder {
CheckBox checkButton;
TextView notesText;
Button goButton;
Button moreButton;
}
@Override
public View getView(int position, View rowView, ViewGroup parent) {
ViewHolder holder;
// Recycle existing view if passed as parameter
if (rowView == null) {
LayoutInflater inflater = getLayoutInflater();
rowView = inflater.inflate(R.layout.activity_noteslist_row, parent, false);
holder = new ViewHolder();
holder.checkButton = (CheckBox) rowView.findViewById(R.id.selectedCheckBox);
holder.notesText = (TextView) rowView.findViewById(R.id.notesrowtext);
holder.goButton = (Button) rowView.findViewById(R.id.gobutton);
holder.moreButton = (Button) rowView.findViewById(R.id.morebutton);
rowView.setTag(holder);
} else {
holder = (ViewHolder) rowView.getTag();
}
final ANote currentNote = visibleNotesList.get(position);
final CheckBox checkBox = holder.checkButton;
checkBox.setChecked(currentNote.isChecked());
checkBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
checkBox.setChecked(isChecked);
currentNote.setChecked(isChecked);
}
});
holder.notesText.setText(currentNote.getName());
holder.goButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent = getIntent();
intent.putExtra(LibraryConstants.LATITUDE, currentNote.getLat());
intent.putExtra(LibraryConstants.LONGITUDE, currentNote.getLon());
intent.putExtra(LibraryConstants.ZOOMLEVEL, 16);
// if (getParent() == null) {
setResult(Activity.RESULT_OK, intent);
finish();
}
});
final Button moreButton2 = holder.moreButton;
moreButton2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
openMoreMenu(moreButton2, currentNote);
}
});
return rowView;
}
};
listView.setAdapter(arrayAdapter);
}
private void openMoreMenu(Button button, final ANote currentNote) {
String editLabel = null;
if (currentNote instanceof Note) {
Note note = (Note) currentNote;
if (note.getForm() == null || note.getForm().length() == 0) {
// simple note
editLabel = null;
} else {
editLabel = edit;
}
} else if (currentNote instanceof Image) {
// image
editLabel = view;
}
PopupMenu popup = new PopupMenu(this, button);
if (editLabel != null)
popup.getMenu().add(editLabel);
popup.getMenu().add(share);
popup.getMenu().add(delete);
popup.getMenu().add(asSelection);
SubMenu subMenu = popup.getMenu().addSubMenu(allNotesSubmenu);
subMenu.add(selectAll);
subMenu.add(invertSelection);
subMenu.add(deleteSelected);
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
String actionName = item.getTitle().toString();
if (actionName.equals(share)) {
shareNote(currentNote);
} else if (actionName.equals(edit) || actionName.equals(view)) {
editNote(currentNote);
} else if (actionName.equals(delete)) {
deleteNote(currentNote);
} else if (actionName.equals(asSelection)) {
String name = currentNote.getName();
filterText.setText(name);
} else if (actionName.equals(selectAll)) {
for (ANote aNote : visibleNotesList) {
aNote.setChecked(true);
}
arrayAdapter.notifyDataSetChanged();
} else if (actionName.equals(invertSelection)) {
for (ANote aNote : visibleNotesList) {
aNote.setChecked(!aNote.isChecked());
}
arrayAdapter.notifyDataSetChanged();
} else if (actionName.equals(deleteSelected)) {
deleteSelectedNotes();
}
return true;
}
});
popup.show();
}
private void shareNote(ANote currentNote) {
float lat = (float) currentNote.getLat();
float lon = (float) currentNote.getLon();
String osmUrl = UrlUtilities.osmUrlFromLatLong(lat, lon, true, false);
if (currentNote instanceof Note) {
Note note = (Note) currentNote;
if (note.getForm() == null || note.getForm().length() == 0) {
// simple note
String text = note.getName();
text = text + "\n" + osmUrl;
ShareUtilities.shareText(NotesListActivity.this, SHARE_NOTE_WITH, text);
} else {
String description = note.getDescription();
String form = note.getForm();
try {
String formText = FormUtilities.formToPlainText(form, false);
formText = formText + "\n" + osmUrl;
if (form.length() > 0 && !description.equals(LibraryConstants.OSM)) {
// double altim = note.getAltim();
IImagesDbHelper imageHelper = DefaultHelperClasses.getDefaulfImageHelper();
File tempDir = ResourcesManager.getInstance(GeopaparazziApplication.getInstance()).getTempDir();
// for now only one image is shared
List<String> imageIds = note.getImageIds();
File imageFile = null;
if (imageIds.size() > 0) {
String imageId = imageIds.get(0);
Image image = imageHelper.getImage(Long.parseLong(imageId));
String imageName = image.getName();
imageFile = new File(tempDir, imageName);
byte[] imageData = imageHelper.getImageDataById(image.getImageDataId(), null);
ImageUtilities.writeImageDataToFile(imageData, imageFile.getAbsolutePath());
}
if (imageFile != null) {
ShareUtilities.shareTextAndImage(NotesListActivity.this, SHARE_NOTE_WITH, formText,
imageFile);
} else {
ShareUtilities.shareText(NotesListActivity.this, SHARE_NOTE_WITH, formText);
}
}
} catch (Exception e) {
GPLog.error(this, null, e); //$NON-NLS-1$
GPDialogs.errorDialog(NotesListActivity.this, e, null);
}
}
} else if (currentNote instanceof Image) {
Image image = (Image) currentNote;
try {
File tempDir = ResourcesManager.getInstance(NotesListActivity.this).getTempDir();
String ext = ".jpg";
if (image.getName().endsWith(".png"))
ext = ".png";
File imageFile = new File(tempDir, ImageUtilities.getTempImageName(ext));
byte[] imageData = new DaoImages().getImageData(image.getId());
ImageUtilities.writeImageDataToFile(imageData, imageFile.getAbsolutePath());
if (imageFile.exists()) {
ShareUtilities.shareTextAndImage(NotesListActivity.this, SHARE_NOTE_WITH, osmUrl, imageFile);
} else {
GPDialogs.errorDialog(NotesListActivity.this, new IOException("The image is missing: "
+ imageFile), null);
}
} catch (Exception e) {
GPLog.error(this, null, e); //$NON-NLS-1$
}
}
}
private void editNote(ANote currentNote) {
if (currentNote instanceof Note) {
Note note = (Note) currentNote;
if (note.getForm() == null || note.getForm().length() == 0) {
// can't edit simple notes
GPDialogs.warningDialog(this,
getString(R.string.only_complex_notes_editing), null);
} else {
String description = note.getDescription();
double lat = note.getLat();
double lon = note.getLon();
String form = note.getForm();
if (form.length() > 0 && !description.equals(LibraryConstants.OSM)) {
double altim = note.getAltim();
Intent formIntent = new Intent(this, FormActivity.class);
FormInfoHolder formInfoHolder = new FormInfoHolder();
formInfoHolder.sectionName = currentNote.getName();
formInfoHolder.formName = null;
formInfoHolder.noteId = note.getId();
formInfoHolder.longitude = lon;
formInfoHolder.latitude = lat;
formInfoHolder.elevation = altim;
formInfoHolder.sectionObjectString = form;
formInfoHolder.objectExists = true;
formIntent.putExtra(FormInfoHolder.BUNDLE_KEY_INFOHOLDER, formInfoHolder);
this.startActivityForResult(formIntent, MapviewActivity.FORMUPDATE_RETURN_CODE);
}
}
} else if (currentNote instanceof Image) {
try {
Image image = (Image) currentNote;
byte[] imageData = new DaoImages().getImageData(image.getId());
AppsUtilities.showImage(imageData, image.getName(), this);
} catch (Exception e) {
GPLog.error(this, null, e);
}
}
}
private void deleteNote(final ANote currentNote) {
GPDialogs.yesNoMessageDialog(NotesListActivity.this, getString(R.string.prompt_delete_note),
new Runnable() {
public void run() {
new AsyncTask<String, Void, String>() {
protected String doInBackground(String... params) {
return "";
}
protected void onPostExecute(String response) {
try {
if (currentNote instanceof Note) {
DaoNotes.deleteNote(currentNote.getId());
} else if (currentNote instanceof Image) {
DaoImages.deleteImages(currentNote.getId());
}
refreshList();
} catch (IOException e) {
GPLog.error(this, e.getLocalizedMessage(), e);
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.getLocalizedMessage(),
Toast.LENGTH_LONG).show();
}
}
}.execute((String) null);
}
}, null
);
}
private void deleteSelectedNotes() {
GPDialogs.yesNoMessageDialog(NotesListActivity.this, getString(R.string.prompt_delete_selected_notes),
new Runnable() {
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
int total = 0;
for (ANote aNote : visibleNotesList) {
if (aNote.isChecked()) {
total++;
}
}
deletionTask = new StringAsyncTask(NotesListActivity.this) {
protected String doBackgroundWork() {
try {
int index = 0;
for (ANote aNote : visibleNotesList) {
if (aNote.isChecked()) {
if (aNote instanceof Note) {
DaoNotes.deleteNote(aNote.getId());
} else if (aNote instanceof Image) {
DaoImages.deleteImages(aNote.getId());
}
publishProgress(index);
index++;
}
}
} catch (Exception e) {
return getString(R.string.error_while_removing_notes) + e.getLocalizedMessage();
}
return "";
}
protected void doUiPostWork(String response) {
dispose();
if (response.length() != 0) {
GPDialogs.warningDialog(NotesListActivity.this, response, null);
} else {
refreshList();
}
}
};
deletionTask.setProgressDialog(null, getString(R.string.removing_notes), false, total);
deletionTask.execute();
}
});
}
}, null
);
}
private TextWatcher filterTextWatcher = new TextWatcher() {
public void afterTextChanged(Editable s) {
// ignore
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// ignore
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
// arrayAdapter.getFilter().filter(s);
filterList(s.toString());
}
};
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (GPLog.LOG_ABSURD)
GPLog.addLogEntry(this, "Activity returned"); //$NON-NLS-1$
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case (MapviewActivity.FORMUPDATE_RETURN_CODE): {
if (resultCode == Activity.RESULT_OK) {
FormInfoHolder formInfoHolder = (FormInfoHolder) data.getSerializableExtra(FormInfoHolder.BUNDLE_KEY_INFOHOLDER);
if (formInfoHolder != null) {
try {
long noteId = formInfoHolder.noteId;
String nameStr = formInfoHolder.renderingLabel;
String jsonStr = formInfoHolder.sectionObjectString;
DaoNotes.updateForm(noteId, nameStr, jsonStr);
} catch (Exception e) {
GPLog.error(this, null, e);
GPDialogs.warningDialog(this, getString(eu.geopaparazzi.library.R.string.notenonsaved), null);
}
}
}
break;
}
}
}
public void clearFilter(View view) {
filterText.setText("");
}
}