/* * Geopaparazzi - Digital field mapping on Android based devices * Copyright (C) 2010 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.mapsforge.sourcesview; import java.io.File; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map.Entry; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.TextWatcher; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.EditText; import android.widget.ExpandableListView; import android.widget.RelativeLayout; import org.json.JSONException; import eu.geopaparazzi.library.core.maps.BaseMap; import eu.geopaparazzi.library.database.GPLog; import eu.geopaparazzi.library.profiles.ProfilesHandler; import eu.geopaparazzi.library.style.ColorUtilities; import eu.geopaparazzi.library.util.AppsUtilities; import eu.geopaparazzi.library.util.FileUtilities; import eu.geopaparazzi.library.util.GPDialogs; import eu.geopaparazzi.library.util.IActivitySupporter; import eu.geopaparazzi.library.util.LibraryConstants; import eu.geopaparazzi.library.util.StringAsyncTask; import eu.geopaparazzi.library.util.Utilities; import eu.geopaparazzi.mapsforge.R; import eu.geopaparazzi.mapsforge.BaseMapSourcesManager; import eu.geopaparazzi.library.util.types.ESpatialDataSources; /** * Activity for tile source visualisation. * * @author Andrea Antonello (www.hydrologis.com) */ public class SourcesTreeListActivity extends AppCompatActivity implements IActivitySupporter { public static final int PICKFILE_REQUEST_CODE = 666; public static final int PICKFOLDER_REQUEST_CODE = 667; public static final String SHOW_MAPS = "showMaps"; public static final String SHOW_MAPURLS = "showMapurls"; public static final String SHOW_MBTILES = "showMbtiles"; public static final String SHOW_RASTER_LITE_2 = "showRasterLite2"; private ExpandableListView mExpListView; private EditText mFilterText; private String mTextToFilter = ""; private SharedPreferences mPreferences; private boolean[] mCheckedValues; private List<String> mTypeNames; private final LinkedHashMap<String, List<BaseMap>> newMap = new LinkedHashMap<>(); private StringAsyncTask loadTask; private StringAsyncTask addNewSourcesTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sources_list); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); mPreferences = PreferenceManager.getDefaultSharedPreferences(this); mFilterText = (EditText) findViewById(R.id.search_box); mFilterText.addTextChangedListener(filterTextWatcher); boolean showMaps = mPreferences.getBoolean(SHOW_MAPS, true); boolean showMapurls = mPreferences.getBoolean(SHOW_MAPURLS, true); boolean showMbtiles = mPreferences.getBoolean(SHOW_MBTILES, true); boolean showRasterLite2 = mPreferences.getBoolean(SHOW_RASTER_LITE_2, true); String mapTypeName = ESpatialDataSources.MAP.getTypeName(); String mapurlTypeName = ESpatialDataSources.MAPURL.getTypeName(); String mbtilesTypeName = ESpatialDataSources.MBTILES.getTypeName(); String rasterLiteTypeName = ESpatialDataSources.RASTERLITE2.getTypeName(); mTypeNames = new ArrayList<>(); mTypeNames.add(mapTypeName); mTypeNames.add(mapurlTypeName); mTypeNames.add(mbtilesTypeName); mTypeNames.add(rasterLiteTypeName); mCheckedValues = new boolean[mTypeNames.size()]; mCheckedValues[0] = showMaps; mCheckedValues[1] = showMapurls; mCheckedValues[2] = showMbtiles; mCheckedValues[3] = showRasterLite2; // get the listview mExpListView = (ExpandableListView) findViewById(R.id.expandableSourceListView); if (ProfilesHandler.INSTANCE.getActiveProfile() != null) { RelativeLayout mainView = (RelativeLayout) findViewById(R.id.sources_list_mainview); int color = ColorUtilities.toColor(ProfilesHandler.INSTANCE.getActiveProfile().color); mainView.setBackgroundColor(color); FloatingActionButton addSourceButton = (FloatingActionButton) findViewById(R.id.addSourceButton); addSourceButton.hide(); } loadTask = new StringAsyncTask(this) { List<BaseMap> baseMaps; protected String doBackgroundWork() { baseMaps = BaseMapSourcesManager.INSTANCE.getBaseMaps(); return ""; } protected void doUiPostWork(String response) { dispose(); try { refreshData(baseMaps); } catch (Exception e) { GPLog.error(this, "Problem getting sources.", e); } } }; loadTask.setProgressDialog("", getString(R.string.loading_sources), false, null); loadTask.execute(); } @Override protected void onStart() { super.onStart(); } @Override protected void onDestroy() { if (loadTask != null) loadTask.dispose(); if (addNewSourcesTask != null) addNewSourcesTask.dispose(); super.onDestroy(); } @Override protected void onStop() { mFilterText.removeTextChangedListener(filterTextWatcher); super.onStop(); } public void add(View view) { try { String title = getString(R.string.select_basemap_source); String[] supportedExtensions = ESpatialDataSources.getSupportedTileSourcesExtensions(); AppsUtilities.pickFile(this, PICKFILE_REQUEST_CODE, title, supportedExtensions, null); } catch (Exception e) { GPLog.error(this, null, e); GPDialogs.errorDialog(this, e, null); } } public void addFolder(View view) { try { String title = getString(R.string.select_basemap_source_folder); String[] supportedExtensions = ESpatialDataSources.getSupportedTileSourcesExtensions(); AppsUtilities.pickFolder(this, PICKFOLDER_REQUEST_CODE, title, null, supportedExtensions); } catch (Exception e) { GPLog.error(this, null, e); GPDialogs.errorDialog(this, e, null); } } public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case (PICKFILE_REQUEST_CODE): { if (resultCode == Activity.RESULT_OK) { try { String filePath = data.getStringExtra(LibraryConstants.PREFS_KEY_PATH); File file = new File(filePath); if (file.exists()) { Utilities.setLastFilePath(this, filePath); final File finalFile = file; // add basemap to list and in mPreferences addNewSourcesTask = new StringAsyncTask(this) { public List<BaseMap> baseMaps; protected String doBackgroundWork() { try { // add basemap to list and in mPreferences if (BaseMapSourcesManager.INSTANCE.addBaseMapsFromFile(finalFile).size() != 0) { baseMaps = BaseMapSourcesManager.INSTANCE.getBaseMaps(); } else { return getString(R.string.selected_file_no_basemap) + finalFile; } } catch (Exception e) { GPLog.error(this, "Problem getting sources.", e); return "ERROR: " + e.getLocalizedMessage(); } return ""; } protected void doUiPostWork(String response) { dispose(); if (response.length() > 0) { GPDialogs.warningDialog(SourcesTreeListActivity.this, response, null); } else { try { refreshData(baseMaps); } catch (Exception e) { GPLog.error(this, null, e); } } } }; addNewSourcesTask.setProgressDialog("", getString(R.string.adding_new_source), false, null); addNewSourcesTask.execute(); } } catch (Exception e) { GPDialogs.errorDialog(this, e, null); } } break; } case (PICKFOLDER_REQUEST_CODE): { if (resultCode == Activity.RESULT_OK) { try { String folderPath = data.getStringExtra(LibraryConstants.PREFS_KEY_PATH); final File folder = new File(folderPath); if (folder.exists()) { Utilities.setLastFilePath(this, folderPath); final List<File> foundFiles = new ArrayList<>(); // get all supported files String[] supportedExtensions = ESpatialDataSources.getSupportedTileSourcesExtensions(); FileUtilities.searchDirectoryRecursive(folder, supportedExtensions, foundFiles); // add basemap to list and in mPreferences addNewSourcesTask = new StringAsyncTask(this) { public List<BaseMap> baseMaps = new ArrayList<>(); protected String doBackgroundWork() { try { for (int i = 0; i < foundFiles.size(); i++) { File file = foundFiles.get(i); try { // add basemap to list and in mPreferences BaseMapSourcesManager.INSTANCE.addBaseMapsFromFile(file); } catch (Exception e) { // ignore } finally { onProgressUpdate(i + 1); } } baseMaps = BaseMapSourcesManager.INSTANCE.getBaseMaps(); if (baseMaps.size() == 0) { return getString(R.string.selected_file_no_basemap) + folder; } } catch (Exception e) { GPLog.error(this, "Problem getting sources.", e); return "ERROR: " + e.getLocalizedMessage(); } return ""; } protected void doUiPostWork(String response) { dispose(); if (response.length() > 0) { GPDialogs.warningDialog(SourcesTreeListActivity.this, response, null); } else { try { refreshData(baseMaps); } catch (Exception e) { GPLog.error(this, null, e); } } } }; addNewSourcesTask.setProgressDialog("", getString(R.string.adding_new_source), false, foundFiles.size()); addNewSourcesTask.execute(); } } catch (Exception e) { GPDialogs.errorDialog(this, e, null); } } break; } } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_sources, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.select_type_item) { MapTypesChoiceDialog dialog = new MapTypesChoiceDialog(); dialog.open(getString(R.string.select_type), SourcesTreeListActivity.this, mTypeNames, mCheckedValues); } return super.onOptionsItemSelected(item); } public void refreshData(List<BaseMap> baseMaps) throws Exception { SharedPreferences.Editor editor = mPreferences.edit(); editor.putBoolean(SHOW_MAPS, mCheckedValues[0]); editor.putBoolean(SHOW_MAPURLS, mCheckedValues[1]); editor.putBoolean(SHOW_MBTILES, mCheckedValues[2]); editor.putBoolean(SHOW_RASTER_LITE_2, mCheckedValues[3]); editor.apply(); boolean log = GPLog.LOG; if (log) { GPLog.addLogEntry(this, "Available baseMaps:"); for (BaseMap basemap : baseMaps) { GPLog.addLogEntry(this, basemap.toString()); } } newMap.clear(); for (BaseMap baseMap : baseMaps) { String key = baseMap.parentFolder; List<BaseMap> newValues = newMap.get(key); if (newValues == null) { newValues = new ArrayList<>(); newMap.put(key, newValues); } boolean doAdd = false; String mapType = baseMap.mapType; if (mCheckedValues[0] && mapType.equals(ESpatialDataSources.MAP.getTypeName())) { doAdd = true; } else if (mCheckedValues[1] && mapType.equals(ESpatialDataSources.MAPURL.getTypeName())) { doAdd = true; } else if (mCheckedValues[2] && mapType.equals(ESpatialDataSources.MBTILES.getTypeName())) { doAdd = true; } else if (mCheckedValues[3] && mapType.equals(ESpatialDataSources.RASTERLITE2.getTypeName())) { doAdd = true; } if (log) { GPLog.addLogEntry(this, "doAdd: " + doAdd + " baseMap: " + baseMap); } if (mTextToFilter.length() > 0) { // filter text String filterString = mTextToFilter.toLowerCase(); String valueString = baseMap.databasePath.toLowerCase(); if (!valueString.contains(filterString)) { valueString = baseMap.title.toLowerCase(); if (!valueString.contains(filterString)) { doAdd = false; } } } if (doAdd) { newValues.add(baseMap); if (log) { GPLog.addLogEntry(this, "Added: " + baseMap.toString()); } } if (newValues.size() == 0) { newMap.remove(key); } } SourcesExpandableListAdapter listAdapter = new SourcesExpandableListAdapter(this, newMap); mExpListView.setAdapter(listAdapter); mExpListView.setClickable(true); mExpListView.setFocusable(true); mExpListView.setFocusableInTouchMode(true); mExpListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { int index = 0; BaseMap selectedBaseMap = null; for (Entry<String, List<BaseMap>> entry : newMap.entrySet()) { if (groupPosition == index) { List<BaseMap> value = entry.getValue(); selectedBaseMap = value.get(childPosition); break; } index++; } try { BaseMapSourcesManager.INSTANCE.setSelectedBaseMap(selectedBaseMap); } catch (jsqlite.Exception e) { GPLog.error(SourcesTreeListActivity.this, "ERROR", e); } finish(); return false; } }); if (ProfilesHandler.INSTANCE.getActiveProfile() == null) mExpListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { // When clicked on child, function longClick is executed if (ExpandableListView.getPackedPositionType(id) == ExpandableListView.PACKED_POSITION_TYPE_CHILD) { int groupPosition = ExpandableListView.getPackedPositionGroup(id); int childPosition = ExpandableListView.getPackedPositionChild(id); int index = 0; for (String group : newMap.keySet()) { if (index == groupPosition) { List<BaseMap> baseMapList = newMap.get(group); final BaseMap baseMap = baseMapList.get(childPosition); GPDialogs.yesNoMessageDialog(SourcesTreeListActivity.this, String.format(getString(R.string.remove_from_list), baseMap.title), new Runnable() { @Override public void run() { try { BaseMapSourcesManager.INSTANCE.removeBaseMap(baseMap); } catch (JSONException e) { GPLog.error(this, null, e); } runOnUiThread(new Runnable() { @Override public void run() { try { refreshData(BaseMapSourcesManager.INSTANCE.getBaseMaps()); } catch (Exception e) { GPLog.error(this, null, e); } } }); } }, null); return true; } index++; } return true; } return false; } }); int groupCount = listAdapter.getGroupCount(); for (int i = 0; i < groupCount; i++) { mExpListView.expandGroup(i); } } 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) { mTextToFilter = s.toString(); try { refreshData(BaseMapSourcesManager.INSTANCE.getBaseMaps()); } catch (Exception e) { GPLog.error(SourcesTreeListActivity.this, "ERROR", e); } } }; @Override public Context getContext() { return this; } }