I am trying to create a new File in SD Card for Android 5.0 and above. So first I am making the user grant the permission through SAF. This is how I am check if the selected Directory is SD Card or Not.
public static boolean wrong_directory_selected(Uri uri, Context con)
{
final File uri_path=new File(FileUtil.getFullPathFromTreeUri(uri,con));
if(uri_path.getName().toLowerCase().equals(new File("SD CARD PATH").getName().toLowerCase()))
{
return false;
}
return true;
}
And then this is how I am Trying to Create a new File.
DocumentFile move = DocumentFile(new File("path)).createFile(mime,"name); // But I am getting java.lang.NullPointerException
Below are the methods which I am using to get the DocumentFile for the Directory to which the file has to be Created.
public static DocumentFile DocumentFile(final File file)
{
DocumentFile rootDocFile = DocumentFile.fromTreeUri(con, permission().getUri());
String[] parts = (file.getPath()).split("\\/");
for (int i = 3; i < parts.length; i++)
{
rootDocFile = rootDocFile.findFile(parts[i]);
}
return rootDocFile;
}
public static UriPermission permission()
{
for (UriPermission permissionUri : con.getContentResolver().getPersistedUriPermissions())
{
final File uri_path = new File(FileUtil.getFullPathFromTreeUri(permissionUri.getUri(), con));
if (uri_path.getName().toLowerCase().equals(new File("SD_CARD_PATH").getName().toLowerCase()))
{
return permissionUri;
}
}
return null;
}
The code is working fine most of the time but sometime I am getting java.lang.NullPointerException.
Any Help would be Grateful.
EDIT: This is my FileUtil class
public final class FileUtil {
private static final String PRIMARY_VOLUME_NAME = "primary";
#Nullable
public static String getFullPathFromTreeUri(#Nullable final Uri treeUri, Context con)
{
if (treeUri == null)
{
return null;
}
String volumePath = FileUtil.getVolumePath(FileUtil.getVolumeIdFromTreeUri(treeUri),con);
if (volumePath == null)
{
return File.separator;
}
if (volumePath.endsWith(File.separator))
{
volumePath = volumePath.substring(0, volumePath.length() - 1);
}
String documentPath = FileUtil.getDocumentPathFromTreeUri(treeUri);
if (documentPath.endsWith(File.separator))
{
documentPath = documentPath.substring(0, documentPath.length() - 1);
}
if (documentPath.length() > 0)
{
if (documentPath.startsWith(File.separator))
{
return volumePath + documentPath;
}
else {
return volumePath + File.separator + documentPath;
}
}
else
{
return volumePath;
}
}
private static String getVolumePath(final String volumeId, Context con)
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
{
return null;
}
try {
StorageManager mStorageManager =
(StorageManager) con.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getUuid = storageVolumeClazz.getMethod("getUuid");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++)
{
Object storageVolumeElement = Array.get(result, i);
String uuid = (String) getUuid.invoke(storageVolumeElement);
Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);
// primary volume?
if (primary && PRIMARY_VOLUME_NAME.equals(volumeId))
{
return (String) getPath.invoke(storageVolumeElement);
}
// other volumes?
if (uuid != null)
{
if (uuid.equals(volumeId))
{
return (String) getPath.invoke(storageVolumeElement);
}
}
}
// not found.
return null;
}
catch (Exception ex)
{
return null;
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getVolumeIdFromTreeUri(final Uri treeUri)
{
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if (split.length > 0)
{
return split[0];
}
else
{
return null;
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getDocumentPathFromTreeUri(final Uri treeUri)
{
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if ((split.length >= 2) && (split[1] != null))
{
return split[1];
}
else
{
return File.separator;
}
}
}
EDIT 2 :
The Path in which the file has to be created is fine and I have also checked the Permission URI and even that is not null.
The Values are
The path where the file has to be created- /storage/external_SD
Permission Uri- content://com.android.externalstorage.documents/tree/6634-3765%3A
EDIT 3:
I am using this library to find the SD Card path.
Continue from this answer now that you have the DocumentFile (which is a directory to create a file inside it) through the loop just use myDocumentFile.createFile(...) to create a new file on your desired directory.
// creating the file
DocumentFile documentFileNewFile = documentFileGoal.createFile(myMimeType,
myNewFileName);
Then stream to it
outputStream = getContentResolver().openOutputStream(documentFileNewFile.getUri());
inputStream = new FileInputStream(myInputFile);
...
if (outputStream != null) {
byte[] buffer = new byte[1024];
int read;
while ((read = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, read);
}
}
...
...
...
} finally {
if (inputStream != null)
inputStream.close();
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
}
Edite
Prevent findFile on a null DocumentFile by checking the value of rootDocFile on each loop. (happens when the user selects a wrong path instead of the sd-card)
for (int i = 3; i < parts.length; i++)
{
if (rootDocFile != null) {
rootDocFile = rootDocFile.findFile(parts[i]);
}
}
Related
I am uploading my App on play store but get me bellow error:
Zip Path Traversal Your app contains an unsafe unzipping pattern that
may lead to a Path Traversal vulnerability. Please see this Google
Help Center article to learn how to fix the issue.
org.apache.cordova.Zip.unzipSync
I edited my source code like this LINK, but get me error.
Here is my source code changed:
public class Zip extends CordovaPlugin {
private static final String LOG_TAG = "Zip";
// Can't use DataInputStream because it has the wrong endian-ness.
private static int readInt(InputStream is) throws IOException {
int a = is.read();
int b = is.read();
int c = is.read();
int d = is.read();
return a | b << 8 | c << 16 | d << 24;
}
#Override
public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
if ("unzip".equals(action)) {
unzip(args, callbackContext);
return true;
}
return false;
}
private void unzip(final CordovaArgs args, final CallbackContext callbackContext) {
this.cordova.getThreadPool().execute(new Runnable() {
public void run() {
unzipSync(args, callbackContext);
}
});
}
private void unzipSync(CordovaArgs args, CallbackContext callbackContext) {
InputStream inputStream = null;
try {
String zipFileName = args.getString(0);
String outputDirectory = args.getString(1);
// Since Cordova 3.3.0 and release of File plugins, files are accessed via cdvfile://
// Accept a path or a URI for the source zip.
Uri zipUri = getUriForArg(zipFileName);
Uri outputUri = getUriForArg(outputDirectory);
CordovaResourceApi resourceApi = webView.getResourceApi();
File tempFile = resourceApi.mapUriToFile(zipUri);
if (tempFile == null || !tempFile.exists()) {
String errorMessage = "Zip file does not exist";
callbackContext.error(errorMessage);
Log.e(LOG_TAG, errorMessage);
return;
}
File outputDir = resourceApi.mapUriToFile(outputUri);
outputDirectory = outputDir.getAbsolutePath();
outputDirectory += outputDirectory.endsWith(File.separator) ? "" : File.separator;
if (outputDir == null || (!outputDir.exists() && !outputDir.mkdirs())) {
String errorMessage = "Could not create output directory";
callbackContext.error(errorMessage);
Log.e(LOG_TAG, errorMessage);
return;
}
OpenForReadResult zipFile = resourceApi.openForRead(zipUri);
ProgressEvent progress = new ProgressEvent();
progress.setTotal(zipFile.length);
inputStream = new BufferedInputStream(zipFile.inputStream);
inputStream.mark(10);
int magic = readInt(inputStream);
if (magic != 875721283) { // CRX identifier
inputStream.reset();
} else {
// CRX files contain a header. This header consists of:
// * 4 bytes of magic number
// * 4 bytes of CRX format version,
// * 4 bytes of public key length
// * 4 bytes of signature length
// * the public key
// * the signature
// and then the ordinary zip data follows. We skip over the header before creating the ZipInputStream.
readInt(inputStream); // version == 2.
int pubkeyLength = readInt(inputStream);
int signatureLength = readInt(inputStream);
inputStream.skip(pubkeyLength + signatureLength);
progress.setLoaded(16 + pubkeyLength + signatureLength);
}
// The inputstream is now pointing at the start of the actual zip file content.
ZipInputStream zis = new ZipInputStream(inputStream);
inputStream = zis;
ZipEntry ze;
byte[] buffer = new byte[32 * 1024];
boolean anyEntries = false;
while ((ze = zis.getNextEntry()) != null) {
try {
anyEntries = true;
String compressedName = ze.getName();
if (ze.isDirectory()) {
try {
File dir = new File(outputDirectory + compressedName);
File f = new File(dir, ze.getName());
String canonicalPath = f.getCanonicalPath();
if (!canonicalPath.startsWith(dir.toString())){
dir.mkdirs();
}else {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
} catch (Exception e) {
String errorMessage = "An error occurred while unzipping.";
callbackContext.error(errorMessage);
Log.e(LOG_TAG, errorMessage, e);
}
} else {
File file = new File(outputDirectory + compressedName);
File f = new File(file, ze.getName());
String canonicalPath = f.getCanonicalPath();
if (!canonicalPath.startsWith(file.toString())) {
file.getParentFile().mkdirs();
if (file.exists() || file.createNewFile()) {
try {
Log.w("Zip", "extracting: " + file.getPath());
FileOutputStream fout = new FileOutputStream(file);
int count;
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
} catch (Exception e) {
String errorMessage = "An error occurred while unzipping.";
callbackContext.error(errorMessage);
Log.e(LOG_TAG, errorMessage, e);
}
}
}else {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
}
progress.addLoaded(ze.getCompressedSize());
updateProgress(callbackContext, progress);
zis.closeEntry();
} catch (Exception e) {
String errorMessage = "An error occurred while unzipping.";
callbackContext.error(errorMessage);
Log.e(LOG_TAG, errorMessage, e);
}
}
// final progress = 100%
progress.setLoaded(progress.getTotal());
updateProgress(callbackContext, progress);
if (anyEntries)
callbackContext.success();
else
callbackContext.error("Bad zip file");
} catch (Exception e) {
String errorMessage = "An error occurred while unzipping.";
callbackContext.error(errorMessage);
Log.e(LOG_TAG, errorMessage, e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
}
private void updateProgress(CallbackContext callbackContext, ProgressEvent progress) throws JSONException {
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
pluginResult.setKeepCallback(true);
callbackContext.sendPluginResult(pluginResult);
}
private Uri getUriForArg(String arg) {
CordovaResourceApi resourceApi = webView.getResourceApi();
Uri tmpTarget = Uri.parse(arg);
return resourceApi.remapUri(
tmpTarget.getScheme() != null ? tmpTarget : Uri.fromFile(new File(arg)));
}
private static class ProgressEvent {
private long loaded;
private long total;
public long getLoaded() {
return loaded;
}
public void setLoaded(long loaded) {
this.loaded = loaded;
}
public void addLoaded(long add) {
this.loaded += add;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public JSONObject toJSONObject() throws JSONException {
return new JSONObject(
"{loaded:" + loaded +
",total:" + total + "}");
}
}
}
I was developing an app which had requirement to implement root detection logic, so by researching I found some detection logic in JAVA and had implemented following class.
class RootDetection {
public boolean isDeviceRooted() {
return checkForBinary("su") || checkForBinary("busybox") || checkForMaliciousPaths() || checkSUonPath()
|| detectRootManagementApps() || detectPotentiallyDangerousApps() || detectRootCloakingApps()
|| checkForDangerousProps() || checkForRWPaths()
|| detectTestKeys() || checkSuExists();
}
private boolean detectTestKeys() {
String buildTags = android.os.Build.TAGS;
String buildFinger = Build.FINGERPRINT;
String product = Build.PRODUCT;
String hardware = Build.HARDWARE;
String display = Build.DISPLAY;
System.out.println("Java: build: " + buildTags + "\nFingerprint: " + buildFinger + "\n Product: " + product + "\n Hardware: " + hardware + "\nDisplay: " + display);
return (buildTags != null) && (buildTags.contains("test-keys") || buildFinger.contains("genric.*test-keys") || product.contains("generic") || product.contains("sdk") || hardware.contains("goldfish") || display.contains(".*test-keys"));
}
private boolean detectRootManagementApps() {
return detectRootManagementApps(null);
}
private boolean detectRootManagementApps(String[] additionalRootManagementApps) {
ArrayList<String> packages = new ArrayList<>();
packages.addAll(Arrays.asList(knownRootAppsPackages));
if (additionalRootManagementApps != null && additionalRootManagementApps.length > 0) {
packages.addAll(Arrays.asList(additionalRootManagementApps));
}
return isAnyPackageFromListInstalled(packages);
}
private boolean detectPotentiallyDangerousApps() {
return detectPotentiallyDangerousApps(null);
}
private boolean detectPotentiallyDangerousApps(String[] additionalDangerousApps) {
ArrayList<String> packages = new ArrayList<>();
packages.addAll(Arrays.asList(knownDangerousAppsPackages));
if (additionalDangerousApps != null && additionalDangerousApps.length > 0) {
packages.addAll(Arrays.asList(additionalDangerousApps));
}
return isAnyPackageFromListInstalled(packages);
}
private boolean detectRootCloakingApps() {
return detectRootCloakingApps(null);
}
private boolean detectRootCloakingApps(String[] additionalRootCloakingApps) {
ArrayList<String> packages = new ArrayList<>();
packages.addAll(Arrays.asList(knownRootCloakingPackages));
if (additionalRootCloakingApps != null && additionalRootCloakingApps.length > 0) {
packages.addAll(Arrays.asList(additionalRootCloakingApps));
}
return isAnyPackageFromListInstalled(packages);
}
private boolean checkForBinary(String filename) {
for (String path : suPaths) {
String completePath = path + filename;
File f = new File(completePath);
boolean fileExists = f.exists();
if (fileExists) {
return true;
}
}
return false;
}
private boolean checkForMaliciousPaths() {
for (String path : maliciousPaths) {
File f = new File(path);
boolean fileExists = f.exists();
if (fileExists) {
return true;
}
}
return false;
}
private static boolean checkSUonPath() {
for (String pathDir : System.getenv("PATH").split(":")) {
if (new File(pathDir, "su").exists()) {
return true;
}
}
return false;
}
private String[] propsReader() {
InputStream inputstream = null;
try {
inputstream = Runtime.getRuntime().exec("getprop").getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
String propval = "";
try {
propval = new Scanner(inputstream).useDelimiter("\\A").next();
} catch (NoSuchElementException e) {
}
return propval.split("\n");
}
private String[] mountReader() {
InputStream inputstream = null;
try {
inputstream = Runtime.getRuntime().exec("mount").getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
if (inputstream == null) return null;
String propval = "";
try {
propval = new Scanner(inputstream).useDelimiter("\\A").next();
} catch (NoSuchElementException e) {
e.printStackTrace();
}
return propval.split("\n");
}
private boolean isAnyPackageFromListInstalled(List<String> packages) {
PackageManager pm = activity.getPackageManager();
for (String packageName : packages) {
try {
pm.getPackageInfo(packageName, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
}
}
return false;
}
private boolean checkForDangerousProps() {
final Map<String, String> dangerousProps = new HashMap<>();
dangerousProps.put("ro.debuggable", "1");
dangerousProps.put("ro.secure", "0");
String[] lines = propsReader();
for (String line : lines) {
for (String key : dangerousProps.keySet()) {
if (line.contains(key)) {
String badValue = dangerousProps.get(key);
badValue = "[" + badValue + "]";
if (line.contains(badValue)) {
return true;
}
}
}
}
return false;
}
private boolean checkForRWPaths() {
String[] lines = mountReader();
for (String line : lines) {
String[] args = line.split(" ");
if (args.length < 4) {
continue;
}
String mountPoint = args[1];
String mountOptions = args[3];
for (String pathToCheck : pathsThatShouldNotBeWrtiable) {
if (mountPoint.equalsIgnoreCase(pathToCheck)) {
for (String option : mountOptions.split(",")) {
if (option.equalsIgnoreCase("rw")) {
return true;
}
}
}
}
}
return false;
}
private boolean checkSuExists() {
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[]{"which", "su"});
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
return in.readLine() != null;
} catch (Throwable t) {
return false;
} finally {
if (process != null) process.destroy();
}
}
}
but now to increase security I want to do this root detection logic in native C++ JNI code. I managed to migrate package detection code to JNI C but am not able to find anything regarding these 3 functions
checkForDangerousProps(),checkForRWPaths(),checkSuExists()
these 3 use Runtime.getRuntime().exec which am not able to find. can someone help me in converting this 3 logics to JNI C one from above code? Help would be really appreciated.
Pls guys help.
I am using Tesseract ocr in Spring MVC to convert some images in searchable pdf files.
I used tessApi with tessrenderresult that works perfectly fine in simple java project. But, it is not working in spring MVC project while I am deploying it in tomcat 7. I am getting error in line :
int result = api.TessBaseAPIProcessPages1(handle, imagepath, null, 0,
renderer);
I am getting following Exception
Error during processing.
Below is my code :
public class ImageToPDF {
private static String datapath = "F:/Projects Dev/Pfe Projects/GedWeb/.";
private static String language = "fra";
private static TessAPI api;
private static TessBaseAPI handle;
public ImageToPDF() {
super();
api = new TessDllAPIImpl().getInstance();
handle = api.TessBaseAPICreate();
}
public void finalize() throws Throwable {
super.finalize();
}
public void convert(String imagepath) {
File file = new File(imagepath);
String nomfile = FilenameUtils.removeExtension(file.getName());
String parentfolder = file.getParent();
String output = parentfolder + File.separator + nomfile + ".txt";
int set_only_init_params = FALSE;
int oem = TessOcrEngineMode.OEM_DEFAULT;
PointerByReference configs = null;
int configs_size = 0;
String[] params = { "load_system_dawg", "tessedit_char_whitelist" };
String vals[] = { "Relevé", "" }; // 0123456789-.IThisalotfpnex
PointerByReference vars_vec = new PointerByReference();
vars_vec.setPointer(new StringArray(params));
PointerByReference vars_values = new PointerByReference();
vars_values.setPointer(new StringArray(vals));
NativeSize vars_vec_size = new NativeSize(params.length);
api.TessBaseAPISetOutputName(handle, output);
int rc = api.TessBaseAPIInit4(handle, datapath, language, oem, configs,
configs_size, vars_vec, vars_values, vars_vec_size,
set_only_init_params);
System.out.println(rc);
if (rc != 0) {
api.TessBaseAPIDelete(handle);
System.err.println("Could not initialize tesseract.");
return;
}
TessResultRenderer renderer = api.TessHOcrRendererCreate();
api.TessResultRendererInsert(renderer, api.TessBoxTextRendererCreate());
api.TessResultRendererInsert(renderer, api.TessTextRendererCreate());
String dataPath = api.TessBaseAPIGetDatapath(handle);
api.TessResultRendererInsert(renderer,
api.TessPDFRendererCreate(dataPath));
api.TessResultRendererBeginDocument(renderer, imagepath);
int result = api.TessBaseAPIProcessPages1(handle, imagepath, null, 0,
renderer);
api.TessResultRendererEndDocument(renderer);
System.out.println(handle.toString());
System.out.println(renderer.toString());
if (result == FALSE) {
System.err.println("Error during processing.");
return;
}
File file1 = new File(imagepath);
String parent = file1.getParent();
String nomfile1 = FilenameUtils.removeExtension(file1.getName());
String outputbase = parent + File.separator + nomfile1;
for (; renderer != null; renderer = api
.TessResultRendererNext(renderer)) {
String ext = api.TessResultRendererExtention(renderer).getString(0);
System.out
.println(String
.format("TessResultRendererExtention: %s\nTessResultRendererTitle: %s\nTessResultRendererImageNum: %d",
ext, api.TessResultRendererTitle(renderer)
.getString(0),
api.TessResultRendererImageNum(renderer)));
PointerByReference data = new PointerByReference();
IntByReference dataLength = new IntByReference();
result = api
.TessResultRendererGetOutput(renderer, data, dataLength);
if (result == TRUE) {
if (ext.equals("pdf")) {
int length = dataLength.getValue();
byte[] bytes = data.getValue().getByteArray(0, length);
try {
File file3 = new File(outputbase + "." + ext);
// create parent dirs when necessary
if (file3.getParentFile() != null) {
file3.getParentFile().mkdirs();
}
FileOutputStream bw = new FileOutputStream(
file3.getAbsoluteFile());
bw.write(bytes);
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
String result1 = data.getValue().getString(0);
}
}
}
api.TessDeleteResultRenderer(renderer);
api.TessBaseAPIDelete(handle);
}
}
Any suggestion?
I am building an app with a self-made LruDiskCache to reduce loading times. The LruDiskCache downloads a file if it is not already present and returns it via a callback. I'm having a very weird issue where the first image isn't loaded properly. This only happends the first time a activity is started. If you open the same activity a secon time the image is loaded properly.
After debugging i found that i get the following debug message: --- SkImageDecoder Factory returned null. I've read multiple issues regarding this problem, but the all involve downloading an image through an inputstream, while i'm first downloading the image to persistent storage.
My cache-class:
public class LruDiskCache {
private static final String LOGTAG = "LruDiskCache";
//Cache size
private final long cacheMaxSize;
private volatile long currentCacheSize;
public static final int DEFAULT_CACHE_SIZE_KB = 1024;
public static final int MINIMUM_CACHE_SIZE_KB = 128;
//Preferences
private final String cachePreferencesName;
private final String chachePreferencesSize = "cacheSize";
private final SharedPreferences cachePreferences;
//Lock
private final Object mDiskCacheLock = new Object();
//Cache Path
private final File cachePath;
//Initialisation
private boolean openingCache;
//Static variables
private static final long BYTES_IN_KB = 1024;
public LruDiskCache (Context c, String cacheName, int cacheMaxSizeKB){
//Preferences
cachePreferencesName = cacheName + "CachePreferences";
cachePreferences = c.getSharedPreferences(cachePreferencesName, Context.MODE_PRIVATE);
//Paths
cachePath = new File(c.getFilesDir().getPath()+"/"+cacheName);
//Cache size
if(cacheMaxSizeKB < MINIMUM_CACHE_SIZE_KB){
throw new IllegalArgumentException
("Invalid cache size, size must be bigger than "
+ MINIMUM_CACHE_SIZE_KB + "kb.");
}
cacheMaxSize = cacheMaxSizeKB * BYTES_IN_KB;
//Initialize
openingCache = true;
new InitializeCache().execute();
}
//PUBLIC METHODS
public synchronized void getRemoteFile(URL remoteFile, Callback c){
if(openingCache){
try {
mDiskCacheLock.wait(1000);
} catch (InterruptedException e) {
c.onRequestedFileRetrieved(null, false);
}
}
String path = createFilePath(remoteFile);
File f = new File(path);
if(f.exists() && f.isFile()){
f.setLastModified(System.currentTimeMillis());
c.onRequestedFileRetrieved(f, true);
} else {
new RemoteFileDownloadTask(remoteFile, f, c).execute();
}
}
//PRIVATE METHODS
private String createFilePath(URL key){
return cachePath + "/" + key.getHost() + key.getPath();
}
private synchronized void cleanUpCache(){
long cacheSize = getDirSize(cachePath);
int failedToDeleteCounter = 0;
while(cacheSize > cacheMaxSize){
File toDelete = getFirstRequestedFile(cachePath);
Log.w("LruDiskCache", "File to be deleted: " + toDelete.getName() + ".");
long toDeleteSize = toDelete.length();
if(!toDelete.delete()){
failedToDeleteCounter++;
} else {
Log.w("LruDiskCache", "Deleted file to clean cache.");
cacheSize -= toDeleteSize;
}
if(failedToDeleteCounter > 100){
Log.w("LruDiskCache", "Failed to clean cache, could not delete files.");
break;
}
}
}
private File getFirstRequestedFile(File directory){
File first = null;
for(File f : directory.listFiles()){
if(f.isFile()){
if(first == null){
first = f;
} else {
if(f.lastModified() < first.lastModified()){
first = f;
}
}
} else if (f.isDirectory()) {
File firstReqInDir = getFirstRequestedFile(f);
if(first == null){
first = firstReqInDir;
} else if (firstReqInDir.lastModified() < first.lastModified()){
first = firstReqInDir;
}
}
}
return first;
}
private long getDirSize(File dir) {
long bytes = 0;
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
bytes += getDirSize(f);
} else {
bytes += f.length();
}
}
return bytes;
}
//ASYNC TASKS
private class InitializeCache implements Runnable{
public void execute(){
new Thread(this).start();
}
#Override
public void run() {
synchronized (mDiskCacheLock) {
Log.d("Initialize cache", "Starting init...");
if (!cachePath.exists()) {
if (!cachePath.mkdirs()) {
mDiskCacheLock.notifyAll();
openingCache = false;
}
}
currentCacheSize = getDirSize(cachePath);
Log.d("LruDiskCache", "Cache size: " + currentCacheSize / BYTES_IN_KB + "kb.");
if(currentCacheSize > cacheMaxSize){
cleanUpCache();
}
Log.d("Initialize cache", "Did init...");
mDiskCacheLock.notifyAll();
openingCache = false;
}
}
}
private class RemoteFileDownloadTask extends AsyncTask<Void, Void, File> {
private final Callback c;
private URL remoteFile;
private File localFile;
protected RemoteFileDownloadTask(URL remoteFile, File localFile, Callback c){
this.c = c;
this.remoteFile = remoteFile;
this.localFile = localFile;
}
#Override
protected File doInBackground(Void... params) {
if(retrieveRemoteFile(remoteFile, localFile)){
return localFile;
} else {
return null;
}
}
#Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
currentCacheSize += file.length();
if(currentCacheSize > cacheMaxSize){
cleanUpCache();
}
c.onRequestedFileRetrieved(file, true);
}
public boolean retrieveRemoteFile(URL remote, File filePath) {
try {
if (filePath.exists() && filePath.isFile()) {
return true;
} else {
if (!filePath.getParentFile().exists()) {
if (!filePath.getParentFile().mkdirs()) {
return false;
}
}
}
} catch (Exception e){
return false;
}
Log.w("LruDiskCache", "Created empty file: " + filePath.getPath());
Log.w("LruDiskCache", "Downloading source from: " + remote.toString());
try {
FileOutputStream fos;
InputStream is;
BufferedInputStream bis;
fos = new FileOutputStream(filePath);
HttpURLConnection con = (HttpURLConnection) remote.openConnection();
if(con.getResponseCode() != HttpURLConnection.HTTP_OK){
Log.e("Receiver", "HTTP Response code is not OK");
return false;
}
is = con.getInputStream();
bis = new BufferedInputStream(is);
while(bis.available() > 0){
fos.write(bis.read());
}
bis.close();
is.close();
fos.close();
} catch (Exception e) {
return false;
}
Log.d("LruDiskCacheReceiver", filePath.getPath() + " received, " + filePath.length() + " bytes.");
return true;
}
}
//CALLBACK INTERFACE
public interface Callback{
public abstract void onRequestedFileRetrieved(File f, boolean success);
}
}
And the implementation is shown here:
URL emblemURL = new URL(data[position].getEmblemUrl());
cache.getRemoteFile(emblemURL, new LruDiskCache.Callback() {
#Override
public void onRequestedFileRetrieved(File f, boolean success) {
if (success && f.exists()) {
Drawable bitmap = Drawable.createFromPath(f.getAbsolutePath());
emblem.setImageDrawable(bitmap);
}
}
});
Any help is appreciated.
I have written the following method to detemine whether file in question is formatted with DOS/ MAC, or UNIX line endings.
I see at least 1 obvious issue:
1. i am hoping that i will get the EOL on the first run, say within first 1000 bytes. This may or may not happen.
I ask you to review this and suggest improvements which will lead to hardening the code and making it more generic.
THANK YOU.
new FileFormat().discover(fileName, 0, 1000);
and then
public void discover(String fileName, int offset, int depth) throws IOException {
BufferedInputStream in = new BufferedInputStream(new FileInputStream(fileName));
FileReader a = new FileReader(new File(fileName));
byte[] bytes = new byte[(int) depth];
in.read(bytes, offset, depth);
a.close();
in.close();
int thisByte;
int nextByte;
boolean isDos = false;
boolean isUnix = false;
boolean isMac = false;
for (int i = 0; i < (bytes.length - 1); i++) {
thisByte = bytes[i];
nextByte = bytes[i + 1];
if (thisByte == 10 && nextByte != 13) {
isDos = true;
break;
} else if (thisByte == 13) {
isUnix = true;
break;
} else if (thisByte == 10) {
isMac = true;
break;
}
}
if (!(isDos || isMac || isUnix)) {
discover(fileName, offset + depth, depth + 1000);
} else {
// do something clever
}
}
Your method seems unnecessarily complicated. Why not:
public class FileFormat {
public enum FileType { WINDOWS, UNIX, MAC, UNKNOWN }
private static final char CR = '\r';
private static final char LF = '\n';
public static FileType discover(String fileName) throws IOException {
Reader reader = new BufferedReader(new FileReader(fileName));
FileType result = discover(reader);
reader.close();
return result;
}
private static FileType discover(Reader reader) throws IOException {
int c;
while ((c = reader.read()) != -1) {
switch(c) {
case LF: return FileType.UNIX;
case CR: {
if (reader.read() == LF) return FileType.WINDOWS;
return FileType.MAC;
}
default: continue;
}
}
return FileType.UNKNOWN;
}
}
Which puts this in a static method that you can then call and use as:
switch(FileFormat.discover(fileName) {
case WINDOWS: ...
case MAC: ...
case UNKNOWN: ...
}
Here's a rough implementation that guesses the line ending type based on a simple majority and falls back on unknown in a worst-case scenario:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.EnumMap;
import java.util.Map;
import java.util.Scanner;
class LineEndings
{
private enum ExitState
{
SUCCESS, FAILURE;
}
public enum LineEndingType
{
DOS("Windows"), MAC("Mac OS Classic"), UNIX("Unix/Linux/Mac OS X"), UNKNOWN("Unknown");
private final String name;
private LineEndingType(String name)
{
this.name = name;
}
public String toString()
{
if (null == this.name) {
return super.toString();
}
else {
return this.name;
}
}
}
public static void main(String[] arguments)
{
ExitState exitState = ExitState.SUCCESS;
File inputFile = getInputFile();
if (null == inputFile) {
exitState = ExitState.FAILURE;
System.out.println("Error: No input file specified.");
}
else {
System.out.println("Determining line endings for: " + inputFile.getName());
try {
LineEndingType lineEndingType = getLineEndingType(inputFile);
System.out.println("Determined line endings: " + lineEndingType);
}
catch (java.io.IOException exception) {
exitState = ExitState.FAILURE;
System.out.println("Error: " + exception.getMessage());
}
}
switch (exitState) {
case SUCCESS:
System.exit(0);
break;
case FAILURE:
System.exit(1);
break;
}
}
private static File getInputFile()
{
File inputFile = null;
Scanner stdinScanner = new Scanner(System.in);
while (true) {
System.out.println("Enter the input file name:");
System.out.print(">> ");
if (stdinScanner.hasNext()) {
String inputFileName = stdinScanner.next();
inputFile = new File(inputFileName);
if (!inputFile.exists()) {
System.out.println("File not found.\n");
}
else if (!inputFile.canRead()) {
System.out.println("Could not read file.\n");
}
else {
break;
}
}
else {
inputFile = null;
break;
}
}
System.out.println();
return inputFile;
}
private static LineEndingType getLineEndingType(File inputFile)
throws java.io.IOException, java.io.FileNotFoundException
{
EnumMap<LineEndingType, Integer> lineEndingTypeCount =
new EnumMap<LineEndingType, Integer>(LineEndingType.class);
BufferedReader inputReader = new BufferedReader(new FileReader(inputFile));
LineEndingType currentLineEndingType = null;
while (inputReader.ready()) {
int token = inputReader.read();
if ('\n' == token) {
currentLineEndingType = LineEndingType.UNIX;
}
else if ('\r' == token) {
if (inputReader.ready()) {
int nextToken = inputReader.read();
if ('\n' == nextToken) {
currentLineEndingType = LineEndingType.DOS;
}
else {
currentLineEndingType = LineEndingType.MAC;
}
}
}
if (null != currentLineEndingType) {
incrementLineEndingType(lineEndingTypeCount, currentLineEndingType);
currentLineEndingType = null;
}
}
return getMostFrequentLineEndingType(lineEndingTypeCount);
}
private static void incrementLineEndingType(Map<LineEndingType, Integer> lineEndingTypeCount, LineEndingType targetLineEndingType)
{
Integer targetLineEndingCount = lineEndingTypeCount.get(targetLineEndingType);
if (null == targetLineEndingCount) {
targetLineEndingCount = 0;
}
else {
targetLineEndingCount++;
}
lineEndingTypeCount.put(targetLineEndingType, targetLineEndingCount);
}
private static LineEndingType getMostFrequentLineEndingType(Map<LineEndingType, Integer> lineEndingTypeCount)
{
Integer maximumEntryCount = Integer.MIN_VALUE;
Map.Entry<LineEndingType, Integer> mostFrequentEntry = null;
for (Map.Entry<LineEndingType, Integer> entry : lineEndingTypeCount.entrySet()) {
int entryCount = entry.getValue();
if (entryCount > maximumEntryCount) {
mostFrequentEntry = entry;
maximumEntryCount = entryCount;
}
}
if (null != mostFrequentEntry) {
return mostFrequentEntry.getKey();
}
else {
return LineEndingType.UNKNOWN;
}
}
}
There is a whole lot wrong with this. You need to understand the FileInputStream class better. Note that read is not guaranteed to read all the bytes you requested. offset is the offset into the array, not the file. And so on.