Showing posts with label XML. Show all posts
Showing posts with label XML. Show all posts

06 October, 2015

Some points on the Android UI

Android is a great OS - there's no doubt about that, even if you measure that statement using the number of active installations.  It has an interesting history, starting from plans to create an OS for digital cameras and ending up being Google's core mobile platform running on around a billion devices.  It is technically well designed, open source and very adaptable; from CPU architectures to screen sizes, Android can adapt.

Progress of Android over the years (Ars Technica)


As Android progressed to meet the expected standards of the day, the general UI got more minimalist while more colours were introduced (older versions looked darker).  Despite the move towards a more modern UI in general, it is still possible for application developers to apply their own style.  A typical result of this support was that developers of older applications did not bother updating their styles to the latest version (this is basically an XML file).

What we ended up with is a FIAT 127 in 2015's motorshow.
Not quite in the same league
The problem with this situation is that not only we have to sometimes use outdated applications, but Google is also pushing a new 'UI language'.  There is nothing wrong in having a new UI language...except when few developers are following it, and you're not one of them.  If Android is to have a uniform, clean and modern UI, there should be a mechanism which automates the transition of styles to the latest standard in cases where the default file was left lying around.  Automation is not uncommon on the Android ecosystem - Code is checked for potential errors, style files must be up to standard, even copyright issues are flagged by a bot - so why not a simple style file?
What's the deal with this UI (SIM Tool Kit)?


As I mentioned earlier, there is also another problem which Google does not seem to want to fix - Material Design.  Consistency is key in product branding and Google is/was known for their efforts in this regard.  The ubiquitous bar in all their web products and their logo in the exact same location made it clear that this is a Google product.


Nowadays, their Android apps are cacophony of UI element styles and whatnot.  Despite their efforts to make Material Design the next standard, it's already been 2 years and I have no idea when this next will be now.  It can be seen in some apps, such as the settings, and the major Google apps.  However, applications such as the Google Analytics still sport the Android 4.3 UI.  Even worse is the app for Blogger - with UI probably designed by the Romans.

Yet again, even though the apps follow the general material design, all of them seem to have a language of their own.  One aspect which was recently highlighted was the lack of consistent scrollbars.  Now we got a new scrollbar in the application launcher too, for diversity.

Google Calender app has to be one of my favourites.  It's fast, visually appealing and above all, useful.  I use it regularly to set appointments, reminders, etc. just like all other users.  The problem with the whole Calendar ecosystem is the web version.  Why has Google introduced the Material Design, implemented it correctly in Calender on Android yet left the web version in the dark while at the same time it developed the Inbox service with correct material guidelines on both Android and the web (I'm not discussing whether Inbox is practical or not)?  I understand the drive towards mobile and I truly appreciate the improved UI on mobile, but I'm not in favour of sheer inconsistency (and then again, there are web versions better than their apps).

Yes this was quite a rant - not really helpful for many.  But it gets frustrating when you're working on your services and try to follow as many guidelines as possible to make your users happy.  Thankfully apps are not accepted or rejected based on their looks and interaction, although sometimes I do favour such a system as it does improve the users' mobile experience.

29 September, 2015

Intelligent Input Processing

Hello again after quite a long time!  Quite a lot has happened over the past few years, from starting and graduating from University, finding a job in another company and travelling around the world.  I have also been involved in some small side projects, some of which have actually gained some traction.

One such project which I did some basic parts of is a a system for maintaining a list of classified vehicle listings for used (2nd hand) and new cars...or any other kind vehicle.  The interesting part is the way a new listing can be added with minimal effort.  Typically people *hate* filling long forms, and entering the details of a car is no exception.  Even if the form is actively trying to help the user by showing suggestions, it still is not as streamlined as just typing text or actually filling out a hand-written form.

So here I found the thing I hated about forms - having to switch between inputs and having to think about the value I should enter next.  I don't want to think about it, I just want my form to fill up, by itself if possible.  A little experiment lead to the development of a small but quite helpful feature which tries to offload this (minor) effort from the user.

As the data was a simple table, we could predict what the user is going to enter; some details on a car.  There aren't too many attributes when you think about it - a make and model, year, colour, engine and transmission.  You could also do away with some of them, even though it might hurt search rankings.  The result was that a form may be populated by simply parsing the semi-natural text taken from one big text box.  The user no longer had to enter different and specific values in each entry - just one descriptive sentence where order does not matter and mistakes may be easily fixed.  All we needed is some initial data to "teach" the system what to expect.

We now have a very clean solution for entering listing details where a short description relieves us from having to think about each individual attribute of a car (what an epic case of 1st world problemism).

By some luck, this little experiment has made it through to a website and is now available for many users to enjoy.  Let's hope these kinds of little experiments from professionals and enthusiasts keep getting better and more popular so one day we may have a more user-oriented internet, rather than a machine-oriented one.  Oh by the way, the website making use of this little tech is http://www.pickacarmalta.com!

16 December, 2011

Comparing two XML documents in Java

How many times have you, for example, modified some XML file but wanted to make sure that only certain parts were changed?  Well it did occurred to me, a number of times.  Actually the problem is this:  I had a small application which modifies XML files in a graphical way and I wanted to ensure that nothing got messed up when I saved the file.  By "messed up" I mean tags changing their values or positions.

So what I did was build a simple program which will take in two files (XML, naturally) and then map all the nodes and compare their paths and values.  Quite a simple task.


So first, we'll start off with the usual Utilities class.  Static methods such as those reading a file should be placed here, ideally.


package com.jf.xmlcomp.util;




import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Scanner;


import javax.xml.parsers.DocumentBuilderFactory;


import org.w3c.dom.Document;


public class Utils
{
public static String readStringFromFile(File file)
{
StringBuilder ds_text = new StringBuilder();
String str_sep = System.getProperty("line.separator");
try
{
Scanner ds_scan = new Scanner(new FileInputStream(file), "UTF-8");
while (ds_scan.hasNextLine())
ds_text.append(ds_scan.nextLine() + str_sep);
ds_scan.close();
}
catch (Exception e) 
{
System.err.println(file.getPath() + " :: Not read");
}

return ds_text.toString();
}

public static Document stringToDocument(String str_doc)
{
Document ds_doc = null;
try
{
ds_doc = DocumentBuilderFactory
.newInstance()
.newDocumentBuilder()
.parse(new ByteArrayInputStream(str_doc.getBytes()));
}
catch (Exception e) 
{
System.err.println("String could not be converted to Document");
}

return ds_doc;
}
}


We only have two simple methods here; one which reads in the contents of a file and one which converts an XML String to a W3C Document object.  I won't be detailing much about reading a file or converting a String as it is not the scope of this post.

Next thing to do is create a very simple implementation of a Map, sort of.  Now those that actually know what a Map is will be frothing at their mouths, since what we will be doing is a simple ArrayList mapping and we won't even be using Generics, hashes or even collision tables.  The reason is that this is a simple post about creating a simple XML Comparator.

So, to the SimpleMap thing...


package com.jf.xmlcomp.util;


import java.util.ArrayList;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;


public class SimpleMap
{
private ArrayList<String> keys;
private ArrayList<String> values;


public SimpleMap()
{
keys = new ArrayList<String>();
values = new ArrayList<String>();
}


public void put(String s, String v)
{
keys.add(s);
values.add(v);
}


public ArrayList<String> get(String k)
{
ArrayList<String> result = new ArrayList<String>();
for (int c = 0; c < keys.size(); c++)
if (keys.get(c).equals(k)) result.add(values.get(c));


return result;
}


As you can see, we created the usual methods you'd find in a Map: the put and get.  The rest should be pretty easy; declaring the ArrayLists containing the keys and values and then initialising them in the constructor.

The put method will simply add the new key and value Strings in the respective ArrayList while get, gets a little over-done.  In the get method we first set up a results ArrayList and then loop through the keys list and store every matching key.  The key won't be stored though, as they are of no use really; if you ask for door which is opened by a particular key, you will want to find a door.  So we get the index at which the key was found and then store the value found at the same index in the values list in the results list.  Finally the results list is returned.  Probably you now know that we can have the same key appearing more than once in this map.


public ArrayList<String> keys()
{
return keys;
}

public ArrayList<String> getCompleteItemList()
{
ArrayList<String> res = new ArrayList<String>();
for (String key : uniqueKeys())
for (String val : get(key))
res.add(key + " :: " + val);
return res;
}

Following the mapping details are two other simple methods.  One simply returns the list of keys while the other will return all the items we have as a String of key :: value.  At first it might look like an over kill.  Getting only the unique keys only to loop over the values obtained by every key.  What happens if you use the normal key list and loop over it, is a repetitive amount of values.

Let's say there are two keys named "a" and "b".  If we loop over the key list we get "a", "a", "b".  Now let's include the part which gets the values obtained from each key.  We will get two values for each key named "a", so eventually we'll end up with the values of a key named "a", twice.

So to "fix" this, we will simple get each key once and then simply add the resulting values of each.  That way we can be totally sure that no key-value pair is repeated.

Naturally we'll be needing the uniqueKeys method, so here it comes.




public ArrayList<String> uniqueKeys()

{
Set<String> s = new TreeSet<String>(new Comparator<String>()
{
@Override
public int compare(String o1, String o2)
{
if (o1.equals(o2))
return 0;
return 1;
}
});
s.addAll(keys);

ArrayList<String> result = new ArrayList<String>();
result.addAll(s);

return result;
}


This method will create a Set (which will not accept duplicates) and give it a String Comparator.  The compare method is overridden so basically you will have to write it ("you", as in "the programmer").  As these are Strings we are comparing, the equals method is needed (Don't even think about using "==" when comparing Strings.  Really, don't.)  Next, the key list is added to the set and it will automatically store each value once, so no duplicates.  Finally a new ArrayList is declared and the new modified set of unique keys are added to the list, and then it's returned.

This is a rather quick and dirty way to get unique values from a List object.  This is basically our new Map, which should be used only in this project.  And nowhere else.


package com.jf.xmlcomp;


import java.io.File;
import java.util.ArrayList;


import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;


import com.jf.xmlcomp.util.SimpleMap;
import com.jf.xmlcomp.util.Utils;


public class XMLValueComp
{
private File xfile1;
private File xfile2;

private SimpleMap f1v;
private SimpleMap f2v;

public static void main(String[] args)
{
if (args.length < 2)
printHelp();

XMLValueComp xmlComp = new XMLValueComp(args[0], args[1]);

xmlComp.compare();
}

public static void printHelp()
{
System.out.println("Usage: xmlcomp file1 file2");
System.exit(0);
}

This is the main class for this project.  There are the two files which shall be compared along with the SimpleMaps for each of them.  The main class will accept two arguments; the two files.  If not enough arguments are passed, a help message is printed out, and the program exits.


public XMLValueComp(String file1, String file2)
{
f1v = new SimpleMap();
f2v = new SimpleMap();
File f1 = new File(file1);
File f2 = new File(file2);

if (!f1.exists() || !f2.exists())
{
System.out.println("One of the files does not exist!");
System.exit(0);
}
xfile1 = f1;
xfile2 = f2;
}


Now we have the class constructor.  Doesn't do much except for initialisation and ensuring we are not comparing nothingness.  If the files do exist, then we will initilise them.  If they don't, the application will exit.


public void compare()
{
Document doc1 = Utils.stringToDocument(
Utils.readStringFromFile(xfile1));

Document doc2 = Utils.stringToDocument(
Utils.readStringFromFile(xfile2));

if (doc1 == null || doc2 == null)
{
System.err.println("Could not build docs");
System.exit(0);
}

buildMap(f1v, doc1);
buildMap(f2v, doc2);


ArrayList<String> uniques = new ArrayList<String>();


uniques.addAll(f1v.getCompleteItemList());
uniques.removeAll(f2v.getCompleteItemList());


System.out.println("Unique to F1");
for (String v : uniques)
System.out.println(v);




uniques = new ArrayList<String>();


uniques.addAll(f2v.getCompleteItemList());
uniques.removeAll(f1v.getCompleteItemList());

System.out.println("\n\nUnique to F2");
for (String v : uniques)
System.out.println(v);
}

That the core of the application, where we shall be dealing with the real comparator part.  So first off, the documents are created from the String we got from the files.  After that, we shall perform the usual checking to make sure we don't have corrupted files, for example, in which case we'll report the problem and stop.  Then the Maps for each Document are built and a list of unique pairs is created.

The duplicates from each document are removed by filling the unique list with the values from one document and then removing the ones which are found in the other document.  It is a quick way to remove the common nodes and getting only the ones that don't appear in both documents.  The same process is repeated for both documents, so eventually a list of unique items in document one and document two are printed separately.  If you compare a file with itself you can easily deduce that all the common nodes will be removed and the list of unique values will always be empty.


private void buildMap(SimpleMap map, Document doc)
{
Element e = doc.getDocumentElement();


mapNodes(map, e, "/" + e.getNodeName());
}


private void mapNodes(SimpleMap map, Element elem, String parent)
{
for (int x = 0; x < elem.getChildNodes().getLength(); x++)
{
Node n = elem.getChildNodes().item(x);
String path = parent + "/" + n.getNodeName(); 

if (n.getChildNodes().getLength() == 1)
map.put(path, n.getTextContent());

if (n.getNodeType() == Node.ELEMENT_NODE)
mapNodes(map, (Element) n, path);
}
}


The buildMap method is simply preparing some stuff for the mapNodes method.  The document element is passed as the root element to the mapNodes so that it can use recursion to obtain all nodes.

The mapNodes will loop the current child nodes and store the value and path.  Every time an Element Node is encountered it will call itself and the children of that node are stored.  That will go on until all the nodes in the document have been traversed.

You can now try it out by running it and passing in two paths that point to XML documents.  You should try it with the same document to ensure that it actually does not detect any differences.  Then you can go on to see if a modified XML document has kept its integrity :)

As usual, the full code is available here.