/* * GeoSolutions - MapstoreMobile - GeoSpatial Framework on Android based devices * Copyright (C) 2014 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.form; import it.geosolutions.android.map.view.MapViewManager; import it.geosolutions.geocollect.android.app.BuildConfig; import it.geosolutions.geocollect.android.app.R; import it.geosolutions.geocollect.android.core.form.utils.FormUtils; import it.geosolutions.geocollect.android.core.mission.MissionFeature; import it.geosolutions.geocollect.android.core.mission.PendingMissionDetailFragment; import it.geosolutions.geocollect.android.core.mission.PendingMissionListActivity; 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.SpatialiteUtils; import it.geosolutions.geocollect.android.core.widgets.EnableSwipeViewPager; import it.geosolutions.geocollect.android.core.widgets.UILImageAdapter; import it.geosolutions.geocollect.android.core.wmc.ui.WMCForm; import it.geosolutions.geocollect.android.core.wmc.ui.WMCForm.OnDisconnectListener; import it.geosolutions.geocollect.model.config.MissionTemplate; import it.geosolutions.geocollect.model.viewmodel.Field; import it.geosolutions.geocollect.model.viewmodel.Page; import it.geosolutions.geocollect.model.viewmodel.type.XType; import java.util.HashMap; import java.util.List; import org.mapsforge.android.maps.MapActivity; import org.mapsforge.android.maps.MapView; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.NavUtils; import android.support.v4.view.ViewPager; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.GridView; import android.widget.Toast; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.SherlockFragment; import com.actionbarsherlock.app.SherlockFragmentActivity; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; public class FormEditActivity extends SherlockFragmentActivity implements MapActivity { public static final int CONTEXT_IMAGE_ACTION_DELETE = 8001; public static final int FORM_CREATE_NEW_MISSIONFEATURE = 1234; /** * The {@link android.support.v4.view.PagerAdapter} that will provide fragments representing * each object in a collection. We use a {@link android.support.v4.app.FragmentStatePagerAdapter} * derivative, which will destroy and re-create fragments as needed, saving and restoring their * state in the process. This is important to conserve memory and is a best practice when * allowing navigation between objects in a potentially large collection. */ FormCollectionPagerAdapter formPagerAdapter; /** * The {@link android.support.v4.view.ViewPager} that will display the object collection. */ public EnableSwipeViewPager mViewPager; /** * the MapViewManager */ private MapViewManager mapViewManager =new MapViewManager(); /** * Spatialite Database for persistence */ public jsqlite.Database spatialiteDatabase; /** * ListView for Photo Gallery */ AbsListView listView; /** * Singleton of the ImageLoader, used by the Photo Gallery */ ImageLoader imageLoader = ImageLoader.getInstance(); /** * Stores the image urls to be shown */ //String[] imageUrls; /** * Options of the Photo Gallery */ DisplayImageOptions options; MissionFeature mMission; String mMissionTableName; boolean mCreateMissionFeature; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.form_edit_pager); //flag to identify if this activity is for creating a sopralluogo (mission) or a segnalazione (mission) mCreateMissionFeature = getIntent().getExtras().getBoolean(PendingMissionListActivity.ARG_CREATE_MISSIONFEATURE); Log.d(FormEditActivity.class.getSimpleName(), "will create a missionFeature "+ Boolean.toString(mCreateMissionFeature)); // Initialize database if(spatialiteDatabase == null){ spatialiteDatabase = SpatialiteUtils.openSpatialiteDB(this, "geocollect/genova.sqlite"); // Database is correctly open if(spatialiteDatabase != null && !spatialiteDatabase.dbversion().equals("unknown")){ MissionTemplate t = MissionUtils.getDefaultTemplate(this); if(!PersistenceUtils.createOrUpdateTable(spatialiteDatabase,t.schema_sop.localFormStore, t.schema_sop.fields)){ Log.e(PendingMissionListActivity.class.getSimpleName(), "error creating "+t.schema_sop.localFormStore+" table "); } } } /* create the adapter for the pages. it contains a reference to the * mapViewManager to delete the mapViews when destroy items * */ formPagerAdapter = new FormCollectionPagerAdapter(getSupportFragmentManager(),this){ MapViewManager mapManager = mapViewManager; @Override public Object instantiateItem(ViewGroup viewGroup, int position) { Object obj = super.instantiateItem(viewGroup, position); return obj; } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); //get the page and check if map is present //I assume only one map in the form //TODO manage multiple maps MissionTemplate t = MissionUtils.getDefaultTemplate(getActivityContext()); Page p = null; if(mCreateMissionFeature){ p = t.seg_form.pages.get(position); }else{ p = t.sop_form.pages.get(position); } if(p!=null && p.title != null){ List<Field> fields = null; if(mCreateMissionFeature){ fields = t.seg_form.pages.get(position).fields; }else{ fields = t.sop_form.pages.get(position).fields; } for(Field f: fields){ if(XType.mapViewPoint.equals(f.xtype)){ mapManager.destroyMapViews(); } } } //if the item to be destroyed is a WMCForm and it is connected disconnect silently if(object instanceof WMCForm){ WMCForm form = (WMCForm) object; if(form.isConnected()){ if(BuildConfig.DEBUG){ Log.i("FormEditActivity", "disconnecting silently"); } form.disconnect(false); } } } }; // Set up action bar. final ActionBar actionBar = getSupportActionBar(); // Specify that the Home button should show an "Up" caret, indicating that touching the // button will take the user one step up in the application's hierarchy. actionBar.setDisplayHomeAsUpEnabled(true); // Set up the ViewPager, attaching the adapter. mViewPager = (EnableSwipeViewPager) findViewById(R.id.pager); mViewPager.setAdapter(formPagerAdapter); //set a listener for page change events mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int nPage) { MissionTemplate t =MissionUtils.getDefaultTemplate(mViewPager.getContext()); Page p = null; if(mCreateMissionFeature){ p = t.seg_form.pages.get(nPage); }else{ p = t.sop_form.pages.get(nPage); } if(p.attributes != null && p.attributes.containsKey("message")){ Toast.makeText(mViewPager.getContext(), (String) p.attributes.get("message"), Toast.LENGTH_LONG).show(); } //TODO disable scroll if needed } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // TODO save } @Override public void onPageScrollStateChanged(int state) { //nothing to do } }); if(mCreateMissionFeature){ MissionTemplate t = MissionUtils.getDefaultTemplate(mViewPager.getContext()); mMissionTableName= t.schema_seg.localSourceStore+ MissionTemplate.NEW_NOTICE_SUFFIX; //when intent has missionFeature, edit it if(getIntent().getExtras().containsKey(PendingMissionDetailFragment.ARG_ITEM_FEATURE)){ mMission = (MissionFeature) getIntent().getExtras().get(PendingMissionDetailFragment.ARG_ITEM_FEATURE); }else{ //otherwise create a new one mMission = new MissionFeature(); mMission.properties = new HashMap<String, Object>(); mMission.typeName = mMissionTableName; //insert empty row Long id = PersistenceUtils.getIDforNewMissionFeatureEntry(spatialiteDatabase, mMissionTableName); if(id == null){ Log.e(FormEditActivity.class.getSimpleName(), "could not retrieve max for "+mMissionTableName); } Log.d(FormEditActivity.class.getSimpleName(), "reference id for missionFeature " + String.valueOf(id)); mMission.id = String.valueOf(id); PersistenceUtils.insertCreatedMissionFeature(spatialiteDatabase, mMissionTableName, id); } } } @Override public void onBackPressed() { boolean destroy = true; for(Fragment fragment : getSupportFragmentManager().getFragments()){ if(fragment instanceof WMCForm){ final WMCForm form = (WMCForm) fragment; if(form.isConnected()){ //don't destroy now destroy = false; //ask user if to disconnect form.showAskForDisconnectDialog(FormEditActivity.this, new OnDisconnectListener() { @Override public void onDisconnect() { FormEditActivity.super.onBackPressed(); } }); } } } if(destroy){ super.onBackPressed(); } } /** * A {@link android.support.v4.app.FragmentStatePagerAdapter} that returns a fragment * representing an object in the collection. */ public class FormCollectionPagerAdapter extends FragmentStatePagerAdapter { MissionTemplate t; /** * Constructor for <FormCollectionPageAdapter> * To avoid memory problems, on page creation, the mapViewManager will be passed to the created * fragment. The reference to the mapViewManager will allow the fragment to dispose the MapView * when removed. * @param fm the <FragmentManager> * @param c the context * @param mapViewManager the MapViewManager */ public FormCollectionPagerAdapter(FragmentManager fm,Context c) { super(fm); t = MissionUtils.getDefaultTemplate(c); } @Override public SherlockFragment getItem(int i) { SherlockFragment fragment = null; if(!mCreateMissionFeature){ if(t != null && t.seg_form.pages.size() > 0){ Page page = t.sop_form.pages.get(i); if(page.fields != null && page.fields.size() > 0){ for(Field field : page.fields){ if(field.xtype == XType.wmc){ if(BuildConfig.DEBUG){ Log.i("FormEditActivity", "this is a wmc page"); } fragment = new WMCForm(); } } } } if(fragment == null){ fragment = new FormPageFragment(); } }else{ fragment = new CreateMissionFeatureFormPageFragment(); } Bundle args = new Bundle(); args.putInt(FormPageFragment.ARG_OBJECT,i); fragment.setArguments(args); return fragment; } @Override public int getCount() { return mCreateMissionFeature ? t.seg_form.pages.size() : t.sop_form.pages.size(); } @Override public CharSequence getPageTitle(int position) { Page p = mCreateMissionFeature ? t.seg_form.pages.get(position) : t.sop_form.pages.get(position); if(p!=null && p.title != null){ return mCreateMissionFeature ? t.seg_form.pages.get(position).title : t.sop_form.pages.get(position).title; } return "PAGE " + ( position + 1);//TODO i18n? } } @Override public boolean onOptionsItemSelected( com.actionbarsherlock.view.MenuItem item) { int id = item.getItemId(); if (id == android.R.id.home) { // This ID represents the Home or Up button. In the case of this // activity, the Up button is shown. Use NavUtils to allow users // to navigate up one level in the application structure. For // more details, see the Navigation pattern on Android Design: // // http://developer.android.com/design/patterns/navigation.html#up-vs-back // Intent i = new Intent(this,PendingMissionListActivity.class); if(mCreateMissionFeature){ i.putExtra(PendingMissionListActivity.ARG_CREATE_MISSIONFEATURE, true); } i.putExtra(PendingMissionListActivity.KEY_NAVIGATING_UP, true); NavUtils.navigateUpTo(this, i); return true; } return super.onOptionsItemSelected(item); } /** * @return a unique MapView ID on each call. */ @Override public final int getMapViewId() { if(this.mapViewManager==null){ //registration auto creates mapViewManager this.mapViewManager =new MapViewManager(); } int i = this.mapViewManager.getMapViewId(); Log.v("MAPVIEW","created mapview with id:"+i); return i; } /** * This method is called once by each MapView during its setup process. * * @param mapView * the calling MapView. */ @Override public final void registerMapView(MapView mapView) { if(this.mapViewManager==null){ //registration auto creates mapViewManager this.mapViewManager =new MapViewManager(); } this.mapViewManager.registerMapView(mapView); } @Override public Context getActivityContext(){ return this; } @Override protected void onResume() { super.onResume(); if(this.mapViewManager !=null){ this.mapViewManager.resumeMapViews(); } /* if(listView != null){ // TODO: add pauseOnScroll and pauseOnFling to the optionsMenu listView.setOnScrollListener(new PauseOnScrollListener(imageLoader, false, true)); } */ } @Override protected void onDestroy() { super.onDestroy(); if(this.mapViewManager!=null){ this.mapViewManager.destroyMapViews(); } if(this.spatialiteDatabase!=null){ try { this.spatialiteDatabase.close(); Log.v("FORM_EDIT", "Spatialite Database Closed"); } catch (jsqlite.Exception e) { Log.e("FORM_EDIT", Log.getStackTraceString(e)); } } } /** * * @author Lorenzo Pini @Override protected void onActivityResult(int requestCode, int resultCode, Intent resultIntent) { super.onActivityResult(requestCode, resultCode, resultIntent); } */ // Context menu for images @Override public boolean onContextItemSelected(MenuItem item) { // super.onContextItemSelected(item); if(item != null){ switch (item.getItemId()) { case CONTEXT_IMAGE_ACTION_DELETE: Log.v("FEA", "Need to delete the image"); if( item.getMenuInfo() != null && item.getMenuInfo() instanceof AdapterContextMenuInfo ){ final AdapterContextMenuInfo aminfo = (AdapterContextMenuInfo) item.getMenuInfo(); Log.v("FEA", "Target view type: "+aminfo.targetView.getClass().getName()); String imagepath = (String) aminfo.targetView.getTag(R.id.tag_image_path); if(imagepath != null){ Log.v("FEA", "ImagePath: "+imagepath); final String imagePath = imagepath; new AlertDialog.Builder(this) .setTitle(R.string.button_confirm_image_delete_title) .setMessage(R.string.button_confirm_image_delete) .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { FormUtils.deleteFile(imagePath); ((UILImageAdapter)((GridView)aminfo.targetView.getParent()).getAdapter()).notifyDataSetChanged(); } }) .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // do nothing } }) .show(); return true; } } break; default: break; } return false; } return false; } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { Log.v("FEA", "CreatingMenu for "+v.getClass().getName()); super.onCreateContextMenu(menu, v, menuInfo); // a longPress on an Image is detected // Currently supported Actions are: // - Delete Image if (v instanceof GridView) { menu.setHeaderTitle(getString(R.string.gallery_context_menu_title)); // Delete Option menu.add(Menu.NONE, CONTEXT_IMAGE_ACTION_DELETE, Menu.NONE, getString(R.string.gallery_context_menu_delete)); } } /** * Prompt the user before deleting the selected image * @param imagePath public void confirmImageDelete(final String imagePath){ new AlertDialog.Builder(this) .setTitle(R.string.button_confirm_image_delete_title) .setMessage(R.string.button_confirm_image_delete) .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { FormUtils.deleteFile(imagePath); } }) .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // do nothing } }) .show(); } */ }