package org.osmdroid; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.graphics.Color; 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.InputType; import android.text.TextWatcher; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import org.osmdroid.config.Configuration; import org.osmdroid.config.DefaultConfigurationProvider; import org.osmdroid.intro.StorageAdapter; import org.osmdroid.model.PositiveLongTextValidator; import org.osmdroid.model.PositiveShortTextValidator; import org.osmdroid.tileprovider.modules.SqlTileWriter; import org.osmdroid.tileprovider.util.StorageUtils; import java.io.File; import java.util.ArrayList; import java.util.List; /** * OK so why is here? * Stupid reason #1: Android Studio's wizard generates a bunch of stupid complex code * Stupid reason #2: Android's Preference Activity is API10+ and we (osmdroid) are API8+ * Stupid reason #3: Simple is better, usually * @since 5.6 * Created by alex on 10/21/16. */ public class PreferenceActivity extends AppCompatActivity implements View.OnClickListener { CheckBox checkBoxDebugTileProvider, checkBoxDebugMode, checkBoxHardwareAcceleration, checkBoxMapViewDebug, checkBoxDebugDownloading; Button buttonSetCache, buttonManualCacheEntry, buttonPurgeCache, buttonReset, buttonSetBase, buttonManualBaseEntry; TextView textViewCacheDirectory; TextView textViewBaseDirectory; EditText httpUserAgent, tileDownloadThreads, tileDownloadMaxQueueSize, cacheMapTileCount, cacheMaxSize, cacheTrimSize, tileFileSystemThreads, tileFileSystemMaxQueueSize, gpsWaitTime, additionalExpirationTime,overrideExpirationTime; boolean abortSave=false; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_prefs); Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar); setSupportActionBar(myToolbar); checkBoxDebugTileProvider = (CheckBox) findViewById(R.id.checkBoxDebugTileProvider); checkBoxDebugMode = (CheckBox) findViewById(R.id.checkBoxDebugMode); checkBoxHardwareAcceleration = (CheckBox) findViewById(R.id.checkBoxHardwareAcceleration); checkBoxDebugDownloading = (CheckBox) findViewById(R.id.checkBoxDebugDownloading); checkBoxMapViewDebug = (CheckBox) findViewById(R.id.checkBoxMapViewDebug); checkBoxDebugTileProvider.setOnClickListener(this); checkBoxDebugMode.setOnClickListener(this); checkBoxHardwareAcceleration.setOnClickListener(this); checkBoxMapViewDebug.setOnClickListener(this); textViewCacheDirectory = (TextView) findViewById(R.id.textViewCacheDirectory); textViewBaseDirectory = (TextView) findViewById(R.id.textViewBaseDirectory); buttonPurgeCache = (Button) findViewById(R.id.buttonPurgeCache); httpUserAgent = (EditText) findViewById(R.id.httpUserAgent); tileDownloadThreads = (EditText) findViewById(R.id.tileDownloadThreads); tileDownloadThreads.addTextChangedListener(new PositiveShortTextValidator(tileDownloadThreads)); tileDownloadMaxQueueSize = (EditText) findViewById(R.id.tileDownloadMaxQueueSize); tileDownloadMaxQueueSize.addTextChangedListener(new PositiveShortTextValidator(tileDownloadMaxQueueSize)); cacheMapTileCount = (EditText) findViewById(R.id.cacheMapTileCount); cacheMapTileCount.addTextChangedListener(new PositiveShortTextValidator(cacheMapTileCount)); tileFileSystemThreads = (EditText) findViewById(R.id.tileFileSystemThreads); tileFileSystemThreads.addTextChangedListener(new PositiveShortTextValidator(tileFileSystemThreads)); tileFileSystemMaxQueueSize = (EditText) findViewById(R.id.tileFileSystemMaxQueueSize); tileFileSystemMaxQueueSize.addTextChangedListener(new PositiveShortTextValidator(tileFileSystemMaxQueueSize)); gpsWaitTime = (EditText) findViewById(R.id.gpsWaitTime); gpsWaitTime.addTextChangedListener(new PositiveLongTextValidator(gpsWaitTime,1)); additionalExpirationTime = (EditText) findViewById(R.id.additionalExpirationTime); additionalExpirationTime .addTextChangedListener(new PositiveLongTextValidator(additionalExpirationTime,0)); cacheMaxSize = (EditText) findViewById(R.id.cacheMaxSize); cacheTrimSize = (EditText) findViewById(R.id.cacheTrimSize); cacheMaxSize.addTextChangedListener(new PositiveLongTextValidator(additionalExpirationTime,0)); cacheTrimSize.addTextChangedListener(new PositiveLongTextValidator(additionalExpirationTime,0)); overrideExpirationTime = (EditText) findViewById(R.id.overrideExpirationTime); buttonSetBase = (Button) findViewById(R.id.buttonSetBase); buttonSetBase.setOnClickListener(this); buttonSetCache = (Button) findViewById(R.id.buttonSetCache); buttonManualCacheEntry = (Button) findViewById(R.id.buttonManualCacheEntry); buttonSetCache.setOnClickListener(this); buttonManualBaseEntry = (Button) findViewById(R.id.buttonManualBaseEntry); buttonManualBaseEntry.setOnClickListener(this); buttonManualCacheEntry.setOnClickListener(this); buttonPurgeCache.setOnClickListener(this); buttonReset = (Button) findViewById(R.id.buttonReset); buttonReset.setOnClickListener(this); findViewById(R.id.baseDirTitle).setOnClickListener(this); } @Override public void onResume() { super.onResume(); tileFileSystemMaxQueueSize.setText(Configuration.getInstance().getTileFileSystemMaxQueueSize() + ""); tileFileSystemThreads.setText(Configuration.getInstance().getTileFileSystemThreads() + ""); tileDownloadMaxQueueSize.setText(Configuration.getInstance().getTileDownloadMaxQueueSize() + ""); tileDownloadThreads.setText(Configuration.getInstance().getTileDownloadThreads() + ""); gpsWaitTime.setText(Configuration.getInstance().getGpsWaitTime() + ""); additionalExpirationTime.setText(Configuration.getInstance().getExpirationExtendedDuration()+""); cacheMapTileCount.setText(Configuration.getInstance().getCacheMapTileCount() + ""); if (Configuration.getInstance().getExpirationOverrideDuration()!=null) overrideExpirationTime.setText(Configuration.getInstance().getExpirationOverrideDuration()+""); httpUserAgent.setText(Configuration.getInstance().getUserAgentValue()); checkBoxMapViewDebug.setChecked(Configuration.getInstance().isDebugMapView()); checkBoxDebugMode.setChecked(Configuration.getInstance().isDebugMode()); checkBoxDebugTileProvider.setChecked(Configuration.getInstance().isDebugTileProviders()); checkBoxHardwareAcceleration.setChecked(Configuration.getInstance().isMapViewHardwareAccelerated()); checkBoxDebugDownloading.setChecked(Configuration.getInstance().isDebugMapTileDownloader()); textViewCacheDirectory.setText(Configuration.getInstance().getOsmdroidTileCache().getAbsolutePath()); textViewBaseDirectory.setText(Configuration.getInstance().getOsmdroidBasePath().getAbsolutePath()); cacheMaxSize.setText(Configuration.getInstance().getTileFileSystemCacheMaxBytes()+""); cacheTrimSize.setText(Configuration.getInstance().getTileFileSystemCacheTrimBytes()+""); } @Override public void onPause() { super.onPause(); if (abortSave) return; //save the configuration try { if (tileDownloadThreads.getError() == null) Configuration.getInstance().setTileDownloadThreads(Short.parseShort(tileDownloadThreads.getText().toString())); } catch (Exception ex) { ex.printStackTrace(); } try { if (tileDownloadMaxQueueSize.getError() == null) Configuration.getInstance().setTileDownloadMaxQueueSize(Short.parseShort(tileDownloadMaxQueueSize.getText().toString())); } catch (Exception ex) { ex.printStackTrace(); } try { if (cacheMapTileCount.getError() == null) Configuration.getInstance().setCacheMapTileCount(Short.parseShort(cacheMapTileCount.getText().toString())); } catch (Exception ex) { ex.printStackTrace(); } try { if (tileFileSystemThreads.getError() == null) Configuration.getInstance().setTileFileSystemThreads(Short.parseShort(tileFileSystemThreads.getText().toString())); } catch (Exception ex) { ex.printStackTrace(); } try { if (tileFileSystemMaxQueueSize.getError() == null) Configuration.getInstance().setTileFileSystemMaxQueueSize(Short.parseShort(tileFileSystemMaxQueueSize.getText().toString())); } catch (Exception ex) { ex.printStackTrace(); } try { if (gpsWaitTime.getError() == null) Configuration.getInstance().setGpsWaitTime(Long.parseLong(gpsWaitTime.getText().toString())); } catch (Exception ex) { ex.printStackTrace(); } try { if (additionalExpirationTime.getError() == null) Configuration.getInstance().setExpirationExtendedDuration(Long.parseLong(additionalExpirationTime.getText().toString())); } catch (Exception ex) { ex.printStackTrace(); } try { Long val=Long.parseLong(overrideExpirationTime.getText().toString()); if (val > 0) Configuration.getInstance().setExpirationOverrideDuration(val); else Configuration.getInstance().setExpirationOverrideDuration(null); } catch (Exception ex) { ex.printStackTrace(); Configuration.getInstance().setExpirationOverrideDuration(null); } try { Long val=Long.parseLong(cacheMaxSize.getText().toString()); if (val > 0) Configuration.getInstance().setTileFileSystemCacheMaxBytes(val); } catch (Exception ex) { ex.printStackTrace(); } try { Long val=Long.parseLong(cacheTrimSize.getText().toString()); if (val > 0) Configuration.getInstance().setTileFileSystemCacheTrimBytes(val); } catch (Exception ex) { ex.printStackTrace(); } Configuration.getInstance().setUserAgentValue(httpUserAgent.getText().toString()); Configuration.getInstance().setDebugMapView(checkBoxMapViewDebug.isChecked()); Configuration.getInstance().setDebugMode(checkBoxDebugMode.isChecked()); Configuration.getInstance().setDebugTileProviders(checkBoxDebugTileProvider.isChecked()); Configuration.getInstance().setMapViewHardwareAccelerated(checkBoxHardwareAcceleration.isChecked()); Configuration.getInstance().setDebugMapTileDownloader(checkBoxDebugDownloading.isChecked()); Configuration.getInstance().setOsmdroidTileCache(new File(textViewCacheDirectory.getText().toString())); Configuration.getInstance().setOsmdroidBasePath(new File(textViewBaseDirectory.getText().toString())); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); Configuration.getInstance().save(this, prefs); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.buttonManualCacheEntry: { showManualEntry(textViewCacheDirectory); } break; case R.id.buttonSetCache: { showPickCacheFromList(textViewCacheDirectory, "tiles" + File.separator); } break; case R.id.buttonPurgeCache: { purgeCache(); } break; case R.id.buttonReset: { resetSettings(this); abortSave=true; finish(); } break; case R.id.buttonManualBaseEntry: { showManualEntry(textViewBaseDirectory); } break; case R.id.buttonSetBase: { showPickCacheFromList(textViewBaseDirectory, ""); } break; } } public static void resetSettings(Context ctx) { //delete all preference keys, if you're using this for your own application //you may want to consider some additional logic here (only clear osmdroid settings or //use something other than the default shared preferences map SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(ctx).edit(); edit.clear(); edit.commit(); //this will repopulate the default settings Configuration.setConfigurationProvider(new DefaultConfigurationProvider()); //this will save the default along with the user agent (important for downloading tiles) Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx)); } private void purgeCache() { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which){ case DialogInterface.BUTTON_POSITIVE: //Yes button clicked new Thread(new Runnable() { @Override public void run() { SqlTileWriter sqlTileWriter = new SqlTileWriter(); boolean b = sqlTileWriter.purgeCache(); sqlTileWriter.onDetach(); sqlTileWriter = null; if (b) Toast.makeText(PreferenceActivity.this, "SQL Cache purged", Toast.LENGTH_SHORT).show(); else Toast.makeText(PreferenceActivity.this, "SQL Cache purge failed, see logcat for details", Toast.LENGTH_LONG).show(); } }).start(); break; case DialogInterface.BUTTON_NEGATIVE: //No button clicked break; } } }; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(R.string.userconfirm).setPositiveButton(R.string.yes, dialogClickListener) .setNegativeButton(R.string.no, dialogClickListener).show(); } private void showPickCacheFromList(final TextView tv, final String postfix) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.enterCacheLocation); final List<StorageUtils.StorageInfo> storageList = StorageUtils.getStorageList(); List<StorageUtils.StorageInfo> storageListFiltered = new ArrayList<>(); for (int i = 0; i < storageList.size(); i++) { if (!storageList.get(i).readonly) { storageListFiltered.add(storageList.get(i)); } } try { File f = new File("/data/data/" + getPackageName() + "/osmdroid/"); f.mkdirs(); StorageUtils.StorageInfo privateStorage=new StorageUtils.StorageInfo(f.getAbsolutePath(), true, false, 0); privateStorage.setDisplayName("Application Private Storage"); storageListFiltered.add(privateStorage); } catch (Exception ex) { } if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { File[] externalFilesDirs = getExternalFilesDirs(null); if (externalFilesDirs != null) { //i hate android! for (int i = 0; i < externalFilesDirs.length; i++) { File mount = externalFilesDirs[i]; if (mount.exists() && mount.isDirectory()) { boolean writable = StorageUtils.isWritable(mount); if (writable) { boolean alreadyAdded = false; for (int k = 0; k < storageList.size(); k++) { StorageUtils.StorageInfo storageInfo = storageList.get(k); if (storageInfo.path.equals(mount.getAbsolutePath())) { alreadyAdded = true; break; } } if (!alreadyAdded) { StorageUtils.StorageInfo x = new StorageUtils.StorageInfo(mount.getAbsolutePath(), false, false, 0); x.freeSpace = mount.getFreeSpace(); storageListFiltered.add(x); } } } } } } final StorageAdapter arrayAdapter = new StorageAdapter(this, storageListFiltered); builder.setAdapter(arrayAdapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { StorageUtils.StorageInfo item = (StorageUtils.StorageInfo) arrayAdapter.getItem(which); try { new File(item.path + File.separator + "osmdroid" + File.separator + postfix).mkdirs(); tv.setText(item.path + File.separator + "osmdroid" + File.separator + postfix); } catch (Exception ex) { ex.printStackTrace(); Toast.makeText(PreferenceActivity.this, "Invalid entry: " + ex.getMessage(), Toast.LENGTH_LONG); } } }); builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); builder.show(); } private void showManualEntry(final TextView textView) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.enterCacheLocation); // Set up the input final EditText input = new EditText(this); // Specify the type of input expected; this, for example, sets the input as a password, and will mask the text input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); input.setLines(1); input.setText(textView.getText().toString()); input.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { File file = new File(input.getText().toString()); if (!file.exists()) { input.setError("Does not exist"); } else if (file.exists() && !file.isDirectory()) { input.setError("Not a directory"); } else if (!StorageUtils.isWritable(file)) { input.setError("Not writable"); } else { input.setError(null); } } }); builder.setView(input); // Set up the buttons builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (input.getError() == null) { textView.setText(input.getText().toString()); } } }); builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); builder.show(); } }