Is there a simpler way to do parsing in Java on Android? - android

I'm having difficulty performing what should be a simple task. Let's say I have a text file that states:
a = b
I want my program to read this file and output "b" whenever the user inputs "a". In Python or C++ I can accomplish this in around 7 lines or less.
However, I'm having difficulty finding a simple way to do this on Android. For example, one sample that I found here on SO had nearly 900 lines in 6 files.
Is there a simple way to parse a file and return a variable on Android that I am missing?

BufferedReader r = new BufferedReader(new FileReader(filename));
string s;
while(s = r.readLine()) {
//Oh hey, I got a line out of the file.
//In three lines of code.
//So what's all this Android-bashing about?
}

As long as you're happy with thea = b format used in properties files, you get 99% of the way to your goal with
Properties properties = new Properties();
try {
properties.load(new FileInputStream(filename));
} catch (IOException e) {
// the file is missing or is not in 'a = b' format
}
The, having got a variable key containing the string "a" from the user, the result of properties.getProperty ( key ) will equal "b" if the file contains the line a = b. I'm pretty sure you need more than that in C++ to load a map from a file and handle all the escaping and character encoding issues.
If the properties are held in a text file called mappings.properties in the assets folder of your Android project rather than in the user's file system, then you get at it like this:
final AssetManager am = getResources().getAssets();
final Properties properties = new Properties();
try {
properties.load( am.open("mappings.properties"));
} catch (IOException e) {
// the file is missing or is not in 'a = b' format
}
This next bit is borrowed from the android tutorial to show a toast message with 'b' in it if 'a' is entered in the edit box. Maybe here is where you get your line count from, as setting up a GUI with XML files and adding listeners in Java is fairly verbose compared with other languages. That's due to Java and XML syntax rather than the virtual machine.
final EditText edittext = (EditText) findViewById(R.id.edittext);
edittext.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)) {
// Perform action on key press
Toast.makeText(YourOuterViewClass.this,
properties.getProperty(edittext.getText().toString()),
Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});

You definitely don't need hundreds of lines of code to achieve this. It can be done in a few lines. I don't know what examples you were looking at but they were probably doing way more than what you are describing.

Related

How to read-write character devices (like /dev/ttyS0) in Android

I have little knowledge of Java and Android. What I am trying to do is to open /dev/ttyS0 in an Android App which should talk to the serial line, but I am getting lost.
My device is rooted, and from a command line I can "echo ...>/dev/ttyS0" and also read from it, but I get lost trying to do that in Java. For start, I can not find a method to open a file in simple read-write mode, without coping with buffers and other intricacies (clearly, I want unbuffered I/O).
I searched the Internet, but all examples refer to USB which is not available for me. Then I've found the UartDevice class, but it is a class to derive a proper implementation from...
I tried to use the File class, and attach to it both a Reader and a Writer class, but the compiler complains and, frankly, I am not sure it is the way to go. I would need a skeleton code to start from; I miss a simple TextFile class with unbuffered read() and write() methods to be used at the same time on the same open file!
Can someone point me in the right direction thanks?
After many tries, and with the help of much information from the SO site, I finally succeded in the task. Here is the code:
public class MainActivity
extends AppCompatActivity {
File serport;
private FileInputStream mSerR;
private FileOutputStream mSerW;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// let this program to access the serial port, and
// turn off the local echo. sudo() is a routine found here on S.O.
sudo("chmod a+rw /dev/ttyS0");
sudo("stty -echo </dev/ttyS0");
// open the file for read and write
serport = new File("/dev/ttyS0");
try {
mSerR = new FileInputStream(serport);
mSerW = new FileOutputStream(serport);
} catch (FileNotFoundException e) {}
// edLine is a textbox where to write a string and send to the port
final EditText edLine = (EditText) findViewById(R.id.edLine);
// edTerm is a multiline text box to show the dialog
final TextView edTerm = findViewById(R.id.edTerm);
// pressing Enter, the content of edLine is echoed and sent to the port
edLine.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
// Perform action on key press
String cmd = edLine.getText()+"\n";
edTerm.append(cmd);
byte[] obuf = cmd.getBytes();
try {
mSerW.write(obuf);
} catch (IOException e) {}
edLine.setText("");
// read the reply; some time must be granted to the server
// for replying
cmd = "";
int b=-1, tries=8;
while (tries>0) {
try {
b = mSerR.read();
} catch (IOException e) {}
if (b==-1) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {}
--tries;
} else {
tries=3; // allow more timeout (more brief)
if (b==10) break;
cmd = cmd + (char) b;
}
}
// append the received reply to the multiline control
edTerm.append(cmd+"\n");
return true;
}
return false;
}
});
}
}
Please note the presence of the sudo() command in the code: it is there to give r/w permissions to the ttyS0 file, and to disable its echo option. If those permissions+options are already right, or another mean to set them exists, then the sudo() command is not needed.
Note: I believe that the sudo() command implies that the device must be rooted.
All File access in Java is done via input and output streams. If you want to open a file, you simply create a FileOutputStream or FileInputStream for it. These are unbuffered streams. If you then want to write raw bytes you can wrap that in a ByteArrayOutputStream or ByteArrayInputStream.
To do character mode, you can use a Writer. An OutputStreamWriter with a charset of ascii can wrap the FileOutputStream. That should do the character conversion for you. Just don't use a FileWriter- while it seems like the right fit, it has no option to select a character set, and the default is not ascii. For reading in, use an InputStreamReader.

SharedPreferences in Android not persisted to disk when key contains newline

In Android, I'd like to write SharedPreferences key-value pairs where the keys are Base64 strings.
// get a SharedPreferences instance
SharedPreferences prefs = getSharedPreferences("some-name", Context.MODE_PRIVATE);
// generate the base64 key
String someKey = new String(Base64.encode("some-key".getBytes("UTF-8"), Base64.URL_SAFE), "UTF-8");
// write the value for the generated key
prefs.edit().putBoolean(someKey, true).commit();
In the last line, the call to commit returns true. So this key-value pair should have been saved successfully.
When I close and destroy the Activity where this piece of code was used and then re-create the Activity (running this code again), the specified value is returned for the key that we used.
But it turns out that, when I destroy the whole application/process (e.g. using "Force stop" in app settings), the value for our key is lost on the next launch of the Activity.
When I don't use Base64.URL_SAFE but Base64.URL_SAFE | Base64.NO_WRAP as the flags for the Base64 encoding, it works fine.
So this problem has been caused by the newlines at the end of the Base64 keys. Keys like abc can be written without any problems. But when the key is abc\n, it fails.
The problem is that it appears to work without problems first, returning true on commit() and returning the correct preference value on subsequent calls. But when the whole application is destroyed and re-started, the value has not been persisted.
Is this intended behaviour? A bug? Does the documentation say anything about valid key names?
I took a look at GrepCode and see that the operations will be the following (I do not mention useless ones) :
android.app.SharedPreferencesImpl.commit()
android.app.SharedPreferencesImpl.commitToMemory()
android.app.SharedPreferencesImpl.queueDiskWrite(MemoryCommitResult,Runnable)
3.1. XmlUtils.writeMapXml(Map, OutputStream)
3.2. XmlUtils.writeMapXml(Map, String, XmlSerializer)
3.3. XmlUtils.writeValueXml(Object v, String name, XmlSerializer ser)
First : how your data are converted ?
The method XmlUtils.writeValueXml writes the Object value in a XML tag with the attribute name set to the String value. This String value contains exactly the value you specified at the SharedPreference's name.
(And I confirmed this by doing a step-by-step debug with your piece of code).
The XML will be with an unescaped line break character. Actually, the XmlSerializer instance is a FastXmlSerializer instance and it does not escape the \n character (see the link for this class at the end if you want to read the source code)
Interesting piece of code :
writeValueXml(Object v, String name, XmlSerializer out) {
// -- "useless" code skipped
out.startTag(null, typeStr);
if (name != null) {
out.attribute(null, "name", name);
}
out.attribute(null, "value", v.toString());
out.endTag(null, typeStr);
// -- "useless" code skipped
}
Second : why the result is true ?
The commit method has the following code :
public boolean commit() {
MemoryCommitResult mcr = commitToMemory();
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}
So it returns the mcr.writeToDiskResult which is set in the SharedPreferencesImpl.writeToFile(MemoryCommitResult) method. Interesting piece of code :
writeToFile(MemoryCommitResult mcr) {
// -- "useless" code skipped
try {
FileOutputStream str = createFileOutputStream(mFile);
if (str == null) {
mcr.setDiskWriteResult(false);
return;
}
XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
FileUtils.sync(str);
str.close();
ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
try {
final StructStat stat = Libcore.os.stat(mFile.getPath());
synchronized (this) {
mStatTimestamp = stat.st_mtime;
mStatSize = stat.st_size;
}
} catch (ErrnoException e) {
// Do nothing
}
// Writing was successful, delete the backup file if there is one.
mBackupFile.delete();
mcr.setDiskWriteResult(true);
return;
} catch (XmlPullParserException e) {
Log.w(TAG, "writeToFile: Got exception:", e);
} catch (IOException e) {
Log.w(TAG, "writeToFile: Got exception:", e);
}
// -- "useless" code skipped
}
As we see at the previous point : the XML writing is "ok" (do not throw anything, do not fails), so the sync in the file will be too (just a copy of a Stream in another one, nothing checks the XML content here !).
Currently : your key was converted to (badly formatted) XML and correctly wrote in a File. The result of the whole operation is true as everything went OK. Your changes are comitted to the disk and in the memory.
Third and last : why do I get back the correct value the first time and a bad one the second time
Take a quick look to what happen when we commit the changes to memory in SharedPreferences.Editor.commitToMemory(...) method (interesting part only... :)):
for (Map.Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
if (v == this) { // magic value for a removal mutation
if (!mMap.containsKey(k)) {
continue;
}
mMap.remove(k);
} else {
boolean isSame = false;
if (mMap.containsKey(k)) {
Object existingValue = mMap.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
mMap.put(k, v);
}
mcr.changesMade = true;
if (hasListeners) {
mcr.keysModified.add(k);
}
}
Important point : the changes are commited to the mMap attribute.
Then, take a quick look to how we get back a value :
public boolean getBoolean(String key, boolean defValue) {
synchronized (this) {
awaitLoadedLocked();
Boolean v = (Boolean)mMap.get(key);
return v != null ? v : defValue;
}
}
We are taking back the key from mMap (no reading of the value in the file for now). So we have the correct value for this time :)
When you reload your application, you will load the data back from the disk, and so the SharedPreferencesImpl constructor will be called, and it will call the SharedPreferencesImpl.loadFromDiskLocked() method. This method will read the file content and load it in the mMap attribute (I let you see the code by yourself, link provided at the end).
A step-by-step debug shown me that the abc\n was written as abc (with a whitespace character). So, when you will try to get it back, you will never succeed.
To finish, thank you to #CommonsWare to give me a hint about the file content in the comment :)
Links
XmlUtils
FastXmlSerializer
SharedPreferencesImpl
SharedPreferencesImpl.EditorImpl.commit()
SharedPreferencesImpl.EditorImpl.commitToMemory()
SharedPreferencesImpl.enqueueDiskWrite(MemoryCommitResult, Runnable)
SharedPreferencesImpl.writeToFile(MemoryCommitResult)
SharedPreferencesImpl.loadFromDiskLocked()

Getting ProtoBuf.ProtoException using OsmSharp in Xamarin Android

I am trying to test a sample project called Android.Routing.Offline from OsmSharp.Samples in Github.
After two taps on the screen (the first one gets just the GeoCoordinate) I get a ProtoBuf.ProtoException in the Router.cs
private static IBasicRouterDataSource<CHEdgeData> _graph;
public static void Initialize()
{
var routingSerializer = new CHEdgeDataDataSourceSerializer();
_graph = routingSerializer.Deserialize(
Assembly.GetExecutingAssembly().GetManifestResourceStream(#"Android.Routing.Offline.kempen-big.contracted.mobile.routing"));
}
public static Route Calculate(GeoCoordinate from, GeoCoordinate to)
{
try
{
lock(_graph)
{
var router = Router.CreateCHFrom(_graph, new CHRouter(), new OsmRoutingInterpreter());
// The exception happens here below
var fromResolved = router.Resolve(Vehicle.Car, from);
var toResolved = router.Resolve(Vehicle.Car, to);
if(fromResolved != null && toResolved !=null)
{
return router.Calculate(Vehicle.Car, fromResolved, toResolved);
}
}
}
catch(Exception ex)
{
OsmSharp.Logging.Log.TraceEvent("Router", OsmSharp.Logging.TraceEventType.Critical, "Unhandled exception occured: {0}", ex.ToString());
}
return null;
}
And the exception:
> {ProtoBuf.ProtoException: Invalid wire-type; this usually means you
> have over-written a file without truncating or setting the length; see
> http://stackoverflow.com/q/2152978/23354 at
> ProtoBuf.ProtoReader.ReadSingle () ...
I didnt overwrite the file (kempen-big.contracted.mobile.routing) just added it as a linked file in the project. Any ideas how I can solve this issue?
Well, the first thing to try is to check that the contents of the Stream you are reading (via GetManifestResourceStream) contains exactly the contents you are expecting, and not some wrapper or otherwise-corrupt mess. If you have some checksum algorithm you can run: great! Checking just the .Length would be a great start. Otherwise, you could cheat (just for the purposes of validating the contents) by getting the hex:
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
string hex = BitConverter.ToString(
ms.GetBuffer(), 0, (int)ms.Length);
// dump this string, and compare it to the same output run on the
// oringal file; they should be identical
}
Note that this duplicates the contents in-memory, purely so we can get a byte[] (oversized) to get the hex from - it isn't intended for "real" code, but until you are sure that the contents are correct, all other bets are off. I strongly suspect that you'll find that the contents are not identical to the contents in the original file. Note that I'm also implicitly assuming that the original file works fine in terms of deserialization. If the original file doesn't work: again, all bets are off.

android separating a text file

I've been researching about how diablo 2 dynamically generates loot, and I thought it'd be fun to create a fun app that will randomly generate items using this system.
I currently have code which I believe should read the entire txt file, but it's not parsed.
It looks like:
private void itemGenerator() {
int ch;
StringBuffer strContent = new StringBuffer("");
InputStream fs = getResources().openRawResource(R.raw.treasureclass);
// read file until end and put into strContent
try {
while((ch = fs.read()) != -1){
strContent.append((char)ch);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
An example in the text file would look something like:
Treasure Class Item1 Item2 Item3
tc:armo3 Quilted_Armor Buckler Leather_Armor
tc:armo60a Embossed_Plate Sun_Spirit Fury_Visor
tc:armo60b Sacred_Rondache Mage_Plate Diadem
So what I'm thinking right now is putting each row into an array with StringTokenizer delimited by \n to get each row. Then somehow do it again with tab-delimited for each item in the array and put it into a 2D array?
I haven't coded it yet because I think there's a better way to implement this that I haven't been able to find, and was hoping for some helpful input on the matter.
For anyone actually interested in knowing how the item generation works, their wiki page, http://diablo2.diablowiki.net/Item_Generation_Tutorial, goes very in-depth!
I think you are facing problem in distinguishing between each lines that are read-out from file. In order to read the file line-by-line you should change your code as below:
InputStream fs = getResources().openRawResource(R.raw.treasureclass);
BufferedReader br = new BufferedReader(new InputStreamReader(fs));
String line = null;
while((line = br.readLine()) != null){
Log.i("line", line);
//split the content of 'line' and save them in your desired way
}

Android: Trying to read a word in another language other than english

I have a scanner which uses the method s.next(); (it reads a whole word separates by " ")
it works fine with English letters and words, but when I write something in Hebrew, it returns "??" instead.
My Phone does have the Hebrew language, all other strings in my app are in Hebrew, only what the scanner reads returns ?????.
Any Help?
Thanks :)
The scanner code: (it's part of an asynctask)
#Override
protected Void doInBackground(CharSequence... txt) {
Scanner s = new Scanner(getResources().openRawResource(R.raw.data));
try {
boolean add;
do {
add = false;
ListItem temp = new ListItem();
temp.data[ListItem.NAME] = s.next();
Log.i("AsyncTask", temp.data[ListItem.NAME]);
if (temp.data[ListItem.NAME].contains(txt[0])) {
Log.i("match", "found");
add = true;
}
temp.data[ListItem.MAKAT] = s.next();
Log.i("AsyncTask", temp.data[ListItem.MAKAT]);
if (temp.data[ListItem.MAKAT].contains(txt[0])) {
add = true;
}
temp.data[ListItem.JHAZIT] = s.next();
temp.data[ListItem.KOMA] = s.next();
temp.data[ListItem.MESPAR] = s.next();
if (add) {
this.publishProgress(temp);
}
}
while (s.hasNext());
}
finally {
s.close();
}
return null;
}
And the file i take out the info is named data.txt :
Barrel 4315124 2 3 43
car 414124 2 4 41
Red_box 4315124 1 2 77
Glass_cup 4515124 4 7 10
Iron_cup 4515124 4 7 9
Gabi_Noob 5717217 7 8 9
מכסה_גומי 46245897235 8 9 10
Well, Actually using the UTF-8 encoding like it was mention in the comments worked, only i had to create the data.txt using a notepad outside of eclipse and save it with UTF-8 encoding instead of the ANSI(default) than i simply imported it inside the eclipse where the raw folder, clicked run and it worked. MAGIC.
i guess my mistake was to create the data.txt from inside eclipse.

Categories

Resources