Directory: assets/tessdata - android

I've downloaded an OCR text recognizer from github.
My problem is: I want to launch my app without being online, but everytime I install the apk on my phone, it starts downloading the english language and the tesseract OCR engine.
I've found an online guide which says I have to create a folder in the assets folder called "tessdata" and put the eng.traineddata and the osd.traineddata in this folder.
I've tried but the download process still starts when I install the app for the first time.
What can I do to make this app completely offline?

First, in your project directory in computer (YourProjectDirectory\app\src\main) create assets folder, int this folder create another tessdata folder. In tessdata folder put your .traineddata files, these will be transferred in your phone when your project starts running. You can download .traineddata files for your language HERE.
For transferring .traineddata files into phone I use this code:
public class TessOCR {
public static final String PACKAGE_NAME = "com.example.dainius.ocr";
public static final String DATA_PATH = Environment
.getExternalStorageDirectory().toString() + "/AndroidOCR/";
public static final String lang = "eng";
private static final String TAG = "TESSERACT";
private AssetManager assetManager;
private TessBaseAPI mTess;
public TessOCR(AssetManager assetManager) {
Log.i(TAG, DATA_PATH);
this.assetManager = assetManager;
String[] paths = new String[] { DATA_PATH, DATA_PATH + "tessdata/" };
for (String path : paths) {
File dir = new File(path);
if (!dir.exists()) {
if (!dir.mkdirs()) {
Log.v(TAG, "ERROR: Creation of directory " + path + " on sdcard failed");
return;
} else {
Log.v(TAG, "Created directory " + path + " on sdcard");
}
}
}
if (!(new File(DATA_PATH + "tessdata/" + lang + ".traineddata")).exists()) {
try {
InputStream in = assetManager.open("tessdata/" + lang + ".traineddata");
OutputStream out = new FileOutputStream(new File(DATA_PATH + "tessdata/", lang + ".traineddata"));
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
in.close();
out.close();
Log.v(TAG, "Copied " + lang + " traineddata");
} catch (IOException e) {
Log.e(TAG, "Was unable to copy " + lang + " traineddata " + e.toString());
}
}
mTess = new TessBaseAPI();
mTess.setDebug(true);
mTess.init(DATA_PATH, lang);
}
public String getResults(Bitmap bitmap)
{
mTess.setImage(bitmap);
String result = mTess.getUTF8Text();
return result;
}
public void onDestroy() {
if (mTess != null)
mTess.end();
}
}
This code checks whether in your phone exists file with directory /AndroidOCR/tessdata/eng.traineddata and if not, creates one and puts .traineddata file here.
For this to occur, in your OnCreate you will have to create AssetManager, which will let you to access that .traineddata file you placed in your computer in your project.
So in your OnCreate in MainActivity:
AssetManager assetManager = getAssets();
TessOCR tess = new TessOCR(assetManager);
Also, to allow your Android project write data into your phone in AndroidManifest.xml file you need to add permision line:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
This is the method I use personally and it works without any errors. If you have any, search in google for answers and if you still can't find an answer, post in comments.

Related

Fixing a Zip Path Traversal Vulnerability In Android

I have uploaded My Application in Google Play Store and Google has given warning that is "Android Security".
In Application, we downloaded the Zip folder and save this Zip folder in internal Storage and than unZip that folder in internal Storage of device.
here is the UnZip Folder Code:
public static void doUnzip(String inputZipFile, String
destinationDirectory, ZipProgressListener zipProgressListener) throws
IOException, RuntimeException {
Log.e(TAG, "doUnzip:inputZipFile: " + inputZipFile);
Log.e(TAG, "doUnzip:destinationDirectory: " + destinationDirectory);
int BUFFER = 6 * 1024;
List zipFiles = new ArrayList();
File sourceZipFile = FileUtils.createValidFile(inputZipFile);
File unzipDestinationDirectory =
FileUtils.createValidFile(destinationDirectory);
unzipDestinationDirectory.mkdir();
String newPath = unzipDestinationDirectory.getAbsolutePath() +
File.separator +
FileUtils.getFileNameWithoutExtension(sourceZipFile.getName());
new File(newPath).mkdir();
ZipFile zipFile;
// Open Zip file for reading
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
int entries = zipFile.size();
int total = 0;
Log.e(TAG, "doUnzip: entries Found !!" + entries);
// Create an enumeration of the entries in the zip file
Enumeration zipFileEntries = zipFile.entries();
if (zipProgressListener != null) {
zipProgressListener.onZipStart();
}
// Process each entry
while (zipFileEntries.hasMoreElements()) {
// grab a zip file entry
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
Log.i(TAG, "[doUnzip] " + currentEntry);
File file = new File(newPath);
File destFile = new File(newPath, currentEntry);
Log.i(TAG, "doUnzip getCanonicalPath : " +
destFile.getCanonicalPath());
if (Build.VERSION.SDK_INT <= VERSION_CODES.LOLLIPOP) {
Log.i(TAG, "doUnzip: LOLLIPOP");
if
(!destFile.getCanonicalPath().startsWith(destinationDirectory)) {
throw new RuntimeException(destFile.getCanonicalPath() +
" is outside of targetDirectory: " + destinationDirectory);
}
} else {
Log.i(TAG, "doUnzip: Above ");
if(!destFile.getCanonicalPath().contains(file.getName()) &&
!destFile.getCanonicalPath().contains("/")){
throw new RuntimeException(destFile.getCanonicalPath() +
" is outside of targetDirectory: " + destinationDirectory);
}
}
if (currentEntry.endsWith(".zip")) {
zipFiles.add(destFile.getAbsolutePath());
}
// grab file's parent directory structure
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
try {
// extract file if not a directory
if (!entry.isDirectory()) {
BufferedInputStream is = new
BufferedInputStream(zipFile.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte data[] = new byte[BUFFER];
// write the current file to disk
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos,
BUFFER);
// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
Log.e(TAG, "unzip:outPath: =>" +
destFile.getAbsolutePath() + "\nFile size: " + destFile.length()
/ 1024);
dest.flush();
dest.close();
is.close();
}
int progress = 0;
if (zipProgressListener != null) {
progress = (total++ * 100 / entries);
zipProgressListener.onZipProgressUpdate(progress);
}
Log.e(TAG, "unzip: PROGRESS::" + progress);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
zipFile.close();
for (Object zipFile1 : zipFiles) {
String zipName = (String) zipFile1;
Log.i(TAG, "doUnzip: ");
doUnzip(zipName, destinationDirectory + File.separator +
zipName.substring(0, zipName.lastIndexOf(".zip")),
zipProgressListener);
}
if (zipProgressListener != null) {
Log.i(TAG, "doUnzip: " + sourceZipFile.getName());
zipProgressListener.onZipCompleted(destinationDirectory +
File.separatorChar + sourceZipFile.getName().substring(0,
sourceZipFile.getName().lastIndexOf(".zip")));
}
}
Here is Google warning :
This information is intended for developers with the app(s) that contain unsafe unzipping patterns, which may potentially lead to a Zip Path Traversal attack. Locations of vulnerable app classes containing unsafe unzipping patterns can be found in the Play Console notification for your app.
Additional details
Zip files can contain an entry (file or directory) having path traversal characters (“../”) in its name. If developers unzip such zip file entries without validating their name, it can potentially cause a path traversal attack, leading to writes in arbitrary directories or even overwriting the files in the app's private folders.
We recommend fixing this issue in your app by checking if canonical paths to unzipped files are underneath an expected directory. Specifically, before using a File object created using the return value of ZipEntry's getName() method, always check if the return value of File.GetCanonicalPath() belongs to the intended directory path. For example:
InputStream is = new InputStream(untrustedFileName);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
while((ZipEntry ze = zis.getNextEntry()) != null) {
File f = new File(DIR, ze.getName());
String canonicalPath = f.getCanonicalPath();
if (!canonicalPath.startsWith(DIR)) {
// SecurityException
}
// Finish unzipping…
}
How can I solve this warning in Above Android OS-6?
Check vulnerability like this
InputStream is = new InputStream(untrustedFileName);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
while((ZipEntry ze = zis.getNextEntry()) != null) {
File outputFile = new File(outputDir, ze.getName());
try {
ensureZipPathSafety(outputFile, outputDir);
} catch (Exception e) {
e.printStackTrace();
return;
}
// Finish unzipping…
}
private void ensureZipPathSafety(final File outputFile, final String destDirectory) throws Exception {
String destDirCanonicalPath = (new File(destDirectory)).getCanonicalPath();
String outputFilecanonicalPath = outputFile.getCanonicalPath();
if (!outputFileCanonicalPath.startsWith(destDirCanonicalPath)) {
throw new Exception(String.format("Found Zip Path Traversal Vulnerability with %s", canonicalPath));
}
}

How to link AIML files to Android Studio

Our team is creating a chatbot application this week. We finished coding the AIML files as well as the main codes in Android Studio. The only problem we have right now is the link between these two.
I've already placed the Ab.jar in the libs folder. Also, I've placed the AIML files in the assets folder.
Assets folder
The codes I think are relevant to linking are the following (from ChatActivity.class):
//checking SD card availability
boolean a = isSDCARDAvailable();
//receiving the assets from the app directory
AssetManager assets = getResources().getAssets();
File seedletDir = new File(Environment.getExternalStorageDirectory().toString() + "/bots/seedlet");
boolean b = seedletDir.mkdirs();
if (seedletDir.exists()) {
//Reading the file
try {
for (String dir : assets.list("seedlet")) {
File subdir = new File(seedletDir.getPath() + "/" + dir);
boolean subdir_check = subdir.mkdirs();
for (String file : assets.list("seedlet/" + dir)) {
File f = new File(seedletDir.getPath() + "/" + dir + "/" + file);
if (f.exists()) {
continue;
}
InputStream in = null;
OutputStream out = null;
in = assets.open("seedlet/" + dir + "/" + file);
out = new FileOutputStream(seedletDir.getPath() + "/" + dir + "/" + file);
//copy file from assets to the mobile's SD card or any secondary memory
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
//get the working directory
MagicStrings.root_path = Environment.getExternalStorageDirectory().toString() + "/seedlet";
System.out.println("Working Directory = " + MagicStrings.root_path);
AIMLProcessor.extension = new PCAIMLProcessorExtension();
//Assign the AIML files to bot for processing
bot = new Bot("seedlet", MagicStrings.root_path, "chat");
chat = new Chat(bot);
String[] args = null;
mainFunction(args);
}
When I ran the application and started to chat with the bot, the bot incorrectly replies "I have no answer for that"
Chat
How can I solve this problem?
Add these two permissions to manifest file
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

Reboot to view files on Android external storage

I have created a simple application to take some user data and write it to a text file which gets saved on the external storage of my device. However, I am unable to access those files using my computer until after I have rebooted my device. Can anyone tell me why this is and if there is something I can do to fix it?
Here is the code I use to write data.
private void commitToFile(String worldOrApp, String xPos, String yPos, String orient) {
Intent intent = getIntent();
String filename = intent.getStringExtra(MainActivity.FILENAME) + ".txt";
final String position = worldOrApp + " - x: " + xPos + "; y: " + yPos + "; alpha: " + orient + "\r\n";
File myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File myFolder = new File(myPath.getAbsolutePath()+"/test_folder");
if (!myFolder.exists()) {
myFolder.mkdirs();
}
File myFile = new File(myFolder, filename);
try {
FileOutputStream fileOutputStream = new FileOutputStream(myFile, true);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
outputStreamWriter.write(position);
outputStreamWriter.flush();
outputStreamWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Thanks to #CommonsWare for the direction. I found the following code at Android saving file to external storage
// Tell the media scanner about the new file so that it is
// immediately available to the user.
MediaScannerConnection.scanFile(this, new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
}
});
which I placed directly underneath the exception catch in my code, and updated file to myFile, which is the relevant File for my commitToFile method.

Storing file on Android for Native reading

I'm writing app for android and I'm using caffe library. My problem is that on start I need to initialize caffe, which is done by passing two files (structures of network) to caffe.
Problem is that I don't know how to store extra files on device. I've added model file to assets, but I don't know how can I read it using file path. Can you tell me where to store these file that could be access using file path?
Thanks for any ideas.
This should do it. Just copy those files to data directory from asset folder. If you already have those files there just load them.
String toPath = "/data/data/" + getPackageName(); // Your application path
private static boolean copyAssetFolder(AssetManager assetManager,
String fromAssetPath, String toPath) {
try {
String[] files = assetManager.list(fromAssetPath);
new File(toPath).mkdirs();
boolean res = true;
for (String file : files)
if (file.contains("."))
res &= copyAsset(assetManager,
fromAssetPath + "/" + file,
toPath + "/" + file);
else
res &= copyAssetFolder(assetManager,
fromAssetPath + "/" + file,
toPath + "/" + file);
return res;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static boolean copyAsset(AssetManager assetManager,
String fromAssetPath, String toPath) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(fromAssetPath);
new File(toPath).createNewFile();
out = new FileOutputStream(toPath);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
return true;
} catch(Exception e) {
e.printStackTrace();
return false;
}
}
private static void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
}
Put them into your project as assets, and then when the app starts, you can read them from the assets and copy them into the app's private storage area. You can find this directory using Context.getFilesDir().
From there, you'll be able to pass the files to Caffe.
Assets are packaged and access only using special methods, so i solved problem by access file and then copy it to new location which i passed to native method.

Generate android apk with cache file

I am making an android webview application,there are a lot of files such as js/css/images must be downloaded from CDN to the app, because the network is not stable in our region and the size of the cached files is much bigger than the app itself, is there some way to build the apk file with the cache files stored automatically when the app runs at first few times.
Put your files and folders to assets. you'll find it in your project directory. When your application runs copy all assets contents to your SD card. then run your app :)
If you need any help about how to copy assets content to SD card let me know.
Copy Assets contents to SD card
Bellow code will copy all the contents of specified folder of your assets to your specified location of your SD card
CopyAssetContents.java
public class CopyAssetContents {
public static boolean copyAssetFolder(AssetManager assetManager,String fromAssetPath, String toPath) {
try {
String[] files = assetManager.list(fromAssetPath);
new File(toPath).mkdirs();
boolean res = true;
for (String file : files)
if (file.contains("."))
res &= copyAsset(assetManager,
fromAssetPath + "/" + file,
toPath + "/" + file);
else
res &= copyAssetFolder(assetManager,
fromAssetPath + "/" + file,
toPath + "/" + file);
return res;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static boolean copyAsset(AssetManager assetManager,
String fromAssetPath, String toPath) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(fromAssetPath);
new File(toPath).createNewFile();
out = new FileOutputStream(toPath);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
return true;
} catch(Exception e) {
e.printStackTrace();
return false;
}
}
private static void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
}
}
For example you have all your contents in a folder named "CONTENTS" inside of your assets.and want to copy all of its content to root of your SD card.
call bellow method.
CopyAssetContents.copyAssetFolder(getAssets(), "CONTENTS", Environment.getExternalStorageDirectory().getAbsolutePath());

Categories

Resources