Lightning browser with I2P configuration
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

249 lines
9.1 KiB

package org.purplei2p.lightning.utils;
import android.app.Application;
import android.os.Bundle;
import android.os.Environment;
import android.os.Parcel;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.anthonycr.bonsai.Schedulers;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
/**
* A utility class containing helpful methods
* pertaining to file storage.
*/
public class FileUtils {
private static final String TAG = "FileUtils";
public static final String DEFAULT_DOWNLOAD_PATH =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
/**
* Writes a bundle to persistent storage in the files directory
* using the specified file name. This method is a blocking
* operation.
*
* @param app the application needed to obtain the file directory.
* @param bundle the bundle to store in persistent storage.
* @param name the name of the file to store the bundle in.
*/
public static void writeBundleToStorage(final @NonNull Application app, final Bundle bundle, final @NonNull String name) {
Schedulers.io().execute(new Runnable() {
@Override
public void run() {
File outputFile = new File(app.getFilesDir(), name);
FileOutputStream outputStream = null;
try {
//noinspection IOResourceOpenedButNotSafelyClosed
outputStream = new FileOutputStream(outputFile);
Parcel parcel = Parcel.obtain();
parcel.writeBundle(bundle);
outputStream.write(parcel.marshall());
outputStream.flush();
parcel.recycle();
} catch (IOException e) {
Log.e(TAG, "Unable to write bundle to storage");
} finally {
Utils.close(outputStream);
}
}
});
}
/**
* Use this method to delete the bundle with the specified name.
* This is a blocking call and should be used within a worker
* thread unless immediate deletion is necessary.
*
* @param app the application object needed to get the file.
* @param name the name of the file.
*/
public static void deleteBundleInStorage(final @NonNull Application app, final @NonNull String name) {
File outputFile = new File(app.getFilesDir(), name);
if (outputFile.exists()) {
outputFile.delete();
}
}
/**
* Reads a bundle from the file with the specified
* name in the peristent storage files directory.
* This method is a blocking operation.
*
* @param app the application needed to obtain the files directory.
* @param name the name of the file to read from.
* @return a valid Bundle loaded using the system class loader
* or null if the method was unable to read the Bundle from storage.
*/
@Nullable
public static Bundle readBundleFromStorage(@NonNull Application app, @NonNull String name) {
File inputFile = new File(app.getFilesDir(), name);
FileInputStream inputStream = null;
try {
//noinspection IOResourceOpenedButNotSafelyClosed
inputStream = new FileInputStream(inputFile);
Parcel parcel = Parcel.obtain();
byte[] data = new byte[(int) inputStream.getChannel().size()];
//noinspection ResultOfMethodCallIgnored
inputStream.read(data, 0, data.length);
parcel.unmarshall(data, 0, data.length);
parcel.setDataPosition(0);
Bundle out = parcel.readBundle(ClassLoader.getSystemClassLoader());
out.putAll(out);
parcel.recycle();
return out;
} catch (FileNotFoundException e) {
Log.e(TAG, "Unable to read bundle from storage");
} catch (IOException e) {
e.printStackTrace();
} finally {
//noinspection ResultOfMethodCallIgnored
inputFile.delete();
Utils.close(inputStream);
}
return null;
}
/**
* Writes a stacktrace to the downloads folder with
* the following filename: [EXCEPTION]_[TIME OF CRASH IN MILLIS].txt
*
* @param throwable the Throwable to log to external storage
*/
public static void writeCrashToStorage(@NonNull Throwable throwable) {
String fileName = throwable.getClass().getSimpleName() + '_' + System.currentTimeMillis() + ".txt";
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
FileOutputStream outputStream = null;
try {
//noinspection IOResourceOpenedButNotSafelyClosed
outputStream = new FileOutputStream(outputFile);
throwable.printStackTrace(new PrintStream(outputStream));
outputStream.flush();
} catch (IOException e) {
Log.e(TAG, "Unable to write bundle to storage");
} finally {
Utils.close(outputStream);
}
}
@NonNull
public static String readStringFromStream(@NonNull InputStream inputStream,
@NonNull String encoding) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, encoding));
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
return result.toString();
}
/**
* Converts megabytes to bytes.
*
* @param megaBytes the number of megabytes.
* @return the converted bytes.
*/
public static long megabytesToBytes(long megaBytes) {
return megaBytes * 1024 * 1024;
}
/**
* Determine whether there is write access in the given directory. Returns false if a
* file cannot be created in the directory or if the directory does not exist.
*
* @param directory the directory to check for write access
* @return returns true if the directory can be written to or is in a directory that can
* be written to. false if there is no write access.
*/
public static boolean isWriteAccessAvailable(@Nullable String directory) {
if (directory == null || directory.isEmpty()) {
return false;
}
final String sFileName = "test";
final String sFileExtension = ".txt";
String dir = addNecessarySlashes(directory);
dir = getFirstRealParentDirectory(dir);
File file = new File(dir + sFileName + sFileExtension);
for (int n = 0; n < 100; n++) {
if (!file.exists()) {
try {
if (file.createNewFile()) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
return true;
} catch (IOException ignored) {
return false;
}
} else {
file = new File(dir + sFileName + '-' + n + sFileExtension);
}
}
return file.canWrite();
}
/**
* Returns the first parent directory of a directory that exists. This is useful
* for subdirectories that do not exist but their parents do.
*
* @param directory the directory to find the first existent parent
* @return the first existent parent
*/
@Nullable
private static String getFirstRealParentDirectory(@Nullable String directory) {
while (true) {
if (directory == null || directory.isEmpty()) {
return "/";
}
directory = addNecessarySlashes(directory);
File file = new File(directory);
if (!file.isDirectory()) {
int indexSlash = directory.lastIndexOf('/');
if (indexSlash > 0) {
String parent = directory.substring(0, indexSlash);
int previousIndex = parent.lastIndexOf('/');
if (previousIndex > 0) {
directory = parent.substring(0, previousIndex);
} else {
return "/";
}
} else {
return "/";
}
} else {
return directory;
}
}
}
@NonNull
public static String addNecessarySlashes(@Nullable String originalPath) {
if (originalPath == null || originalPath.length() == 0) {
return "/";
}
if (originalPath.charAt(originalPath.length() - 1) != '/') {
originalPath = originalPath + '/';
}
if (originalPath.charAt(0) != '/') {
originalPath = '/' + originalPath;
}
return originalPath;
}
}