/*
* 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.library.core;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ApplicationInfo;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import eu.geopaparazzi.library.R;
import eu.geopaparazzi.library.database.GPLog;
import eu.geopaparazzi.library.profiles.ProfilesHandler;
import eu.geopaparazzi.library.util.GPDialogs;
import eu.geopaparazzi.library.util.LibraryConstants;
import eu.geopaparazzi.library.util.Utilities;
import static eu.geopaparazzi.library.util.LibraryConstants.PREFS_KEY_CUSTOM_EXTERNALSTORAGE;
import static eu.geopaparazzi.library.util.LibraryConstants.PREFS_KEY_DATABASE_TO_LOAD;
/**
* Singleton that takes care of resources management.
* <p/>
* <p>It creates a folder structure with possible database and log file names.</p>
*
* @author Andrea Antonello (www.hydrologis.com)
*/
public class ResourcesManager implements Serializable {
private static final long serialVersionUID = 1L;
private static final String PATH_TEMP = "temp"; //$NON-NLS-1$
/**
* The nomedia file defining folders that should not be searched for media.
*/
public static final String NO_MEDIA = ".nomedia"; //$NON-NLS-1$
/**
* The support folder for geopap. If not there, it is created.
* <p/>
* <p>It has the name of the application and resides in the sdcard.</p>
* <p>It contains for example the json tags file and temporary files if necessary.
*/
private File applicationSupportFolder;
/**
* The database file for geopap.
*/
private File databaseFile;
/**
* The temporary folder.
*/
private File tempDir;
private static ResourcesManager resourcesManager;
/**
* The name of the application.
*/
private String applicationLabel;
private static boolean useInternalMemory = true;
private File sdcardDir;
private final String packageName;
/**
* @param useInternalMemory if <code>true</code>, internal memory is used.
*/
public static void setUseInternalMemory(boolean useInternalMemory) {
ResourcesManager.useInternalMemory = useInternalMemory;
}
/**
* The getter for the {@link ResourcesManager} singleton.
* <p>
* <p>This is a singleton but might require to be recreated
* in every moment of the application. This is due to the fact
* that when the application looses focus (for example because of
* an incoming call, and therefore at a random moment, if the memory
* is too low, the parent activity could have been killed by
* the system in background. In which case we need to recreate it.)
*
* @param context the context to refer to.
* @return the {@link ResourcesManager} instance.
* @throws Exception if something goes wrong.
*/
public synchronized static ResourcesManager getInstance(Context context) throws Exception {
if (resourcesManager == null) {
resourcesManager = new ResourcesManager(context);
}
return resourcesManager;
}
/**
* Reset the {@link ResourcesManager}.
*/
public static void resetManager() {
resourcesManager = null;
}
/**
* Getter for the app name.
*
* @return the name of the app.
*/
public String getApplicationName() {
return applicationLabel;
}
private ResourcesManager(Context context) throws Exception {
Context appContext = context.getApplicationContext();
ApplicationInfo appInfo = appContext.getApplicationInfo();
packageName = appInfo.packageName;
int lastDot = packageName.lastIndexOf('.');
applicationLabel = packageName.replace('.', '_');
if (lastDot != -1) {
applicationLabel = packageName.substring(lastDot + 1, packageName.length());
}
applicationLabel = applicationLabel.toLowerCase();
/*
* take care to create all the folders needed
*
* The default structure is:
*
* sdcard
* |
* |-- applicationname.gpap -> main database
* |-- applicationSupportfolder
* | |
* | |--- temp/ -> temporary files
* | `--- tags.json
* |
* `-- mapsdir
*/
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(appContext);
String cantCreateSdcardmsg = appContext.getResources().getString(R.string.cantcreate_sdcard);
String customFolderPath = preferences.getString(PREFS_KEY_CUSTOM_EXTERNALSTORAGE, "asdasdpoipoi");
customFolderPath = customFolderPath.trim();
File customFolderFile = new File(customFolderPath);
if (customFolderFile.exists() && customFolderFile.isDirectory() && customFolderFile.canWrite()) {
// we can write to the user set folder, let's use it
sdcardDir = customFolderFile;
applicationSupportFolder = new File(sdcardDir, applicationLabel);
} else {
// checks
// the folder doesn't exist for some reason, fallback on default
String state = Environment.getExternalStorageState();
if (GPLog.LOG_HEAVY) {
Log.i("RESOURCESMANAGER", state);
}
boolean mExternalStorageAvailable;
boolean mExternalStorageWriteable;
if (Environment.MEDIA_MOUNTED.equals(state)) {
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
if (mExternalStorageAvailable && mExternalStorageWriteable) {
if (customFolderPath.equals("internal")) {
/*
* the user folder doesn't exist, but is "internal":
* - use internal app memory
* - set sdcard anyways to the external folder for maps use
*/
useInternalMemory = true;
applicationSupportFolder = appContext.getDir(applicationLabel, Context.MODE_PRIVATE);
sdcardDir = Environment.getExternalStorageDirectory();
} else {
sdcardDir = Environment.getExternalStorageDirectory();
applicationSupportFolder = new File(sdcardDir, applicationLabel);
}
} else if (useInternalMemory) {
/*
* no external storage available:
* - use internal memory
* - set sdcard for maps inside the space
*/
applicationSupportFolder = appContext.getDir(applicationLabel, Context.MODE_PRIVATE);
sdcardDir = applicationSupportFolder;
} else {
String msgFormat = Utilities.format(cantCreateSdcardmsg, "sdcard/" + applicationLabel);
throw new IOException(msgFormat);
}
}
// if (GPLog.LOG_HEAVY) {
Log.i("RESOURCESMANAGER", "Application support folder: " + applicationSupportFolder);
// }
String applicationDirPath = applicationSupportFolder.getAbsolutePath();
if (!applicationSupportFolder.exists()) {
if (!applicationSupportFolder.mkdirs()) {
String msgFormat = Utilities.format(cantCreateSdcardmsg, applicationDirPath);
throw new IOException(msgFormat);
}
}
if (GPLog.LOG_HEAVY) {
Log.i("RESOURCESMANAGER", "Application support folder exists: " + applicationSupportFolder.exists());
}
/*
* get the database file
*/
String databasePath = null;
if (ProfilesHandler.INSTANCE.getActiveProfile() != null) {
String projectPath = ProfilesHandler.INSTANCE.getActiveProfile().projectPath;
if (projectPath != null && new File(projectPath).exists()) {
databasePath = projectPath;
}
}
if (databasePath == null)
databasePath = preferences.getString(PREFS_KEY_DATABASE_TO_LOAD, "asdasdpoipoi");
databaseFile = new File(databasePath);
if (databaseFile.getParentFile() == null || !databaseFile.getParentFile().exists()) {
// fallback on the default
String databaseName = applicationLabel + LibraryConstants.GEOPAPARAZZI_DB_EXTENSION;
databaseFile = new File(sdcardDir, databaseName);
}
tempDir = new File(applicationSupportFolder, PATH_TEMP);
if (!tempDir.exists())
if (!tempDir.mkdir()) {
String msgFormat = Utilities.format(cantCreateSdcardmsg, tempDir.getAbsolutePath());
GPDialogs.infoDialog(appContext, msgFormat, null);
tempDir = sdcardDir;
}
Editor editor = preferences.edit();
editor.putString(LibraryConstants.PREFS_KEY_CUSTOM_EXTERNALSTORAGE, sdcardDir.getAbsolutePath());
editor.apply();
}
/**
* Get the name of the package..
*
* @return the name of the package.
*/
public String getPackageName() {
return packageName;
}
/**
* Get the file to the main application support folder.
*
* @return the {@link File} to the app folder.
*/
public File getApplicationSupporterDir() {
return applicationSupportFolder;
}
/**
* Get the sdcard dir or <code>null</code>.
*
* @return the sdcard folder file.
*/
public File getSdcardDir() {
return sdcardDir;
}
/**
* Get the file to a default database location for the app.
* <p>
* <p>This path is generated with default values and can be
* exploited. It doesn't assure that in the location there really is a db.
*
* @return the {@link File} to the database.
*/
public File getDatabaseFile() {
return databaseFile;
}
/**
* Get the temporary folder.
*
* @return the temp folder.
*/
public File getTempDir() {
return tempDir;
}
}