I have an application that allows the user to sign the tablet and an image will be produced. That part I have working with confirmation as I am able to view the images after they are created and saved.
I am writing them to a .csv file with the following code.
//region - Create CSV file
writer = new CSVWriter(new FileWriter(sdCardPath + "Orders_Summary_Report.csv"), ',', CSVWriter.NO_QUOTE_CHARACTER, CSVWriter.DEFAULT_ESCAPE_CHARACTER, "\r\n");
//writer.writeNext(CSVcursor.getColumnNames()); //Write the column headings first before the looping starts
String [] headings = ("Order Id,Item ID,Item Name,Item Price,Item Count,Item Total,Paid Amount,VOID Order,Payment Method,Order Signature,Member/Authorization,Tab Number,Order Time").split(",");
writer.writeNext(headings);
for(CSVcursor.moveToFirst(); !CSVcursor.isAfterLast(); CSVcursor.moveToNext()) //Loop through all results (use row count of table)
{
String[] entries = (CSVcursor.getInt(CSVcursor.getColumnIndex("orderId"))+ ","
+CSVcursor.getInt(CSVcursor.getColumnIndex("itemId"))+ ","
+CSVcursor.getString(CSVcursor.getColumnIndex("itemName"))+ ","
+CSVcursor.getString(CSVcursor.getColumnIndex("itemPrice")) + ","
+CSVcursor.getInt(CSVcursor.getColumnIndex("itemCount"))+ ","
+CSVcursor.getString(CSVcursor.getColumnIndex("itemTotal"))+ ","
+CSVcursor.getString(CSVcursor.getColumnIndex("orderPaid"))+ ","
+CSVcursor.getInt(CSVcursor.getColumnIndex("orderVoid"))+ ","
+CSVcursor.getString(CSVcursor.getColumnIndex("orderType"))+ ","
+CSVcursor.getBlob(CSVcursor.getColumnIndex("orderSignature"))+ ","
+CSVcursor.getString(CSVcursor.getColumnIndex("referenceId"))+ ","
+CSVcursor.getInt(CSVcursor.getColumnIndex("orderTab"))+ ","
+CSVcursor.getString(CSVcursor.getColumnIndex("orderTime"))).split(","); //Split the string by the delimiter
writer.writeNext(entries); //Write current row of DB to file
writer.flush(); //Flush the stream
}
writer.close();
The file is getting written to successfully, but when I view the .csv file, the column that is saving the blob data looks like this.
B#2342342
B#2123982
B#3952929
It looks like I'm just saving the signature or some other attribute of the file rather than the file itself.
Do I need to stream the blob by bit/buffer into the csv file?
Just echoing #CommonsWare. The right thing to do is save the blob to the disk as am image. That way it can be easily retrieved for embedding into a file if needed. I still save my images to the db as blobs and just display them in a view. If you want to put these files into a csv, I would consider another type like pdf.
Related
I am trying to see if FTP files exists on server before uploading them as I am using FTP4J library which uploads existing files (overwrites) but my check does not work even though the file exists in the array:
for (String ftpFolder : my_ftpFolders)
{
FTPFile[] list = ftpClient.list(ftpFolder);
for (int i = 0; i < list.length; i++)
{
String ftpFile = list[i].getName();
Log.v("LOG", "zzz_ARRAY: " +Arrays.asList(myFiles));
Log.v("LOG", "zzz_ftpFile: " +ftpFile);
if (Arrays.asList(myFiles).contains(ftpFile))
{
Log.v("LOG", "zzz_ftpFile EXIST: " +ftpFile);
}else{
Log.v("LOG", "zzz_ftpFile NOT EXIST: " +ftpFile);
}
}
}
Array Print (Arrays.asList(myFiles)):
[[/storage/emulated/0/Download/mohamed-nohassi-odxB5oIG_iA-unsplash.jpg, /storage/emulated/0/Download/atheek-mohomed-e0JOwGDsUHQ-unsplash.jpg]]
ftpFile Print:
mohamed-nohassi-odxB5oIG_iA-unsplash.jpg
As you can see, the file exists in the array but Array.asList(myFiles) returns false
That is because you are trying to seach the string mohamed-nohassi-odxB5oIG_iA-unsplash.jpg
in a list that does not contains it. The value in the list is /storage/emulated/0/Download/mohamed-nohassi-odxB5oIG_iA-unsplash.jpg, that is clearly different.
To obtain the result you're trying to achieve, you have to split the file name, and get rid of the path. You can do it with some string manipulation. First, import Collectors:
import java.util.stream.Collectors;
Then, you can use this code to test if the list contains the file:
if (Arrays.asList(myFiles).stream().map(el -> {
String[] tokenized = el.split("/");
return tokenized.length > 0 ? tokenized[tokenized.length-1] : "";
}).collect(Collectors.toList()).contains(ftpFile))
With this code, for each element in the list you're getting just the last part after the last "/", then you're checking if the ftpFile is contained in the list you'll obtain after taking only all the last parts.
How do I rename a text file from "savedfile.txt" to variable ".txt"?
The user enters text, saved in a string called message. Presses the save button. Right now, a file called savedfile.txt is generated. If I input 1234, I want my file to be called 1234.txt.
public void sendClick(View view) {
// TODO : RENAME FILE TO INPUT VARIABLE
File file = new File (path + "/savedfile.txt");
String[] saveText = String.valueOf(messages.getText()).split(System.getProperty("line.separator") );
messages.setText("");
Save (file, saveText);
String message = messages.getText().toString();
writeLine("Saved ski #: " + message);
Instead of using "/savedfile.txt" you need a variable for the name.
File file = new File(path + "/" + message + ".txt")
Collect your input from the user. Check if it has a number, if it does then you store the filename as the numbers else you store the file name as message.txt. You need to add the smarts so that if user enters "hello123" you treat that accordingly.
I get above mentioned error on some devices (very rarely, 2 times until now only):
android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 3850)
at android.database.sqlite.SQLiteConnection.nativeExecuteForString(Native Method)
at android.database.sqlite.SQLiteConnection.executeForString(SQLiteConnection.java:679)
at android.database.sqlite.SQLiteConnection.setJournalMode(SQLiteConnection.java:361)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:236)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:200)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:806)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:791)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
at ***.a(***.java:115)
The line 115 in my code is following:
// here the exception occurs
SQLiteDatabase db = SQLiteDatabase.openDatabase(pathApp, null, SQLiteDatabase.OPEN_READONLY);
// ...
db.close();
Story
What I do is following:
app has root rights
it copies the database from another app into it's own directory
it tries to open this database and read some data
it close the file again
That's all. This is working on thousands of devices. My app for sure only accesses the database at on place, I'm completely sure about that
Question
Does anyone know what could cause this problem?
Maybe interesting facts
the 2 devices are a OnePlus2
one guy told me that the problem occurred after updating to Oxygen 2.1
I came across a similar problem recently where my app runs fine on all Android devices except the OnePlus Two running OxygenOS 2.1.
After looking into the issue, it appears this specific combination is very sensitive and crashes on a database lock of some sort. In my case, my app was copying a new database to the device on the first run then checking the database version to see if it requires an update. On the check it crashed on these devices. I realized in my code I am opening a separate instance to the database instead of using the currently opened database when checking the version. After changing the code to avoid opening a separate instance of the database, the app stopped crashing on the OnePlus.
My advice is to Not open multiple instances of the database in your app or try closing the database first before opening it again (perhaps your copy method didn't close the database or is using another instance of the same database).
Just encountered this in development. It triggered when the phone ran out of batteries and shut down. Presumably, you just have to be grabbing the database lock when that happens.
Here's the working solution (already tested in production for 2 months now or so) and this works on the OnePlus2 and on Android 6 as well:
add the sqlite binaries to your app (can be found here: http://forum.xda-developers.com/showthread.php?t=2730422). Just put them into your apps assets folder
Then in your app, try to read an external database like following:
Find out which binary is working. I tried a few methods (via calling /proc/cpuinfofor example) but could not find a reliable solution, so I do it via trial and error like following:
First I copy the binary from my assets folder to my apps files folder
Then I try to dump the desired database via <path_to_SQLite3_bnary> <path_to_database> \.dump '<tablename>'\n and read the result
I check the result, if it contains error: I know the binary is not working, if it starts with INSERT INTO I know it works
I repeat this with my binaries until I found a working binary
Then I just read the result. The result will contain an error or be some csv similar content. I either handle the error or convert every line to valid csv via row.replace("INSERT INTO \"" + tablename + "\" VALUES(", "").replace(");", ""), so that I get the content in a csv format. I use opencsv's CSVReader to parse the data then...
That's it, this works (until know) on all devices without a problem
Code - copied from my sources, adopt it a little bit to fit your needs
public static List<String> readCSVData(String pathDatabase, String tableName) throws InterruptedException, TimeoutException, RootDeniedException, IOException
{
List<String> res = null;
List<String> csvData = null;
final String[] architectures = new String[]{
"armv7",
"armv7-pie",
"armv6",
"armv6-nofpu"
};
for (int i = 0; i < architectures.length; i++)
{
res = readDatabase(architectures[i], pathDatabase, tableName, rootMethod);
if (res.toString().contains("[error:"))
{
L.d(RootNetworkUtil.class, "Trial and Error - ERROR: " + architectures[i]);
}
else
{
int maxLength = (res.toString().length() < 100) ? res.toString().length() : 100;
L.d(RootNetworkUtil.class, "Trial and Error - RESULT: " + res.toString().substring(0, maxLength));
L.d(RootNetworkUtil.class, "Architecture found via trial and error: " + architectures[i]);
csvData = res;
break;
}
}
return csvData;
}
private static List<String> readDatabase(String architecture, String pathDB, String tablename) throws InterruptedException, TimeoutException, RootDeniedException, IOException {
String sqlite = "sqlite3." + architecture;
String pathSQLite3 = getSQLitePath(architecture, tablename);
// OWN class, just copy the file from the assets to the sqlite3 path!!!
AssetUtil.copyAsset(sqlite, pathSQLite3);
String[] cmd = new String[]{
"su\n",
//"chown root.root " + pathSQLite3 + "\n",
"chmod 777 " + pathSQLite3 + "\n",
pathSQLite3 + " " + pathDB + " \".dump '" + tablename + "'\"\n"
};
List<String> res = new ArrayList<>();
List<String> temp = RootUtils.execute(cmd);
for (int i = 0; i < temp.size(); i++)
{
// Fehlerzeilen behalten!!!
if (temp.get(i).contains("error:"))
res.add(temp.get(i));
else if (temp.get(i).startsWith("INSERT INTO \"" + tablename + "\""))
res.add(temp.get(i).replace("INSERT INTO \"" + tablename + "\" VALUES(", "").replace(");", ""));
}
return res;
}
public static String getSQLitePath(String architecture, String addon)
{
String sqlite = "sqlite3." + architecture;
String pathSQLite3 = "/data/data/" + MainApp.get().getPackageName() + "/files/" + sqlite + addon;
return pathSQLite3;
}
The app records the sensor data and write the data into a .txt file into the phone SD card.
During the data collection process, one may press the stop button anytime to stop writing.
The relevant writing part is as follows:
myFile = new File("/sdcard/ResearchData/"
+ txtData.getText() + ".txt");
myFile.createNewFile();
fOut = new FileOutputStream(myFile);
myOutWriter = new OutputStreamWriter(fOut);
myBufferedWriter = new BufferedWriter(myOutWriter);
myPrintWriter = new PrintWriter(myBufferedWriter);
myPrintWriter.write(currentTime - startTime + " " + acceleration[0]
+ " " + acceleration[1] + " " + acceleration[2] + "\n");
Once the STOP button is pressed, the following codes are executed.
myOutWriter.close();
fOut.close();
The codes are functioning okay, but the only problem is that because one may press STOP anytime, so the last line of the .txt file is always INCOMPLETE. Due to the need for data analysis, I need the data to be a nice rectangular matrix. Thus, I need to remove the last incomplete line. How may I do it?
Or solve this question from another angle. Can I make the STOP button synchronous to the writing process? i.e., after one line has been completely written in, then the STOP is checked.
You need a variable of bool type which is initially false and let's call it isStopPressed.
In button's down event, you should assign it to true.
I assume you create your file inside a top function, and modify this file inside onSensorChanged() function.
Then, after this line
myPrintWriter.write(currentTime - startTime + " " + acceleration[0]
+ " " + acceleration[1] + " " + acceleration[2] + "\n");
you need a conditional like,
if(isStopPressed) {
mSensorManager.unregisterListener([Your Sensor Listener]);
myOutWriter.close();
fOut.close();
}
This will provide you the most comprehensive, valid interval for your needs.
You may try setting up a boolean flag, like isStopped to indicate the STOP button is pressed.
When the STOP button is pressed, set the isStopped flag to true.
In your file writing part, check the isStopped flag. if it's true, exit the loop and close the file. Otherwise, the code can continue writing lines to the file.
I'm buiding an app that what it does is scan the root folder
and then it searches if there is any folder inside... the purpose of this is to save all the
text files routes in database..the database will contain the textfile name and the route.
this is part of the code:
private void seedData(int indent, File file) throws IOException {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++)
{
seedData(indent + 4, files[i]);
path+=files[i].getPath();
}
}
else{
db.execSQL("insert into "+TABLE+" (title, url) values ('"+
file.getName().substring(0, file.getName().length()-4)+"', '"+file.getPath()+"');");
}
}
but in normal Java I just create a File with the route and then send it to the method like
this: seedData(1, new File("/root")); .So my question is, how do I do this in Android? or to be more precise, how do I create a file that points to the root folder that is located in assets so it gets "scanned" by my code. I already tried seedData(1, new File("/assets/root")); but it just didnt work.
Any help would be much appreciated.
Note: No, I cannot save the paths manually since there is over 3k text files in all those subfolders.
The problem here is that the assets are not files. The Android equivalent of your above method would be something like this:
private void seedData(int indent, String path, AssetManager mgr)
throws IOException
{
String[] list = mgr.list(path);
if (list.length > 0) {
// path is a directory with something in it
for (String item : list) {
seedData(indent + 4, path + "/" + item, mgr);
}
} else {
// path is either an empty directory or a file
// unfortunately, Android makes it hard to distinguish
String name = path.substring(path.lastIndexOf('/') + 1);
db.execSQL("insert into "+TABLE+" (title, url) values ('"
+name.substring(0, name.length()-4)+"', '"
+path+"');");
}
}
You would call this from your Activity with
seedData(0, "root", getAssets());
For reference, the URI format for assets in the "root" folder is file:///android_asset/root/....