/*
* GeoSolutions - MapstoreMobile - GeoSpatial Framework on Android based devices
* Copyright (C) 2014 - 2016 GeoSolutions (www.geo-solutions.it)
*
* 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 it.geosolutions.geocollect.android.core.mission;
import it.geosolutions.android.map.model.query.BBoxQuery;
import it.geosolutions.android.map.model.query.BaseFeatureInfoQuery;
import it.geosolutions.android.map.utils.MapFilesProvider;
import it.geosolutions.android.map.utils.ZipFileManager;
import it.geosolutions.geocollect.android.app.BuildConfig;
import it.geosolutions.geocollect.android.core.GeoCollectApplication;
import it.geosolutions.geocollect.android.app.R;
import it.geosolutions.geocollect.android.core.login.utils.NetworkUtil;
import it.geosolutions.geocollect.android.core.mission.utils.MissionUtils;
import it.geosolutions.geocollect.android.core.mission.utils.PersistenceUtils;
import it.geosolutions.geocollect.android.core.mission.utils.SQLiteCascadeFeatureLoader;
import it.geosolutions.geocollect.android.core.widgets.dialog.UploadDialog;
import it.geosolutions.geocollect.model.config.MissionTemplate;
import it.geosolutions.geocollect.model.http.CommitResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import jsqlite.Database;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.SubMenu;
import com.actionbarsherlock.widget.SearchView;
import com.actionbarsherlock.widget.SearchView.OnQueryTextListener;
/**
* A list fragment representing a list of Pending Missions. This fragment also supports tablet devices by allowing list items to be given an
* 'activated' state upon selection. This helps indicate which item is currently being viewed in a {@link PendingMissionDetailFragment}.
* <p>
* Activities containing this fragment MUST implement the {@link Callbacks} interface.
*/
public class PendingMissionListFragment extends SherlockListFragment implements LoaderCallbacks<List<MissionFeature>>,
OnScrollListener, OnRefreshListener, OnQueryTextListener {
/**
* The serialization (saved instance state) Bundle key representing the activated item position. Only used on tablets.
*/
private static final String STATE_ACTIVATED_POSITION = "activated_position";
/**
* Fragment upload
*/
private static final String FRAGMENT_UPLOAD_DIALOG = "FRAGMENT_UPLOAD_DIALOG";
private static int CURRENT_LOADER_INDEX = 0;
private static final String TAG = "MISSION_LIST";
public static final String INFINITE_SCROLL = "INFINITE_SCROLL";
public static int ARG_ENABLE_GPS = 43231;
public static int RESET_MISSION_FEATURE_ID = 12345;
/**
* mode of this fragment
*/
public enum FragmentMode {
PENDING, CREATION
}
private FragmentMode mMode = FragmentMode.PENDING;
/**
* The fragment's current callback object, which is notified of list item clicks.
*/
private Callbacks listSelectionCallbacks = sDummyCallbacks;
/**
* The current activated item position. Only used on tablets.
*/
private int mActivatedPosition = ListView.INVALID_POSITION;
/**
* The adapter for the Feature
*/
private FeatureAdapter adapter;
// private FeatureAdapter missionAdapter;
/**
* Callback for the Loader
*/
private LoaderCallbacks<List<MissionFeature>> mCallbacks;
/**
* SwipeRefreshLayout, use by the swipeDown gesture
*/
ListFragmentSwipeRefreshLayout mSwipeRefreshLayout;
/**
* Boolean value that allow to start loading only once at time
*/
private boolean isLoading;
/**
* The template that provides the form and the
*/
private MissionTemplate missionTemplate;
/**
* page number for remote queries
*/
private int page = 0;
/**
* page size for remote queries
*/
private int pagesize = 250;
private View footer;
/**
* Main Activity's jsqlite Database instance reference
*/
private Database db;
private Button clearFilterBtn;
private SearchView searchView;
/**
* A callback interface that all activities containing this fragment must implement. This mechanism allows activities to be notified of item
* selections.
*/
public interface Callbacks {
/**
* Callback for when an item has been selected.
*/
public void onItemSelected(Object object);
}
/**
* A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
*/
private static Callbacks sDummyCallbacks = new Callbacks() {
@Override
public void onItemSelected(Object object) {
}
};
/**
* Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon screen orientation changes).
*/
public PendingMissionListFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v("MISSION_LIST_FRAGMENT", "onCreate()");
setRetainInstance(true);
PendingMissionListActivity activity = (PendingMissionListActivity) getSherlockActivity();
// setup the listView
missionTemplate = MissionUtils.getDefaultTemplate(activity);
((GeoCollectApplication) activity.getApplication()).setTemplate(missionTemplate);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
boolean usesDownloaded = prefs.getBoolean(PendingMissionListActivity.PREFS_USES_DOWNLOADED_TEMPLATE, false);
if (usesDownloaded) {
//int index = prefs.getInt(PendingMissionListActivity.PREFS_DOWNLOADED_TEMPLATE_INDEX, 0) + 1;
CURRENT_LOADER_INDEX = missionTemplate.getLoaderIndex();
}
adapter = new FeatureAdapter(activity, R.layout.mission_resource_row, missionTemplate);
setListAdapter(adapter);
// option menu
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
footer = View.inflate(getActivity(), R.layout.loading_footer, null);
// Get the list fragment's content view
final View listFragmentView = inflater.inflate(R.layout.mission_resource_list, container, false);
clearFilterBtn = (Button) listFragmentView.findViewById(R.id.clearFilterBtn);
if (clearFilterBtn != null) {
clearFilterBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (adapter != null) {
adapter.getFilter().filter("");
v.setVisibility(View.GONE);
}
if (searchView != null) {
searchView.setQuery("", false);
}
clearSpatialFilter();
getSherlockActivity().invalidateOptionsMenu();
}
});
}
// Now create a SwipeRefreshLayout to wrap the fragment's content view
mSwipeRefreshLayout = new ListFragmentSwipeRefreshLayout(getSherlockActivity());
// Add the list fragment's content view to the SwipeRefreshLayout, making sure that it fills
// the SwipeRefreshLayout
mSwipeRefreshLayout.addView(listFragmentView, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
// Make sure that the SwipeRefreshLayout will fill the fragment
mSwipeRefreshLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mSwipeRefreshLayout.setColorScheme(R.color.geosol_1, R.color.geosol_2, R.color.geosol_3, R.color.geosol_4);
mSwipeRefreshLayout.setOnRefreshListener(this);
// Now return the SwipeRefreshLayout as this fragment's content view
return mSwipeRefreshLayout;
// return inflater.inflate(R.layout.mission_resource_list, container, false);
}
/**
* hide loading bar and set loading task
*/
private void stopLoadingGUI() {
if (getSherlockActivity() != null) {
getSherlockActivity().setSupportProgressBarIndeterminateVisibility(false);
getSherlockActivity().setSupportProgressBarVisibility(false);
// getListView().removeFooterView(footer);
// Log.v(TAG, "task terminated");
}
adapter.notifyDataSetChanged();
isLoading = false;
if (mSwipeRefreshLayout != null) {
mSwipeRefreshLayout.setRefreshing(false);
}
}
/**
* Sets the view to show that no data are available
*/
private void setNoData() {
((TextView) getView().findViewById(R.id.empty_text)).setText(R.string.no_reporting_found);
getView().findViewById(R.id.progress_bar).setVisibility(TextView.GONE);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getActivity().getIntent().getBooleanExtra(INFINITE_SCROLL, false)) {
getListView().setOnScrollListener(this);
} else {
getListView().setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// Log.v("PMLF", "First: "+ firstVisibleItem + ", count: "+visibleItemCount+ ", total: "+totalItemCount);
if (firstVisibleItem == 0 || visibleItemCount == 0) {
mSwipeRefreshLayout.setEnabled(true);
} else {
mSwipeRefreshLayout.setEnabled(false);
}
}
});
}
// Restore the previously serialized activated item position.
if (savedInstanceState != null && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
}
MissionUtils.checkMapStyles(getResources(), missionTemplate);
startDataLoading(missionTemplate, CURRENT_LOADER_INDEX);
registerForContextMenu(getListView());
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.v("MISSION_LIST_FRAGMENT", "onAttach()");
// Activities containing this fragment must implement its callbacks.
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
// If a previous instance of the database was attached, the loader must be restarted
boolean needReload = false;
if (db != null && db.dbversion().equals("unknown")) {
needReload = true;
}
// Check for a database
if (getSherlockActivity() instanceof PendingMissionListActivity) {
Log.v(TAG, "Loader: Connecting to Activity database");
db = ((PendingMissionListActivity) getSherlockActivity()).spatialiteDatabase;
// restart the loader if needed
if (needReload) {
LoaderManager lm = getSherlockActivity().getSupportLoaderManager();
if (lm.getLoader(CURRENT_LOADER_INDEX) != null) {
lm.restartLoader(CURRENT_LOADER_INDEX, null, this);
}
}
} else {
Log.w(TAG, "Loader: Could not connect to Activity database");
}
listSelectionCallbacks = (Callbacks) activity;
}
/**
* Create the Menu visible on longpress on items
*/
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
// if this item is editable or uploadable, offer the possibility to "reset" the state -> delete its "sop" entry
if (v.getId() == getListView().getId()) {
ListView lv = (ListView) v;
final MissionTemplate template = MissionUtils.getDefaultTemplate(getActivity());
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
MissionFeature feature = (MissionFeature) lv.getItemAtPosition(info.position);
// identify edited/uploadable
if (feature.editing || (feature.typeName != null && feature.typeName.endsWith(MissionTemplate.NEW_NOTICE_SUFFIX))) {
// create a dialog to let the user clear this surveys data
String title = getString(R.string.survey);
if (feature.properties != null && feature.properties.get(template.nameField) != null) {
title = (String) feature.properties.get(template.nameField);
}
if (title != null) {
menu.setHeaderTitle(title);
}
menu.add(0, RESET_MISSION_FEATURE_ID, 0, getString(R.string.menu_clear_survey));
}
}
}
@Override
public boolean onContextItemSelected(android.view.MenuItem item) {
// user selected the option to reset, delete the edited item
if (item.getItemId() == RESET_MISSION_FEATURE_ID) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
final MissionFeature feature = (MissionFeature) getListView().getItemAtPosition(info.position);
final MissionTemplate template = MissionUtils.getDefaultTemplate(getActivity());
String title = (String) feature.properties.get(template.nameField);
Log.d(TAG, "missionfeature " + title + " selected to reset");
final String tableName;
final String fid = MissionUtils.getFeatureGCID(feature);
if(feature.typeName != null && feature.typeName.endsWith(MissionTemplate.NEW_NOTICE_SUFFIX)){
tableName = missionTemplate.schema_seg.localSourceStore + MissionTemplate.NEW_NOTICE_SUFFIX;
}else{
tableName = template.schema_sop.localFormStore;
}
new AlertDialog.Builder(getSherlockActivity()).setTitle(R.string.item_delete_title)
.setMessage(R.string.item_delete_message)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
// delete this new entry
PersistenceUtils.deleteMissionFeature(db, tableName,fid);
// if this entry was uploadable remove it from the list of uploadables
HashMap<String, ArrayList<String>> uploadables = PersistenceUtils.loadUploadables(getSherlockActivity());
if (uploadables.containsKey(tableName) && uploadables.get(tableName).contains(fid)) {
uploadables.get(tableName).remove(fid);
PersistenceUtils.saveUploadables(getSherlockActivity(), uploadables);
}
forceLoad();
getSherlockActivity().supportInvalidateOptionsMenu();
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// nothing
dialog.dismiss();
}
}).show();
return true;
}
return super.onContextItemSelected(item);
}
@Override
public void onDetach() {
super.onDetach();
// Reset the active callbacks interface to the dummy implementation.
listSelectionCallbacks = sDummyCallbacks;
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
listSelectionCallbacks.onItemSelected(getListAdapter().getItem(position));
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mActivatedPosition != ListView.INVALID_POSITION) {
// Serialize and persist the activated item position.
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
}
}
/**
* Turns on activate-on-click mode. When this mode is on, list items will be given the 'activated' state when touched.
*/
public void setActivateOnItemClick(boolean activateOnItemClick) {
// When setting CHOICE_MODE_SINGLE, ListView will automatically
// give items the 'activated' state when touched.
getListView().setChoiceMode(activateOnItemClick ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE);
}
private void setActivatedPosition(int position) {
if (position == ListView.INVALID_POSITION) {
getListView().setItemChecked(mActivatedPosition, false);
} else {
getListView().setItemChecked(position, true);
}
mActivatedPosition = position;
}
/**
* Creates the actionBar buttons
*/
@Override
public void onCreateOptionsMenu(final Menu menu, MenuInflater inflater) {
PendingMissionListActivity activity = (PendingMissionListActivity) getSherlockActivity();
// Are we on a tablet?
boolean mTwoPane = activity.findViewById(R.id.pendingmission_detail_container) != null;
// upload
if (missionTemplate != null && missionTemplate.schema_sop != null
&& missionTemplate.schema_sop.localFormStore != null) {
String newFeaturesTableName = missionTemplate.schema_seg.localSourceStore + MissionTemplate.NEW_NOTICE_SUFFIX ;
String surveysTableName = missionTemplate.schema_sop.localFormStore;
HashMap<String, ArrayList<String>> uploadables = PersistenceUtils.loadUploadables(activity);
if (
(uploadables.containsKey(newFeaturesTableName) && uploadables.get(newFeaturesTableName).size() > 0)
|| (uploadables.containsKey(surveysTableName) && uploadables.get(surveysTableName).size() > 0)) {
// there are uploadable entries, add a menu item
inflater.inflate(R.menu.uploadable, menu);
}
}
// Display the Search only if it is not already filtered
inflater.inflate(R.menu.searchable, menu);
// get searchview and add querylistener
// menu.findItem(R.id.search).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setQueryHint(getString(R.string.search_missions));
searchView.setOnQueryTextListener(this);
searchView.setOnQueryTextFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
// keyboard was closed, collapse search action view
menu.findItem(R.id.search).collapseActionView();
}
}
});
// If SRID is set, a filter exists
SharedPreferences sp = activity.getSharedPreferences(
SQLiteCascadeFeatureLoader.PREF_NAME,
Context.MODE_PRIVATE);
if (sp.contains(SQLiteCascadeFeatureLoader.FILTER_SRID)) {
inflater.inflate(R.menu.filterable, menu);
}
// TODO use the FAB on the main layout instead of inside the list fragment
if (mTwoPane && missionTemplate != null) {
if (missionTemplate.schema_seg != null) {
inflater.inflate(R.menu.creating, menu);
}
}
inflater.inflate(R.menu.map_full, menu);
if (missionTemplate != null) {
if (missionTemplate.schema_sop != null
&& (missionTemplate.schema_sop.orderingField != null || missionTemplate.orderingField != null)) {
inflater.inflate(R.menu.orderable, menu);
if (sp.getBoolean(SQLiteCascadeFeatureLoader.REVERSE_ORDER_PREF, false)) {
MenuItem orderButton = menu.findItem(R.id.order);
orderButton.setIcon(R.drawable.ic_action_sort_by_size);
}
}
}
// Creating the Overflow Menu
SubMenu subMenu1 = menu.addSubMenu(0, R.id.overflow_menu, 90, "Overflow Menu");
SubMenu subMenu2 = subMenu1.addSubMenu(0, R.id.overflow_order, Menu.NONE, R.string.order_by_ellipsis);
subMenu2.setGroupCheckable(1, true, true);
subMenu2.add(1, R.id.overflow_order_az, Menu.NONE, R.string.ordering_az).setChecked(
!sp.getBoolean(SQLiteCascadeFeatureLoader.ORDER_BY_DISTANCE, false));
subMenu2.add(1, R.id.overflow_order_distance, Menu.NONE, R.string.ordering_distance).setChecked(
sp.getBoolean(SQLiteCascadeFeatureLoader.ORDER_BY_DISTANCE, false));
subMenu2.setGroupCheckable(1, true, true);
subMenu1.add(0, R.id.overflow_refresh, Menu.NONE, R.string.update);
MenuItem subMenu1Item = subMenu1.getItem();
subMenu1Item.setIcon(R.drawable.ic_action_overflow);
subMenu1Item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
@Override
public boolean onQueryTextSubmit(String query) {
return query.length() > 0;
}
@Override
public boolean onQueryTextChange(String newText) {
if (adapter != null) {
// filters the adapters entries using its overridden filter
adapter.getFilter().filter(newText);
}
if (adapter.isEmpty()) {
setNoData();
if (clearFilterBtn != null) {
clearFilterBtn.setVisibility(View.VISIBLE);
}
} else {
if (clearFilterBtn != null) {
clearFilterBtn.setVisibility(View.GONE);
}
}
return true;
}
/*
* (non-Javadoc)
*
* @see com.actionbarsherlock.app.SherlockFragment#onOptionsItemSelected(com.actionbarsherlock.view.MenuItem)
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.create_new) {
PendingMissionListActivity.checkGPSandStartCreation(getSherlockActivity());
return true;
} else if (id == R.id.order) {
orderButtonCallback();
SharedPreferences sp = getActivity().getSharedPreferences(SQLiteCascadeFeatureLoader.PREF_NAME,
Context.MODE_PRIVATE);
if (sp.getBoolean(SQLiteCascadeFeatureLoader.REVERSE_ORDER_PREF, false)) {
item.setIcon(R.drawable.ic_action_sort_by_size);
} else {
item.setIcon(R.drawable.ic_action_reverse_sort);
}
return true;
} else if (id == R.id.overflow_order_az) {
setOrdering(false);
item.setChecked(true);
return true;
} else if (id == R.id.overflow_order_distance) {
setOrdering(true);
item.setChecked(true);
return true;
} else if (id == R.id.filter) {
// Clear the Spatial Filter
if (getSherlockActivity().getSupportLoaderManager().getLoader(CURRENT_LOADER_INDEX) != null) {
clearSpatialFilter();
item.setVisible(false);
}
return true;
} else if (id == R.id.upload) {
if (!NetworkUtil.isOnline(getSherlockActivity())) {
Toast.makeText(getSherlockActivity(), getString(R.string.login_not_online), Toast.LENGTH_LONG).show();
return true;
}
// upload
if (missionTemplate != null) {
String title = null;
String uploadList = null;
String itemList = "";
HashMap<String, ArrayList<String>> uploadables = PersistenceUtils
.loadUploadables(getSherlockActivity());
ArrayList<MissionFeature> uploads = new ArrayList<MissionFeature>();
final String newFeaturesTableName = missionTemplate.schema_seg.localSourceStore + MissionTemplate.NEW_NOTICE_SUFFIX ;
final String surveysTableName = missionTemplate.schema_sop.localFormStore;
// Check if there are new items to upload
itemList = updateFeatureUploadList(itemList, uploadables, uploads, newFeaturesTableName);
// Check if there are surveys to upload
itemList = updateFeatureUploadList(itemList, uploadables, uploads, surveysTableName);
uploadList = getResources().getQuantityString(R.plurals.upload_intro, uploads.size()) + ":\n" + itemList;
title = getString(R.string.upload_title);
final ArrayList<MissionFeature> finalUploads = uploads;
// show the dialog to confirm the upload
new AlertDialog.Builder(getSherlockActivity()).setTitle(title).setMessage(uploadList)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// do the upload
// get urls
final String newFeaturesUrl = missionTemplate.seg_form.url;
final String surveysUrl = missionTemplate.sop_form.url;
final String newFeaturesMediaUrl = missionTemplate.seg_form.mediaurl;
final String surveysMediaUrl = missionTemplate.sop_form.mediaurl;
// check urls
if ( newFeaturesUrl == null
|| newFeaturesMediaUrl == null
|| surveysUrl == null
|| surveysMediaUrl == null) {
Log.e(UploadDialog.class.getSimpleName(), "no url or mediaurl available for upload, cannot continue");
Toast.makeText(getSherlockActivity(), R.string.error_sending_data, Toast.LENGTH_LONG).show();
return;
}
// as done before in "SendAction" ....
android.support.v4.app.FragmentManager fm = getSherlockActivity()
.getSupportFragmentManager();
Fragment mTaskFragment = fm.findFragmentByTag(FRAGMENT_UPLOAD_DIALOG);
if (mTaskFragment == null) {
FragmentTransaction ft = fm.beginTransaction();
mTaskFragment = new UploadDialog() {
@Override
public void onFinish(Activity ctx, CommitResponse result) {
if (result != null && result.isSuccess()) {
Toast.makeText(getSherlockActivity(),
getResources().getString(R.string.data_send_success),
Toast.LENGTH_LONG).show();
// update adapter and menu
/*if (mMode == FragmentMode.CREATION) {
fillCreatedMissionFeatureAdapter();
} else {
*/ onRefresh();
//}
getSherlockActivity().supportInvalidateOptionsMenu();
super.onFinish(ctx, result);
} else {
Toast.makeText(getSherlockActivity(), R.string.error_sending_data,
Toast.LENGTH_LONG).show();
super.onFinish(ctx, result);
}
}
};
// fill up the args for the upload dialog
Bundle arguments = new Bundle();
arguments.putSerializable(UploadDialog.PARAMS.MISSION_TEMPLATE, missionTemplate);
arguments.putSerializable(UploadDialog.PARAMS.FEATURES, finalUploads);
mTaskFragment.setArguments(arguments);
((DialogFragment) mTaskFragment).setCancelable(false);
ft.add(mTaskFragment, FRAGMENT_UPLOAD_DIALOG);
ft.commit();
}
}
}).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// nothing, close dialog
}
}).show();
}
} else if (id == R.id.full_map) {
// Open the Map
if (getSherlockActivity() instanceof PendingMissionListActivity) {
((PendingMissionListActivity) getSherlockActivity()).launchFullMap();
}
return true;
} else if (id == R.id.overflow_refresh) {
onRefresh();
return true;
}
return super.onOptionsItemSelected(item);
}
/**
*
*/
public void clearSpatialFilter() {
SharedPreferences sp = getSherlockActivity().getSharedPreferences(SQLiteCascadeFeatureLoader.PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
// Change the ordering
editor.remove(SQLiteCascadeFeatureLoader.FILTER_N);
editor.remove(SQLiteCascadeFeatureLoader.FILTER_S);
editor.remove(SQLiteCascadeFeatureLoader.FILTER_W);
editor.remove(SQLiteCascadeFeatureLoader.FILTER_E);
editor.remove(SQLiteCascadeFeatureLoader.FILTER_SRID);
editor.commit();
adapter.clear();
if(getSherlockActivity().getSupportLoaderManager().getLoader(CURRENT_LOADER_INDEX) != null){
getSherlockActivity().getSupportLoaderManager().getLoader(CURRENT_LOADER_INDEX).forceLoad();
}
}
/**
* @param itemList
* @param uploadables
* @param uploads
* @param newFeaturesTableName
* @return
*/
public String updateFeatureUploadList(String itemList, HashMap<String, ArrayList<String>> uploadables,
ArrayList<MissionFeature> uploads, final String newFeaturesTableName) {
List<String> uploadIDs = uploadables.get(newFeaturesTableName);
ArrayList<MissionFeature> features;
if(uploadIDs!= null && uploadIDs.size() > 0){
features = MissionUtils.getMissionFeatures(newFeaturesTableName, db);
for (MissionFeature f : features) {
String featureID = MissionUtils.getFeatureGCID(f);
if (uploadIDs.contains(featureID)) {
// this new entry is "uploadable" , add it
uploads.add(f);
// Fill the Text for the user
itemList = updateListText(itemList, f, featureID);
}
}
}
return itemList;
}
/**
* @param itemList
* @param f
* @param featureID
* @return
*/
public String updateListText(String itemList, MissionFeature f, String featureID) {
if ( f.properties != null
&& f.properties.containsKey(missionTemplate.nameField)
&& f.properties.get(missionTemplate.nameField) != null) {
itemList += (String) f.properties.get(missionTemplate.nameField) + "\n";
} else {
// survey feature do not contain the "nameField", get it from the according mission
for (int i = 0; i < adapter.getCount(); i++) {
MissionFeature mf = adapter.getItem(i);
if (MissionUtils.getFeatureGCID(mf).equals(featureID)) {
if ( mf.properties != null
&& mf.properties.containsKey(missionTemplate.nameField)) {
itemList += (String) mf.properties.get(missionTemplate.nameField) + "\n";
break;
}
}
}
}
return itemList;
}
/**
*
*/
private void orderButtonCallback() {
// Get it from the mission template
SharedPreferences sp = getSherlockActivity().getSharedPreferences(SQLiteCascadeFeatureLoader.PREF_NAME,
Context.MODE_PRIVATE);
boolean reverse = sp.getBoolean(SQLiteCascadeFeatureLoader.REVERSE_ORDER_PREF, false);
SharedPreferences.Editor editor = sp.edit();
// Change the ordering
Log.v(TAG, "Changing to " + reverse);
editor.putBoolean(SQLiteCascadeFeatureLoader.REVERSE_ORDER_PREF, !reverse);
editor.commit();
forceLoad();
}
/**
*
*/
private void setOrdering(boolean useDistance) {
SharedPreferences sp = getSherlockActivity().getSharedPreferences(SQLiteCascadeFeatureLoader.PREF_NAME,
Context.MODE_PRIVATE);
// This check pass only if the value is not already set to the right value.
if (sp.getBoolean(SQLiteCascadeFeatureLoader.ORDER_BY_DISTANCE, !useDistance) != useDistance) {
SharedPreferences.Editor editor = sp.edit();
// Set the ordering
Log.v(TAG, "useDistance: " + useDistance);
editor.putBoolean(SQLiteCascadeFeatureLoader.ORDER_BY_DISTANCE, useDistance);
editor.commit();
forceLoad();
}
}
/*
* (non-Javadoc)
*
* @see android.widget.AbsListView.OnScrollListener#onScrollStateChanged(android.widget.AbsListView, int)
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see android.widget.AbsListView.OnScrollListener#onScroll(android.widget.AbsListView, int, int, int)
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
Log.v("PMLF", "First: " + firstVisibleItem + ", count: " + visibleItemCount + ", total: " + totalItemCount);
if (firstVisibleItem == 0 || visibleItemCount == 0) {
mSwipeRefreshLayout.setEnabled(true);
} else {
mSwipeRefreshLayout.setEnabled(false);
}
// check if applicable
if (adapter == null) {
return;
}
if (adapter.getCount() == 0) {
return;
}
if (getSherlockActivity().getSupportLoaderManager().getLoader(CURRENT_LOADER_INDEX) == null) {
return;
}
// if the last item is visible and can load more resources
// load more resources
int l = visibleItemCount + firstVisibleItem;
if (l >= totalItemCount && !isLoading) {
// It is time to add new data. We call the listener
// getListView().addFooterView(footer);
isLoading = true;
loadMore();
}
}
/*
* (non-Javadoc)
*
* @see android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader(int, android.os.Bundle)
*/
@Override
public Loader<List<MissionFeature>> onCreateLoader(int id, Bundle arg1) {
getSherlockActivity().setSupportProgressBarIndeterminateVisibility(true);
// getSherlockActivity().getSupportActionBar();
Log.d("MISSION_LIST", "onCreateLoader() for id " + id);
return MissionUtils.createMissionLoader(missionTemplate, getSherlockActivity(), page, pagesize, db);
}
/**
*
*/
/*
* (non-Javadoc)
*
* @see android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished(android.support.v4.content.Loader, java.lang.Object)
*/
@Override
public void onLoadFinished(Loader<List<MissionFeature>> loader, List<MissionFeature> results) {
if (results == null) {
Toast.makeText(getSherlockActivity(), R.string.error_connectivity_problem, Toast.LENGTH_SHORT).show();
setNoData();
} else {
Log.d(TAG, "loader returned " + results.size());
// add loaded resources to the listView
adapter = new FeatureAdapter(getSherlockActivity(), R.layout.mission_resource_row, missionTemplate);
adapter.setItems(results);
setListAdapter(adapter);
if (adapter.isEmpty()) {
setNoData();
} else {
}
}
stopLoadingGUI();
// if this fragment is visible to the user, check if the background data for the current template is available
if (getUserVisibleHint()) {
checkIfBackgroundIsAvailableForTemplate();
}
}
/**
* checks if background data for the current template is available if not, the user is asked to download
*/
private void checkIfBackgroundIsAvailableForTemplate() {
boolean exists = MissionUtils.checkTemplateForBackgroundData(getActivity(), missionTemplate);
if (!exists) {
final HashMap<String, Integer> urls = MissionUtils.getContentUrlsAndFileAmountForTemplate(missionTemplate);
if (BuildConfig.DEBUG) {
Log.i(TAG, "downloading " + urls.toString());
}
Resources res = getResources();
for (String url : urls.keySet()) {
final String mount = MapFilesProvider.getEnvironmentDirPath(getActivity());
String dialogMessage = res.getQuantityString(R.plurals.dialog_message_with_amount, urls.get(url),
urls.get(url));
new ZipFileManager(getActivity(), mount, MapFilesProvider.getBaseDir(), url, null, dialogMessage) {
@Override
public void launchMainActivity(final boolean success) {
// TODO apply ? this was earlier in StartupActivity
// if (getActivity().getApplication() instanceof GeoCollectApplication) {
// ((GeoCollectApplication) getActivity().getApplication()).setupMBTilesBackgroundConfiguration();
// }
// launch.putExtra(PendingMissionListFragment.INFINITE_SCROLL, false);
if (success) {
Toast.makeText(getActivity(), getString(R.string.download_successfull), Toast.LENGTH_SHORT)
.show();
}
}
};
}
}
}
/*
* (non-Javadoc)
*
* @see android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset(android.support.v4.content.Loader)
*/
@Override
public void onLoaderReset(Loader<List<MissionFeature>> arg0) {
adapter.clear();
if (mSwipeRefreshLayout != null)
mSwipeRefreshLayout.setRefreshing(false);
}
/**
* Loads more data from the Loader
*/
protected void loadMore() {
if (getSherlockActivity().getSupportLoaderManager().getLoader(CURRENT_LOADER_INDEX) != null) {
page++;
getSherlockActivity().getSupportLoaderManager().restartLoader(CURRENT_LOADER_INDEX, null, this);
}
}
/**
* Create the data loader and bind the loader to the parent callbacks
*
* @param URL (not used for now)
* @param loaderIndex a unique id for query loader
*/
private void startDataLoading(MissionTemplate t, int loaderIndex) {
// initialize Load Manager
mCallbacks = this;
// reset page
LoaderManager lm = getSherlockActivity().getSupportLoaderManager();
adapter.clear();
page = 0;
lm.initLoader(loaderIndex, null, this);
}
/**
* Refresh the list
*/
@Override
public void onResume() {
super.onResume();
// Start data loading
if (getSherlockActivity().getSupportLoaderManager().getLoader(CURRENT_LOADER_INDEX) != null) {
getSherlockActivity().getSupportLoaderManager().getLoader(CURRENT_LOADER_INDEX).forceLoad();
}
getSherlockActivity().supportInvalidateOptionsMenu();
}
/**
* Handle the results
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult()");
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PendingMissionListActivity.SPATIAL_QUERY) {
if (data != null && data.hasExtra("query")) {
BaseFeatureInfoQuery query = data.getParcelableExtra("query");
// Create task query
if (query instanceof BBoxQuery) {
BBoxQuery bbox = (BBoxQuery) query;
// TODO: Refactor the Preferences Double Storage
// This is just a fast hack to store Doubles in Long but It should be handled by a more clean utility class
// Maybe extend the Preference Editor?
Log.v(TAG, "Received:\nN: " + bbox.getN() + "\nN: " + bbox.getS() + "\nE: " + bbox.getE() + "\nW: "
+ bbox.getW() + "\nSRID: " + bbox.getSrid());
SharedPreferences sp = getSherlockActivity().getSharedPreferences(
SQLiteCascadeFeatureLoader.PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putLong(SQLiteCascadeFeatureLoader.FILTER_N, Double.doubleToRawLongBits(bbox.getN()));
editor.putLong(SQLiteCascadeFeatureLoader.FILTER_S, Double.doubleToRawLongBits(bbox.getS()));
editor.putLong(SQLiteCascadeFeatureLoader.FILTER_W, Double.doubleToRawLongBits(bbox.getW()));
editor.putLong(SQLiteCascadeFeatureLoader.FILTER_E, Double.doubleToRawLongBits(bbox.getE()));
editor.putInt(SQLiteCascadeFeatureLoader.FILTER_SRID, Integer.parseInt(bbox.getSrid()));
editor.commit();
forceLoad();
Toast.makeText(getActivity(), getString(R.string.selection_filtered), Toast.LENGTH_SHORT).show();
}
// else if(query instanceof CircleQuery){ }
} else {
// No result, clean the filter
SharedPreferences sp = getSherlockActivity().getSharedPreferences(SQLiteCascadeFeatureLoader.PREF_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove(SQLiteCascadeFeatureLoader.FILTER_N);
editor.remove(SQLiteCascadeFeatureLoader.FILTER_S);
editor.remove(SQLiteCascadeFeatureLoader.FILTER_W);
editor.remove(SQLiteCascadeFeatureLoader.FILTER_E);
editor.remove(SQLiteCascadeFeatureLoader.FILTER_SRID);
editor.commit();
forceLoad();
}
} else if (requestCode == ARG_ENABLE_GPS) {
if(BuildConfig.DEBUG){
Log.d(TAG, "back from GPS settings");
}
PendingMissionListActivity.startMissionFeatureCreation(getSherlockActivity());
} else {
missionTemplate = ((GeoCollectApplication) getActivity().getApplication()).getTemplate();
CURRENT_LOADER_INDEX = missionTemplate.getLoaderIndex();
adapter.setTemplate(missionTemplate);
forceLoad();
}
}
/**
* Callback for the {@link SwipeRefreshLayout}
*/
@Override
public void onRefresh() {
SharedPreferences sp = getSherlockActivity().getSharedPreferences(SQLiteCascadeFeatureLoader.PREF_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
// Reset the preference to force update
editor.putLong(SQLiteCascadeFeatureLoader.LAST_UPDATE_PREF, 0);
editor.commit();
forceLoad();
if (mSwipeRefreshLayout != null)
mSwipeRefreshLayout.setRefreshing(true);
}
/**
* Trigger a reload of the current loader
*/
private void forceLoad() {
adapter.clear();
Loader<Object> l = getSherlockActivity().getSupportLoaderManager().getLoader(CURRENT_LOADER_INDEX);
if (l != null) {
l.forceLoad();
}
}
/**
* Utility method to check whether a {@link ListView} can scroll up from it's current position. Handles platform version differences, providing
* backwards compatible functionality where needed.
*/
private static boolean canListViewScrollUp(ListView listView) {
if (android.os.Build.VERSION.SDK_INT >= 14) {
// For ICS and above we can call canScrollVertically() to determine this
return ViewCompat.canScrollVertically(listView, -1);
} else {
// Pre-ICS we need to manually check the first visible item and the child view's top
// value
return listView.getChildCount() > 0
&& (listView.getFirstVisiblePosition() > 0 || listView.getChildAt(0).getTop() < listView
.getPaddingTop());
}
}
/**
* Sub-class of {@link android.support.v4.widget.SwipeRefreshLayout} for use in this {@link android.support.v4.app.ListFragment}. The reason that
* this is needed is because {@link android.support.v4.widget.SwipeRefreshLayout} only supports a single child, which it expects to be the one
* which triggers refreshes. In our case the layout's child is the content view returned from
* {@link android.support.v4.app.ListFragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)} which is a
* {@link android.view.ViewGroup}.
*
* <p>
* To enable 'swipe-to-refresh' support via the {@link android.widget.ListView} we need to override the default behavior and properly signal when
* a gesture is possible. This is done by overriding {@link #canChildScrollUp()}.
*/
private class ListFragmentSwipeRefreshLayout extends SwipeRefreshLayout {
public ListFragmentSwipeRefreshLayout(Context context) {
super(context);
}
/**
* As mentioned above, we need to override this method to properly signal when a 'swipe-to-refresh' is possible.
*
* @return true if the {@link android.widget.ListView} is visible and can scroll up.
*/
@Override
public boolean canChildScrollUp() {
final ListView listView = getListView();
if (listView.getVisibility() == View.VISIBLE) {
return canListViewScrollUp(listView);
} else {
return false;
}
}
}
/**
* switches between pending mission mode and created mission mode
*
* @param FragmentMode the new mode
*/
public void switchAdapter() {
// remove longclicklistener
getListView().setOnItemLongClickListener(null);
adapter.setTemplate(missionTemplate);
setListAdapter(adapter);
// invalidate actionbar
getSherlockActivity().supportInvalidateOptionsMenu();
}
/**
* sets the current missionTemplate of this fragments list
*
* @param pMissionTemplate the template to apply
*/
public void setTemplate(final MissionTemplate pMissionTemplate) {
missionTemplate = pMissionTemplate;
}
/**
* @param restarts the loader with a certain index
*/
public void restartLoader(final int index) {
startDataLoading(missionTemplate, index);
CURRENT_LOADER_INDEX = index;
}
public MissionTemplate getCurrentMissionTemplate() {
return missionTemplate;
}
}