13 July 2015

Pasting and Extracting Stuff

A lot of times, but especially when I am implementing some new logic, coding for me takes several steps:
1. Write down what I have to do as comments
2. Implement what I have written down
3. Refactor and improve what I have implemented

More often than not, step 3 means moving stuff around, copy-pasting things, extracting variables, defining constants, etc. I am not a hardcode never-using-my-mouse developer. If anything, I think my brain is limited to holding a limited number of shortcuts for everything I use in my life. Android Studio has the perfect shortcuts for making this easier for me. Luckily, these shortcuts made it to the list of things my brain remembers.

How many times have I copied (or even cut!) text but instead of pasting, I press ⌘+V (CMD+V) again! ARGH. I used to do ⌘+Z (CMD+Z) any number of times until I get back what I wanted. That is, until I learned about ⌘+⇧+V (CMD+SHIFT+V)! This key combo shows the clipboard history, which means no more fretting. Yay!

Refactoring also mostly involves extracting variables. I have already shown how to extract strings into strings.xml, and here I show how to extract things into methods, variables, fields, or constants.

It is fairly easy to remember them. Just combine ⌘+⌥ (CMD+OPTION) with the first letter of what you want to extract to. Time for a handy table!

Shortcut
⌘+⌥+M Method
⌘+⌥+V Variable
⌘+⌥+F Field
⌘+⌥+C Constant

This video might do a better job of showing what I'm trying to say. Code from Chris Banes's Cheesesquare demo.

08 July 2015

Super lightning talk: Tinkering with Tools

If you are just starting Android development or migrating from Eclipse to Android Studio, I gave a lightning talk on setting up some tools: 

14 May 2015

Stringy strings


While we are on the subject of strings, here are more ways of dealing with them in Android Studio. We all know that we should not hardcode strings in code, right? But sometimes, we forget and tend to do code first before defining them in strings.xml.

There are a couple of ways that Android Studio/IntelliJ makes this easy for us. The gif below (which took me a while to figure out how to do, by the way), shows how to deal with:
1. Moving a hardcoded string into strings.xml
2. Giving a previously undefined string ID a value in strings.xml

(Click to embiggen)

Move hardcoded string into XML:
This is probably the more common scenario. You happily put in texts into your TextViews, and now you have to copy and paste them into strings.xml. Don't! There is a shortcut for that.

Put your cursor somewhere in the string, press ALT+Enter to bring up the context menu, choose "Extract string resource" and give your string resource a name. This will create a new <string name="my_string_name">My string value</string> in strings.xml.

Studio magically also calls getString(R.string.xxxxx) for you. Neat, huh?


Make new string from ID:
This is for when you suddenly remember that you need a new string and want to sort of try to do it correctly so you type in the string ID. Only it hasn't been defined yet, so Studio complains. But it's fine. There is a shortcut for that.

Put your cursor somewhere in the as of yet undefined ID, press ALT+Enter to bring up the context menu, choose "Create string value for resource my_string_id", and type in the actual string value you want. Again, this will create a new <string name="my_string_id">My other string value</string> in strings.xml.

Remember, in both of these cases, Studio will create the new strings in strings.xml, but you can modify it to put in whatever variant you want it to be in (for localisations, screen size support, etc).

22 April 2015

Fixing a mistake in your git history

I have been using git for about five years now, but I definitely get stumped by it a lot. It is so powerful it's daunting. There has been a couple lot of times where I had been too careless and reliant on my fingers' add-commit-push muscle memory that I realised I have made a mistake too late. I have always been a proponent of clean, atomic commits, and when I find my commits all messed up, I hit myself in the head.

So, to stop myself from pulling my hair out looking for all the right StackOverflow answers that I KNOW I'VE SEEN THE SOLUTION BEFORE WHERE THE HELL IS IT???, I am writing the steps down to remind myself.

SCENARIO:
- I have committed some files in a previous commit that should not be there
- I want to REMOVE those files from that commit
- I want to preserve all other commits after that bad commit

CAVEATS:
- I am working in a local branch
- I will be removing those files forever
- I AM WORKING IN A LOCAL BRANCH*

SOLUTION:
1. Find out which commit you want to go back to.
$ git log

This should give you something like:
zarah.dominguez@R5003334 swipe-to-refresh-demo (master) $ git log
commit a6c00638b3d466a61e3381a98e6b44cf2d085164
Author: Zarah Dominguez
Date:   Tue Jun 17 16:34:50 2014 +0800

    Removed dependency on ButterKnife.

commit e25c6862a79270921a24d6bf2a9eb07cc3f03b36
Author: Zarah Dominguez
Date:   Tue Jun 17 16:07:33 2014 +0800

    First commit

If you just want the commit messages:
$ git log --oneline


2. Create a new branch based on the bad commit.
$ git checkout -b fix-that-shit e25c686

What this does is create a new branch named fix-that-shit, whose HEAD points to commit e25c686. The -b switch tells git to go to that newly-created branch.

3. Do your thing. In this case, I want to remove files.
$ git rm BadFile.java
rm 'BadFile.java'
$ git rm AnotherBadFile.java
rm 'AnotherBadFile.java'

4. Let git know that you've overcome your stupidity and are now saying sorry.
$ git commit --amend

An editor will open, and here you can edit the commit message.

5. Go back to the original branch. In my case, it is master.
$ git checkout master

6. Give this branch your changes.
$ git rebase fix-that-shit

7. Check your log. git might try and do it's thing, and do funny merges. So you might end up with a new commit in your history, something like:
zarah.dominguez@R5003334 swipe-to-refresh-demo (master) $ git log
commit 3e08701339c35301caf269058eab6359c9d87ecd
Author: Zarah Dominguez
Date:   Tue Jun 17 16:34:50 2014 +0800

    Removed dependency on ButterKnife.

commit ca57ac7974d7a422a2225612404cb6bd555acfc4
Author: Zarah Dominguez 
Date:   Tue Jun 17 16:07:33 2014 +0800

    First commit

commit 679ad41bedb2d61fbea36e39e334074b7de66dcd
Author: Zarah Dominguez
Date:   Tue Jun 17 16:07:33 2014 +0800

    First commit


7b. Examine the two commits, and you'll notice that the second in the list contains the file we just removed. I want to chuck that out completely, so I will do a rebase. This will take me back to the first commit in interactive mode:
$ git rebase -i --root

Now I can trash that bad commit by adding a pound sign (or fine, hashtag) before that commit's SHA:
pick 679ad41 First commit
#pick ca57ac7 First commit
pick 3e08701 Removed dependency on ButterKnife.

8. Check your logs again, and verify that the file is now nowhere to be found.
zarah.dominguez@R5003334 swipe-to-refresh-demo (master) $ git log
commit a6c00638b3d466a61e3381a98e6b44cf2d085164
Author: Zarah Dominguez
Date:   Tue Jun 17 16:34:50 2014 +0800

    Removed dependency on ButterKnife.

commit e25c6862a79270921a24d6bf2a9eb07cc3f03b36
Author: Zarah Dominguez
Date:   Tue Jun 17 16:07:33 2014 +0800

    First commit


9. Now kill and bury that shit:
$ git branch -d fix-that-shit

10. You will then need to force-push your changes if you have a remote branch. Hence the DO NOT DO UNLESS YOU ARE THE ONLY ONE USING THE BRANCH.
$ git push master --force



I am sure there is a more concise way to do this, but doing the verbose solution here for posterity.


-----------
* I cannot stress this enough. DO NOT DO THIS IF YOU ARE SHARING YOUR BRANCH WITH SOMEBODY ELSE. If you do, you automatically give them license to punch you in the face. (It can be a remote branch, as long as YOU ARE NOT SHARING IT WITH SOMEBODY ELSE)

20 February 2015

On being material

In case you missed it, I made a blog post about updating our app to material design. In it I talk about what material design is and what we did to adopt it. I hope you enjoy reading it as much as I did writing it. :) Head on over to Domain's tech blog for the details.

21 January 2015

SQLiteAssetHelper + ORMLite

I recently had cause to use Jeff Gilfelt's SQLite Asset Helper library. For those unfamiliar, it is a library that can help with including a pre-populated SQLite database with your Android application. It is extremely convenient with unbundling a potentially huge database you would want to ship.

For the app I was working on, I also wanted to use ORMLite. This is another library that helps with persisting POJOs to SQLite databases. If you deal with a lot of persisted objects in your code, then this is probably something worth looking into.

I won't deal here with how ORMLite does its stuff, I'll leave it to you to go through the documentation. What I'll write about today is how to make these two libraries work together.

Right. Moving on. If you look at ORMLite's sample code, it mentions that your database helper class must extend OrmLiteSqliteOpenHelper. If you look at SQLite Asset Helper's sample code, it mentions that your database helper class must extend SQLiteAssetHelper. What this means for us is that somehow, we need our database helper class to be able to talk to both of these libraries.

Since Android Studio is now the official IDE of choice, the sample code for this post is now AS-compatible. Yay!

First, gradle:
compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
compile 'com.j256.ormlite:ormlite-android:4.48'

When dealing with anything database, I tend to create my POJOs first. For this sample, I will be using the infamous Northwind database. For simplicity, this app will simply get the first entry from the Employees table and dump the contents into a TextView.

Since SQLite Asset Helper is the one who is in charge of our database creation and upgrade operations, we have to let it do the setup. Follow Jeff's example for that, BUT follow ORMLite's example for setting up ORMLite with no helper. At the end of it, you should have something similar to this.

To actually use ORMLite, just do as you usually would:
mOrmDbHelper = getHelper();

try {

    Dao<Employee, Integer> employeeDao = mOrmDbHelper.getEmployeeDao();

    // Try to get the first entry in the table
    Employee employee = employeeDao.queryBuilder().queryForFirst();
    if(employee == null) {
        textView.setText("No employees found!");
    } else {
        textView.setText(employee.toString());
    }
} catch (SQLException e) {
    e.printStackTrace();
}

Here we just get the first entry we can from the table and dump. If all goes well when we run the app, we should see this in the logs:

01-20 23:02:39.203  12555-12555/droidista.blogspot.com.ormsqliteassethelper W/SQLiteAssetHelper﹕ copying database from assets...
01-20 23:02:39.226  12555-12555/droidista.blogspot.com.ormsqliteassethelper W/SQLiteAssetHelper﹕ database copy complete
01-20 23:02:39.289  12555-12555/droidista.blogspot.com.ormsqliteassethelper I/SQLiteAssetHelper﹕ successfully opened database northwind.db

And we should see Nancy's details displayed on screen in all it's raw .toString() glory.

11 October 2014

AutoCompleteTextView Hell

Today, I ran into a weird "feature" of Android. I was working on an AutoCompleteTextView with the dropdown list having section dividers. It all works well in portrait mode, but gets all messed up in landscape.

I made a sample app to illustrate the point of this blog [Github repo]. Clone it, run it, rotate the phone, select an item from the suggested auto-complete results, and get your mind blown. Or your heart stabbed. Or your stomach sucker-punched. Your choice.

So what was happening? Here's what.

When we tell the app to perform a filter, we construct an array of the resulting matches. The example is pretty straightforward, just look for countries that start with whatever the user has typed in.

// Filter by start of string
String country = mCountries.get(i);
if(country.toLowerCase().startsWith(constraint.toString().toLowerCase())) {
    mFilteredData.add(country);
}
You can make this filtering as complicated as you like or need, just make sure to pass in whatever the user needs to see.

To illustrate having section headers (aka disabled items), we insert dummy text every fifth place in the list. The sample app does not care if there are more results after a section header, we still insert anyway.

So. Let's filter. Typing in "pa" will give us this set of data:
10-11 02:45:46.028  16282-20011/com.blogspot.droidista.autocompletetextviewhell D/AutoCompleteFragment﹕ 
Filtered results: [Section!, Pakistan, Palestine, Panama, Papua New Guinea, Section!, Paraguay]

There are two sections and five countries. Remember this.
Position in listValue
0Section!
1Pakistan
2Palestine
3Panama
4Papua New Guinea
5Section!
6Paraguay

This screenshot shows how the results are rendered in portrait mode. So far, so good. Selecting an item from the list populates the EditText with the country's name.

Now let's try rotating our phone. This is where things get juicy. First off, there are no section headers. Second, the results are not in alphabetical order anymore. If we debug all over the adapter, we see what is written on the screenshot: Item on the left is position = 1, item in the middle is position = 0, and item on the right is position = 2.

Remember the result set we have? "Pakistan" is definitely NOT in position = 0, it should have been a section header! If we go ahead and select "Pakistan" in the suggestions above the keyboard, the EditText will populate with the item in position = 0 of the result set, i.e. "Section!". Definitely not good.


The AutoCompleteTextView widget has been around since API level 1, but I haven't messed around with it as much as I did today. Googling returns very, very sparse results on this topic.
  • Does this mean people do not have the same problem as I did?
  • No one uses section headers in AutoCompleteTextViews?
  • No one uses this screen mode (EditText in full screen) in landscape without setting IME flags?
  • Is there a secret trick to making this work out-of-the-box?
Or the most plausible of all,
  • Am I being stupid?

17 June 2014

Swipe, not Pull, to Refresh

I have recently came across this new View in the support library package that allows your app to have built-in support for pull swipe to refresh. This is pretty cool, since we don't have to use any of the libraries out there. Admittedly, very little customization can be done, but then what else can we customize, right?

Anyway, here's a short demo of using this nifty little view.
Initial list
While refreshing
After refreshing

The app is a simple ListView that shows a list of countries. Swiping down on the list will simulate a long-running activity (like connecting to a server, for example) and afterwards updating the list with new data.

Adding support for swipe to refresh is pretty straightforward. You would have to wrap the swipe-able layout in a SwipeRefreshLayout. Here is my XML file:

<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:ignore="MergeRootFrame" >

   <ListView
          android:id="@android:id/list"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />

</android.support.v4.widget.SwipeRefreshLayout>

In your Fragment, you would then have to put an onRefreshListener on this SwipeRefreshLayout. In my Fragment's onCreateView, I have this:

// Configure the swipe refresh layout
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.container);
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setColorScheme(
   R.color.swipe_color_1, R.color.swipe_color_2,
   R.color.swipe_color_3, R.color.swipe_color_4);

The colors are defined in a colors.xml file in my res/values folder. To show or hide the refresh animation, you would have to call setRefreshing(boolean). This means that if you have to kick off an AsyncTask when the user swipes down, call setRefreshing(true) in onPreExecute, and call setRefreshing(false) in onPostExecute.

The implementation of onRefresh in the demo app is pretty simple, it simply grabs the next bunch of countries from a pre-defined list.

@Override
public void onRefresh() {
    // Start showing the refresh animation
    mSwipeRefreshLayout.setRefreshing(true);
   
    // Simulate a long running activity
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            updateCountries();
        }
    }, 5000);
}

That's it really.  Nice and short. The code for this demo is in Github.

08 June 2014

Quick Tip: Understanding Alternate Resources

Trying to support as many devices as possible the best way possible is a very daunting task indeed. You will usually need to provide a lot of different layouts, strings, or dimensions (among others) to make your app look great whatever the user's device is. And then you start chaining resource qualifiers and testing which resource is being loaded by the OS can become a nightmare very quickly.

Here's a trick I started using which seemed to work quite well. Create a string (the app name works well if you have an Action Bar) that you can display on-screen (or just throw into a Log or a Toast) that will quickly let you know from which of those very many /res folders your resources are being pulled out of.

For this demo, I have the following /res/values-xxxxx folders:

As you can see, there are strings.xml files in each of the configurations I want to support. Each of these files will contain whatever description we want to see on the device or in the Logs. In my case, I have custom strings for each form factor and orientation, which allows me to validate things easier. But this is particularly useful if the changes per form factor and orientation is subtle, such as dimensions.

Here's what it looks like for phones:


And here are the outputs for tablets:

Each of the strings.xml files contains just these two strings (sample taken from /res/values-sw800dp):


    MultiDeviceSupport - Tablet Portrait
    Hello world on a tablet!


Have fun debugging!

05 June 2014

Adding attributes to a custom view

There are times when using the default Android Views just doesn't cut it and you need to create your own version of a View. So how exactly do you do that? It's as simple as subclassing the View! But what if you want to add customizable attributes? Here's how.

Let's say I am creating a form-filling application and I want some of the EditTexts in the form to be required. However, I am so tired of having to implement the error checking for each and every one of those fields. What I will do is create my own EditText that will do the validation for me if that particular field is required. Let's do it.

Step 1: Create your custom view and create fields for the attributes you want. In this case, I want an EditText that will show the default EditText error display if the user has not put in any value.
public class RequiredEditText extends EditText {
   private boolean mRequired;
   private String mErrorMessage;

   public RequiredEditText(Context context) {
      super(context);
   }

   /**
   * Set this EditText's requirement validation. The error message
   * will be set to null by default if not provided.
   * 
   * @param required
   * @param errorMessage (optional)
   */
   public void setRequired(boolean required, String errorMessage) {
      this.mRequired = required;
      this.mErrorMessage = errorMessage;
  
      invalidate();
      requestLayout();
   }
 
   public void setRequired(boolean required) {
      setRequired(required, null);
   }

   /**
   * Lets you know if this field is set as required or not
   * @return
   */
   public boolean isRequiredField() {
   return mRequired;
   }
}
Step 2: If a field is required, we want the default error message to appear (with our own error message, of course). If the user fills in the EditText, we want the error to disappear.
public class RequiredEditText extends EditText {
   private boolean mRequired;
   private String mErrorMessage;

   public RequiredEditText(Context context) {
      super(context);
   }

   /**
   * Set this EditText's requirement validation. The error message
   * will be set to null by default if not provided.
   * 
   * @param required
   * @param errorMessage (optional)
   */
   public void setRequired(boolean required, String errorMessage) {
      this.mRequired = required;
      this.mErrorMessage = errorMessage;
  
      manageRequiredField(required);
  
      invalidate();
      requestLayout();
   }
 
   public void setRequired(boolean required) {
      setRequired(required, null);
   }

   private void manageRequiredField(boolean required) {
      // If we are required, set the listeners
      if(required) {
         setOnFocusChangeListener(mFocusChangeListener);
         addTextChangedListener(mTextWatcher);
      } else {
         // In case there is an error message already, clear it
         setError(null);
   
         // Remove the listeners
         setOnFocusChangeListener(null);
         removeTextChangedListener(mTextWatcher);
      }
   }

   /**
   * Lets you know if this field is set as required or not
   * @return
   */
   public boolean isRequiredField() {
      return mRequired;
   }

   OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener() {

      @Override
      public void onFocusChange(View v, boolean hasFocus) {
         // If the focus was removed from the field and it IS required,
         // check if the user has put in something
         if(!hasFocus && mRequired){
            isRequiredFieldFilled();
         }
      }
   };
 
   TextWatcher mTextWatcher = new TextWatcher() {

      @Override
      public void onTextChanged(CharSequence s, int start, int before, int count) {
         // Once the user types in something, remove the error
         setError(null);
      }

      @Override
      public void beforeTextChanged(CharSequence s, int start, int count, int after) { /* do nothing */ }

      @Override
      public void afterTextChanged(Editable s) { /* do nothing */ }   
   };

   private boolean isRequiredFieldFilled() {
      // If the EditText is empty, show the error message
      if(TextUtils.isEmpty(getText().toString().trim())){
         showRequiredErrorDrawable();
         return false;
      }
      return true;
   }

   private void showRequiredErrorDrawable() {
      setError(mErrorMessage);
   }
}
Step 3: Now it's time to add the fields to the Layout Editor. Create an attrs.xml file in /res if there isn't one already. Declare a styleable and include the attributes you want to appear in the Layout Editor.

   
       
       
   

The name of the styleable does not need to match your custom view class name, but doing so makes it more readable and maintainable.

Step 4: Go back to your custom view implementation and add constructors that take in an AttributeSet.
public RequiredEditText(Context context, AttributeSet attrs, int defStyle) {
   super(context, attrs, defStyle);
   init(attrs);
}

public RequiredEditText(Context context, AttributeSet attrs) {
   super(context, attrs);
   init(attrs);
}

private void init(AttributeSet attrs) { 
   TypedArray a=getContext().obtainStyledAttributes(
      attrs,
      R.styleable.RequiredEditText);

   try {
      setRequired(a.getBoolean(R.styleable.RequiredEditText_required, false), 
            a.getString(R.styleable.RequiredEditText_errorMessage));
   } finally {
      //Don't forget this, we need to recycle
      a.recycle();
   }
}
Step 5: Go to the Layout Editor and look for "Custom & Library Views" in the Palette (you may have to click on "Refresh" several times before your custom view appears in the list). Add the custom view to your layout and check out the properties panel!
 

 As always, the code is in GitHub.