Adding Preferences On-The-Fly

(Make appropriate whooshing sounds)

Today, I will show you how to add preferences to your SharedPreferences (confused yet?) programatically. In an app that I am doing, I wanted to present a ListPreference to the user from the SharedPreferences page. However, the values contained in this list is not known at compile time. (Think Facebook groups that differ per user, or Twitter lists, or things like such, and I want to set a default option.)

Anyway, here’s what I did to make this work. I added a preference in my PreferenceScreen, a sort of placeholder, if you will. Here is my ListPreference:
<pre class="brush:xml"><PreferenceScreen xmlns:android=”http://schemas.android.com/apk/res/android” >

<PreferenceCategory android:title=”@string/pref_settings_header”>
<ListPreference android:key=”@string/pref_key_default_group” android:title=”@string/pref_default_group”/>
</PreferenceCategory>

</PreferenceScreen></pre>And then in the class that handles the SharedPreferences, I get the values that I want displayed and plug them in to the placeholder preference. I will not bother to explain here, just heavily commenting the code:
<pre class="brush:java">public class UserPreferences extends PreferenceActivity {

private static final String LOG_TAG = “UserPreferences”;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.my_apps_prefs);

// Manage the “Default Group” preference
addDefaultGroupPrefs();
}


private void addDefaultGroupPrefs() {
// Get our “placeholder” preference. I prefer to use strings instead of hard-coding the preference key.
// The value passed into findPreference is the value in the android:key parameter in the preference XML file.
ListPreference groupsListPrefs = (ListPreference) findPreference(getString(R.string.pref_key_default_group));

// Get your groups, this may come from a utility class or wherever
// In this example, I have a custom object that has a “name” and an “id”
List<CustomGroup> myGroups = getMyGroups();
if (myGroups == null) {
// Disable the preference if we don’t have a list
groupsListPrefs.setEnabled(false);
} else {
// Enable the preference if we have a list
groupsListPrefs.setEnabled(true);

// We split the list into names and IDs
// We show the names in the list, and the values as the entries
List<String> groupNames = new ArrayList<String>();
List<String> groupIds = new ArrayList<String>();
for (CustomGroup group : myGroups){
groupIds.add(group.getId());
groupNames.add(group.getName());

// I guess you can add directly to a String[]
// just make sure the order is preserved (name1=id1, name2=id2, etc)
}

// These should be user-readable strings
groupsListPrefs.setEntries(groupNames.toArray(new String[groupNames.size()]));

// These are the actual values to be saved in the XML file
groupsListPrefs.setEntryValues(groupIds.toArray(new String[groupIds.size()]));
}
}

private List<customgroup> getMyGroups(){
// do work here, query server or db or whatever
// return the list of groups
}
}</pre>
And we’re done. :)

1 min read

Inspecting your Shared Preferences

Did you know that you can look at your SharedPreferences file?

If during development you want to inspect what your SharedPreferences now contain, you can pull a copy of the XML file from the emulator.

In Eclipse, you can do that by following these steps:

<ol><li>Open the DDMS perspective (Window > Open Perspective > Other > DDMS).</li><li>Click on the File Explorer tab and navigate to your app’s data folder (that’s under data/data/<your package name>/shared_prefs/).</li><li>Choose the file you want to download.</li><li>Click on the Pull file from device button.</li></ol><div class="separator" style="clear: both; text-align: center;"></div><div>
</div>

~1 min read

Eclipse-ception

Guys!! Eclipse just went all Inception on me!
<div class="separator" style="clear: both; text-align: center;"></div>

~1 min read

Adding a float value to your resources

Earlier today, I was trying to figure out how to add a float value to constants file. What I used to do was add a string value in my strings.xml, retrieve the string value, and convert it to float. <pre class="brush:java">float floatFromFile = Float.valueOf(getResources().getString(R.string.my_float));</pre> I was trying out something new but it wasn’t working, so I decided to look for a more accepted solution. Google led me to this StackOverflow question. I was on the right track after all! I think the accepted answer is incomplete, or not clear enough for my purposes. I ended up having this entry in my dimensions.xml file: <pre class="brush:xml"><item name="face_feature_marker_size" type="vals" format="float">2.0</item></pre> And then in my code, I retrieve the value as: <pre class="brush:java">TypedValue tempVal = new TypedValue();
getResources().getValue(R.vals.face_feature_marker_size, tempVal, true);
float markerSize = testVal.getFloat();</pre> I ended up having more lines of code with no idea if this is more optimized. Let me know what you think!

~1 min read

Quick Tip: git cloning

A user-friendly way of cloning a git repo is through the eGit plug-in in Eclipse. But sometimes, especially on Windows machines, Eclipse has trouble cleaning up after itself after completing a clone operation. The best workaround for this is to clone the repo from git bash and then import the repo in Eclipse.
<pre class="brush:bash">default@ZDOMINGUEZ-T420 ~
$ git clone git@github.com:<your git repo> <local folder to check out to></pre>When git finishes cloning your repo, import it to Eclipse.
<div class="separator" style="clear: both; text-align: center;"></div>Browse to the folder you checked out to, click OK, and the newly-cloned repo should now appear on the Git Repositories view.

~1 min read

Selenium and File Downloads

Lately, one of my tasks has been to automate regression tests on one of our apps. Since this is a web app, we are using Selenium. Here, I enumerate the steps to configure Firefox for file downloading using Selenium and JUnit by foregoing the downloads dialog box.

Setting up the profile
We will create a new profile to be used for testing. A profile is simply a set of configuration that Firefox will use when you start it. You can use your existing profile as well, but I opted to create a new one so that the Firefox instance for testing will be as pristine as possible (i.e. no unnecessary plugins, no bookmarks, etc). A note: I am working on a Windows machine.
<ol><li>Bring up the command prompt and start the profile chooser by typing in firefox.exe -ProfileManager -no-remote</li><li>Click on Create Profile.</li><li>Enter the profile name you wish to use. I suggest you include the name of your project instead of just naming it “Test”.</li><li>Click on Choose Folder and navigate to the folder where you wish to store the profile files. Make sure you can access that folder easily; write the path down because you will need this in JUnit.</li><li>Click on Finish.</li><li>You will be brought back to the profile chooser dialog. Click on the Don't ask at start-up checkbox.</li></ol><div>Note: To make Firefox use your original configurations when you are browsing, bring up the profile chooser dialog, choose the profile named default, and start Firefox.</div><div>
</div><div>Configuring Firefox</div><div>Now we will configure Firefox to behave like how we want it to.
<ol><li>From the profile chooser dialog, highlight your test profile and click the Start Firefox button.</li><li>(Optional if Firefox is not your default browser) Uncheck "Always perform this check when starting Firefox".</li><li>Click on Tools > Options. We will go through the steps for each tab in the Options dialog box.</li><li>General tab</li><ul><li>Under Startup, choose Show a blank page from the When Firefox starts dropdown.</li><li>Under Downloads, uncheck the Show the Downloads window when downloading a file checkbox.</li><li>Still under Downloads, click on Browse for the Save files to option. Navigate to the folder where you want Firefox to put downloaded files. Take note of this folder as well because we will use this in JUnit.</li></ul><li>Tabs tab</li><ul><li>Uncheck Open new windows in a new tab instead and uncheck all warnings.</li></ul><li>Content tab</li><ul><li>Uncheck Block pop-up windows.</li></ul><li>(Optional) Privacy tab</li><ul><li>Under History, choose Never remember history from the Firefox will: dropdown.</li></ul><li>Advanced tab</li><ul><li>Under the General sub-tab, uncheck Use autoscrolling and uncheck Always check to see if Firefox is the default browser on startup.</li><li>If you will be using a proxy server, choose the Network sub-tab and input your proxy settings there.</li></ul></ol><div>Adding MIME Types</div></div><div>Now we need to tell Firefox how to handle downloading each specific type of file.</div><div><ol><li>Navigate to the folder where you saved your custom profile and open the mimeTypes.rdf file in a text editor.</li><li>Add the following lines towards the end of the file, right above the closing </RDF:RDF> tag.</li><pre class="brush:xml"><RDF:Seq RDF:about=”urn:mimetypes:root”>
<RDF:li RDF:resource=”urn:mimetype:text/plain”/>
</RDF:Seq>

<RDF:Description RDF:about=”urn:mimetype:handler:text/plain” NC:alwaysAsk=”false” NC:saveToDisk=”true”>
<NC:externalApplication RDF:resource=”urn:mimetype:externalApplication:text/plain”/>
</RDF:Description>

<RDF:Description RDF:about=”urn:mimetype:text/plain” NC:value=”text/plain” NC:editable=”true” NC:fileExtensions=”txt” NC:description=”Text Document”>
<NC:handlerProp RDF:resource=”urn:mimetype:handler:text/plain”/>
</RDF:Description></pre>
What we did there is we told Firefox to directly download files with MIME type text/plain. If you need to test downloading other file types like .doc or .pdf, you would need to add their MIME types to this file too.
<li>There are two ways to add MIME types to the mimeTypes.rdf file.</li><ul><li>Via the Firefox Applications GUI</li><ul><li>From Firefox, choose Tools > Options > Applications</li><li>If you have previously downloaded a file of the required MIME type, look for it in the list. In the dropdown menu on the right, under Action choose Save File.</li><li>If the MIME type you want is not in the list, you would need to go to a website that allows you to download a sample file. If the file downloads automatically without showing the download pop-up, you would not have to do anything. If the pop-up shows up, activate the Save File radio button and check the Do this automatically for files like this from now on checkbox and click Okay.</li></ul><li>Manually editing the file</li> Note: This is generally not the advised way to edit the file due to its complexity. Care is required! <ul><li>Open the mimeTypes.rdf file.</li><li>Look for the RDF:Seq node and add your desired MIME type.</li><pre class="brush:xml"><RDF:Seq RDF:about=”urn:mimetypes:root”>
<RDF:li RDF:resource=”urn:mimetype:text/plain”/>
<RDF:li RDF:resource=”urn:mimetype:application/pdf”/>
</RDF:Seq></pre><li>Add the RDF: Description nodes for that MIME type.</li><pre class="brush:xml"><RDF:Description RDF:about=”urn:mimetype:application/pdf” NC:fileExtensions=”pdf” NC:description=”Adobe Acrobat Document” NC:value=”application/pdf” NC:editable=”true”>
<NC:handlerProp RDF:resource=”urn:mimetype:handler:application/pdf”/>
</RDF:Description>

<RDF:Description RDF:about=”urn:mimetype:handler:application/pdf” NC:saveToDisk=”true” NC:alwaysAsk=”false” /></pre></ul></ul></ol>To use this profile in our Selenium test:
<pre class="brush:java">FirefoxProfile profile = new FirefoxProfile(new File(“path/to/your/profile”));

// We can also set the download folder via code
File testFile = new File(“path/to/download/folder”);
String downloadFolder = testFile.getAbsolutePath();
profile.setPreference(“browser.download.dir”, downloadFolder);

WebDriver driver = new FirefoxDriver(profile);</pre> And you’re done. :) </div>

3 min read

MongoDB and Authentication

By default, MongoDB allows access to the database without authentication. Adding a user with a username/password is easy, but authenticating might be a bit tricky since the official documentation does not say the command directly.
First, we add an admin account. Navigate to the MongoDB directory on your machine then start the database.
<pre class="brush:java">$ ./mongo
> use admin
> db.addUser(adminuser, adminpassword)</pre>Switch to the database of your choice and add users to it.
<pre class="brush:java">> use foo
> db.addUser(myuser, userpassword)</pre>This adds a user myuser that has read and write access to the database. If we want a user with read-only access, set the third parameter for addUser().
<pre class="brush:java">> db.addUser(guest, guestpassword, true)</pre>You can check for users with access to a particular database like thus:
<pre class="brush:bash">> db.system.users.find().pretty()
{
“_id” : ObjectId(“4ee9863d954eb7168e07089d”),
“user” : “zarah”,
“readOnly” : false,
“pwd” : “70581bfb1e32e2286df11fe119addc7a”
}
{
“_id” : ObjectId(“4ee98658954eb7168e07089e”),
“user” : “guest”,
“readOnly” : true,
“pwd” : “88558f1ece63fa0b528012b9840bd9de”
}</pre>
Now stop the MongoDB server and restart it with authentication enabled.
<pre class="brush:java">$ ./mongod –auth
> mongo foo -u myuser -p userpassword</pre>where foo is the database that myuser has access to.
You can now read and write into database foo. Notice however that querying for databases would result to an error:
<pre class="brush:java">> show dbs
Mon Dec 19 17:21:20 uncaught exception: listDatabases failed:{ “errmsg” : “need to login”, “ok” : 0 }</pre>
Exit MongoDB and login again, this time using the read-only account. If we try inserting a document, an error should appear:
<pre class="brush:bash">> db.foo.insert({“title”,”MongoDB Authentication Test”})
unauthorized</pre>The read-only account can query for collections and use find() and its variations. It can’t, however, query for databases.

1 min read

Hello, it’s me again.


To my two readers out there, hello! It’s been a while since I posted here. I was transferred to another (non-Android) project and lost all my Internet privileges, hence the silence. I still can’t believe almost every other site is blocked by the office firewall! Makes software development ten times harder. Ugh.

Anyway, as of the last three months, I have been working on backend development (J2EE). I don’t know a lot about it, and the past months have been a great journey of learning. It is quite a shock being exposed all in one go to so many technologies and tools, I’m lucky my head didn’t explode.

Happy as I am to learn new things, I am quite sad to leave Android development behind. I’m still hoping though that I would be given the chance to go back to it, and pick up where I left off. Hopefully the Android train won’t be too far off by then.

In the meantime, I will be posting stuff that I’ve learned while working on J2EE, and hopefully somebody can learn from my mistakes. :)

~1 min read

Where’s my R.java?

This afternoon, I tried importing an existing project into Eclipse. Doing a Project > Clean usually clears up the R.java not found errors, but this time it didn’t work. I tried re-importing the project, copy-and-pasting it into a new workspace, restarting Eclipse, but the error is still there.

Just a quick note to self: If there is an error in the layout files, resolve that first then Clean.

I find it weird that no more specific error message is presented. Oh well.

~1 min read

ADT 12: Not so shiny after all


For all the shininess that ADT 12 promised, it seems that it also broke one major feature of DDMS: Launching emulators.

After updating to ADT 12, I kept on seeing that error when launching an emulator instance. Restarting Eclipse doesn’t help any.

Anyway, the apparent cause of this error is that ADT 12 has some problems with the SDK location having spaces. If you have already forgotten, it’s in Window > Preferences > Android.

To work around this bug, it is either you move your SDK to a folder in a path without spaces; or, modify the existing path to either of the following:<div><ul><li>If your SDK is in Program Files: C:/PROGRA~1/<path_to_sdk></li><li>If your SDK is in Program Files(x86): C:/PROGRA~2/<path_to_sdk></li></ul><div>Edit (20110711 1943): A bug has already been filed for this issue. Star to be notified of updates.</div></div>

~1 min read