/*
* 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.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcEvent;
import android.os.Bundle;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.TextView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import eu.geopaparazzi.library.database.GPLog;
import eu.geopaparazzi.library.style.ColorUtilities;
import eu.geopaparazzi.library.util.DynamicDoubleArray;
import eu.geopaparazzi.library.util.GPDialogs;
import eu.geopaparazzi.library.util.LibraryConstants;
import eu.geopaparazzi.library.util.Utilities;
import eu.geopaparazzi.core.GeopaparazziApplication;
import eu.geopaparazzi.core.R;
import eu.geopaparazzi.core.database.DaoGpsLog;
import eu.geopaparazzi.core.database.objects.ItemComparators;
import eu.geopaparazzi.core.database.objects.Line;
import eu.geopaparazzi.core.database.objects.LogMapItem;
import eu.geopaparazzi.core.database.objects.MapItem;
import eu.geopaparazzi.core.database.objects.SerializableLogs;
import eu.geopaparazzi.core.mapview.MapsSupportService;
import eu.geopaparazzi.core.utilities.Constants;
/**
* Gpx listing activity.
*
* @author Andrea Antonello (www.hydrologis.com)
*/
public class GpsDataListActivity extends AppCompatActivity implements
NfcAdapter.CreateNdefMessageCallback, NfcAdapter.OnNdefPushCompleteCallback {
private static final int GPSDATAPROPERTIES_RETURN_CODE = 668;
private NfcAdapter mNfcAdapter;
private LogMapItem[] gpslogItems;
private Comparator<MapItem> mapItemSorter = new ItemComparators.MapItemIdComparator(true);
private String logSendingMimeType = "application/eu.geopaparazzi.gpsdatalog_msg";
private SharedPreferences mPeferences;
private ListView mListView;
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_gpsdatalist);
Toolbar toolbar = (Toolbar) findViewById(eu.geopaparazzi.mapsforge.R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mListView = (ListView) findViewById(R.id.gpsdatalist);
mPeferences = PreferenceManager.getDefaultSharedPreferences(this);
// Check for available NFC Adapter
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter != null) {
mNfcAdapter.setNdefPushMessageCallback(this, this);
mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
}
handleNotes();
}
@Override
protected void onResume() {
super.onResume();
// PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
// nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
// Check to see if a Beam launched this Activity
String action = getIntent().getAction();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
processNfcIntent(getIntent());
}
refreshList(true);
}
void processNfcIntent(Intent intent) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs == null) return;
// only one message sent during the beam
NdefMessage msg = (NdefMessage) rawMsgs[0];
// record 0 contains the MIME type, record 1 is the AAR, if present
NdefRecord ndefRecord = msg.getRecords()[0];
byte[] type = ndefRecord.getType();
if (type == null || !new String(type).equals(logSendingMimeType)) {
return;
}
final byte[] nfcBytes = ndefRecord.getPayload();
try {
final SerializableLogs logs = Utilities.deserializeObject(nfcBytes, SerializableLogs.class);
for (int i = 0; i < logs.getSize(); i++) {
LogMapItem log = logs.getLogAt(i);
Line logData = logs.getLogDataAt(i);
DaoGpsLog daoGpsLog = new DaoGpsLog();
long logId = daoGpsLog.addGpsLog(log.getStartTime(), log.getEndTime(), -1, log.getName(), log.getWidth(), log.getColor(), true);
SQLiteDatabase sqliteDatabase = GeopaparazziApplication.getInstance().getDatabase();
sqliteDatabase.beginTransaction();
try {
List<String> dateList = logData.getDateList();
DynamicDoubleArray lonList = logData.getLonList();
DynamicDoubleArray latList = logData.getLatList();
DynamicDoubleArray altimList = logData.getAltimList();
int size = dateList.size();
for (int j = 0; j < size; j++) {
double lon = lonList.get(j);
double lat = latList.get(j);
double altim = altimList.get(j);
long time = Long.parseLong(dateList.get(j));
daoGpsLog.addGpsLogDataPoint(sqliteDatabase, logId, lon, lat, altim, time);
}
sqliteDatabase.setTransactionSuccessful();
intent.removeExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
} catch (Exception e) {
GPLog.error(GpsDataListActivity.this, e.getLocalizedMessage(), e);
throw new IOException(e.getLocalizedMessage());
} finally {
sqliteDatabase.endTransaction();
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
GPDialogs.infoDialog(GpsDataListActivity.this, getString(R.string.incoming_logs_added) + logs.getSize(), null);
}
});
} catch (Exception e) {
GPLog.error(GpsDataListActivity.this, e.getLocalizedMessage(), e);
}
}
@Override
public NdefMessage createNdefMessage(NfcEvent event) {
try {
List<LogMapItem> logsList = DaoGpsLog.getGpslogs();
SerializableLogs logs = new SerializableLogs();
for (int i = 0; i < logsList.size(); i++) {
LogMapItem logMapItem = logsList.get(i);
if (logMapItem.isVisible()) {
Line line = DaoGpsLog.getGpslogAsLine(logMapItem.getLogID(), -1);
logs.addLog(logMapItem, line);
}
}
byte[] logBytes = Utilities.serializeObject(logs);
NdefMessage msg = new NdefMessage(NdefRecord.createMime(
logSendingMimeType, logBytes)
/**
* The Android Application Record (AAR) is commented out. When a device
* receives a push with an AAR in it, the application specified in the AAR
* is guaranteed to run. The AAR overrides the tag dispatch system.
* You can add it back in to guarantee that this
* activity starts when receiving a beamed message. For now, this code
* uses the tag dispatch system.
*/
//,NdefRecord.createApplicationRecord("com.examples.nfcbeam")
);
return msg;
} catch (IOException e) {
GPLog.error(this, "Error in sending logs.", e);
}
return null;
}
@Override
public void onNdefPushComplete(NfcEvent event) {
//This callback happens on a binder thread, don't update
// the UI directly from this method.
runOnUiThread(new Runnable() {
@Override
public void run() {
GPDialogs.infoDialog(GpsDataListActivity.this, getString(R.string.logs_sent), null);
}
});
}
@Override
public void onNewIntent(Intent intent) {
// onResume gets called after this to handle the intent
setIntent(intent);
}
private void refreshList(boolean doReread) {
if (GPLog.LOG_HEAVY)
GPLog.addLogEntry(this, "refreshing gps maps list"); //$NON-NLS-1$
gpslogItems = new LogMapItem[0];
try {
if (doReread) {
List<LogMapItem> logsList = DaoGpsLog.getGpslogs();
Collections.sort(logsList, mapItemSorter);
gpslogItems = logsList.toArray(new LogMapItem[logsList.size()]);
}
} catch (IOException e) {
GPLog.error(this, e.getLocalizedMessage(), e);
}
ArrayAdapter<MapItem> arrayAdapter = new ArrayAdapter<MapItem>(this, R.layout.activity_gpsdatalist_row, gpslogItems) {
class ViewHolder {
TextView nameView;
CheckBox visibleView;
Button colorView;
Button propertiesButton;
}
@Override
public View getView(final int position, View cView, ViewGroup parent) {
ViewHolder holder;
// Recycle existing view if passed as parameter
View rowView = cView;
if (rowView == null) {
LayoutInflater inflater = getLayoutInflater();
rowView = inflater.inflate(R.layout.activity_gpsdatalist_row, parent, false);
holder = new ViewHolder();
holder.nameView = (TextView) rowView.findViewById(R.id.filename);
holder.visibleView = (CheckBox) rowView.findViewById(R.id.visible);
holder.colorView = (Button) rowView.findViewById(R.id.colorButton);
holder.propertiesButton = (Button) rowView.findViewById(R.id.propertiesButton);
rowView.setTag(holder);
} else {
holder = (ViewHolder) rowView.getTag();
}
final MapItem item = gpslogItems[position];
Drawable background = holder.colorView.getBackground();
if (background instanceof GradientDrawable) {
int color = ColorUtilities.toColor(item.getColor());
GradientDrawable gd = (GradientDrawable) background;
gd.setStroke(1, Color.BLACK);
gd.setColor(color);
}
holder.nameView.setText(item.getName());
holder.visibleView.setChecked(item.isVisible());
holder.visibleView.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
item.setVisible(isChecked);
item.setDirty(true);
}
});
holder.propertiesButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(GpsDataListActivity.this, GpsLogPropertiesActivity.class);
intent.putExtra(Constants.PREFS_KEY_GPSLOG4PROPERTIES, gpslogItems[position]);
startActivityForResult(intent, GPSDATAPROPERTIES_RETURN_CODE);
}
});
return rowView;
}
};
mListView.setAdapter(arrayAdapter);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (GPLog.LOG_HEAVY)
GPLog.addLogEntry(this, "Activity returned"); //$NON-NLS-1$
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case (GPSDATAPROPERTIES_RETURN_CODE): {
if (resultCode == Activity.RESULT_OK) {
double lon = data.getDoubleExtra(LibraryConstants.LONGITUDE, 0d);
double lat = data.getDoubleExtra(LibraryConstants.LATITUDE, 0d);
// Intent intent = getIntent();
// intent.putExtra(LibraryConstants.LATITUDE, lat);
// intent.putExtra(LibraryConstants.LONGITUDE, lon);
// setResult(Activity.RESULT_OK, intent);
Intent intent = new Intent(this, MapsSupportService.class);
intent.putExtra(MapsSupportService.CENTER_ON_POSITION_REQUEST, true);
intent.putExtra(LibraryConstants.LONGITUDE, lon);
intent.putExtra(LibraryConstants.LATITUDE, lat);
startService(intent);
finish();
}
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_gpsdatalist, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_selectall) {
try {
DaoGpsLog.setLogsVisibility(true);
refreshList(true);
} catch (IOException e) {
GPLog.error(this, null, e); //$NON-NLS-1$
}
} else if (item.getItemId() == R.id.action_unselectall) {
try {
DaoGpsLog.setLogsVisibility(false);
refreshList(true);
} catch (IOException e) {
GPLog.error(this, null, e); //$NON-NLS-1$
}
} else if (item.getItemId() == R.id.action_merge) {
try {
mergeSelected();
} catch (IOException e) {
GPLog.error(this, e.getLocalizedMessage(), e);
}
} else if (item.getItemId() == R.id.action_notesproperties) {
Intent intent = new Intent(GpsDataListActivity.this, NotesPropertiesActivity.class);
startActivity(intent);
}
return super.onOptionsItemSelected(item);
}
private void mergeSelected() throws IOException {
final List<LogMapItem> selected = new ArrayList<>();
for (LogMapItem mapItem : gpslogItems) {
if (mapItem.isVisible()) {
selected.add(mapItem);
}
}
if (selected.size() < 2) {
return;
}
int logsNum = selected.size();
String message = logsNum + " " + getString(R.string.logs_will_be_merged);
GPDialogs.yesNoMessageDialog(this, message, new Runnable() {
@Override
public void run() {
long mainId = selected.get(0).getId();
for (int i = 1; i < selected.size(); i++) {
MapItem mapItem = selected.get(i);
long id = mapItem.getId();
try {
DaoGpsLog.mergeLogs(id, mainId);
} catch (IOException e) {
GPLog.error(this, null, e); //$NON-NLS-1$
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
refreshList(true);
}
});
}
}, null);
}
@Override
protected void onPause() {
try {
boolean oneVisible = false;
for (MapItem item : gpslogItems) {
if (item.isDirty()) {
DaoGpsLog.updateLogProperties(item.getId(), item.getColor(), item.getWidth(), item.isVisible(), null);
item.setDirty(false);
}
if (!oneVisible && item.isVisible()) {
oneVisible = true;
}
}
// TODO
// DataManager.getInstance().setLogsVisible(oneVisible);
} catch (IOException e) {
GPLog.error(this, e.getLocalizedMessage(), e);
e.printStackTrace();
}
super.onPause();
}
private void handleNotes() {
}
}