Wednesday, July 01, 2015

god's number is 20

Solving Rubik's cube in just 20 moves: cube20.org

Cat in kitchen sink




My cat Pengo sleeps in the kitchen sink.

Thursday, May 28, 2015

Things I like in Eclipse that are missing or wrong in Android Studio

I used to use Eclipse for software development. Last year, Google announced that for Android development, Eclipse is no longer supported, I have to use Android Studio. The following is a list of things that I like a lot in Eclipse that are missing in Android Studio. I did select the Eclipse-compatible key layout, to make things easier after coming from Eclipse.
  1. Going through my code, in Eclipse I can hover over a method name with 'ctrl' pressed, this shows me "open declaration" and "open implementation". This makes it very simple to quickly go to the implementation of a method. In Android Studio, I have to select the method name, then press ctrl-alt-b, then I get implementations. Instead of using one hand for my mouse and the other for my keyboard, AS makes me switch between mouse and keyboard a lot, which eventually may cause Carpal Tunnel Syndrome.
  2. When there's an error in my code, Eclipse shows a red underline under the name of all classes in which there's an error. I can quickly find all code I need to look at. In AS, I need to run the code first, then AS gives me a list of errors and I have to click on each error and go to the code. A lot more cumbersome. Then, after I changed the code, I run the code, and new errors show up. Annoying.
  3. AS uses Java Swing for its UI. The font is pixelated, it doesn't look as good as Eclipse fonts do.
  4. Eclipse has an outline view on the right of the screen, that you can enable. This makes it easy to navigate through methods in your class. AS simply doesn't have this. Instead, you have to go to the menu, click through options, then an outline pops up, that you have to close before you continue editing. Annoying and cumbersome.
  5. In Eclipse, ctrl-alt-g gives you all occurrences of a class. In AS, you alt-click, select "find usages", then in the bottom of your screen a list pops up. More clicks than in Eclipse.
  6. In Eclipse and in Android Studio, ctrl-alt-F formats your code. In AS, the code is scrolled over your screen and leaves you looking at an entirely different part of the code than what you were working on.
  7. When you make a typo or an error, both Eclipse and AS show a red underline. In Eclipse, I hover the mouse over the word, and suggestions pop up. In AS, I double click the word, then press alt-enter, for the same suggestions. It's a few more mouse clicks, and what's worse, it requires going to the keyboard and back to the mouse.
  8. When an if statement has one line between brackets, AS suggests removing the brackets. That's bad programming practice.
  9. Finding text in a file in Eclipse is done by going to menu "Search", then "file", then type your text. In AS, it's "Edit", "find", "find in path". Just one extra click, but the box in "Search" is very wide, so often your mouse leaves the box when moving right, then the box closes, you have to click "Search" again.
  10. If you want to delete a line from your code in Eclipse, a suggestion lightbulb shows up exactly at the spot where you click to start marking the line. You can't select the start of the line, you have to select the line starting position 8 or so. Annoying.
  11. Starting an app takes for ages, for gradle to build it, on a wear device it takes many minutes. In the mean time, you start typing in a shell window or an email. Suddenly, AS has finished building, grabs focus, picks up your first keystroke, and starts the app on the wrong device. Grabbing focus after processing for minutes is evil.
  12. More often than not, the menu just doesn't work.
  13. I used ctrl-shift-O to organize imports, ending up with imports replaced by garbage strings all over my app. I did a "git reset --hard" to fix it.
  14. "Choose device" popups and other popups appear on screen with the "OK" button under the lower edge of the screen. Even when I drag it upwards, the next one appears low again.
  15. In AS, when you het ctrl-shft-R and you type the name of a class, you not only get all classes to select from, you also get a popup with the jar files that contain that class. Oh, wait, this is something that's better in AS than in Eclipse.
  16. I use a script to sign and zipalign apk files. In Android Studio, I can't find how to generate an unsigned unaligned release build. I'm sure it can be done, but the menu only offers "generate signed APK". The Jetbrains web site says
    Extracting an unsigned release Android application package
    
    Select File | Project structure from the main menu and click Artifacts in the left pane.
    if only there would be an "Artifacts" option in the left pane, or any pane.
  17. Eclipse lets me disable specific lint checks, like checking for translations. In AS, there's no menu option or setting to do so. I have a couple of strings that aren't visible to the user, or that are the same in all languages like ">" and "<". AS forces me to define identical strings in four different languages.
  18. Eclipse sometimes generates a build that crashes, saying "can't find class .class. Seems to be a bug in Eclipse. Oh, wait....
  19. Unit test results in Eclipse appear in a window in the IDE. In AS, you have to open an html file somewhere in your file system, to open an overview of test results. Why not just show the results in my IDE? The message window of the IDE shows the link, but it isn't clickable, you have to navigate to the test results manually. Ridiculous...
  20. When reformatting source code, AS does not recognize comments (/** style). It does not reposition the asterisks the way Eclipse does so neatly.
  21. When completing an Enum switch statement, for every case Eclipse gives me the remaining options. AS gives me all options every time. 
  22. to be continued....

Wednesday, January 07, 2015

Friday, July 18, 2014

How the IRS in the Netherlands effectivily prevents the funding of startup companies

A software engineer one day has a brilliant idea for a new product. As she has some savings in the bank, she can afford to take a year off and work on the brilliant new product. After about six months, she publishes the app, and she decides that she needs more people to complete the whole set of software, web site and apps. Fortunately, she finds a VC fund that is willing to invest €3 million. Now she can incorporate the company, hire a couple of programmers, a sales person, and set up a proper set of servers in the cloud.

Two months after the closing of the investment deal, she receives a letter from the IRS. The letter states that as the VC fund invested €3 million for a stake of 30% in the company, our software engineer entrepreneur created a value of €7 million with her own shares at incorporation of the company. She now needs to pay income tax over €7 million. Of course, she does not have that money. Six months later she's declared bankrupt, she needs to leave the company, and the company without her is liquidated, at a substantial loss for all involved except the IRS. It takes her three years to get out of bankruptcy, as that's the legal term on which a personal bankruptcy should end with a clean slate.

Another software engineer, a close friend of our unfortunate first entrepreneur, decides to follow another path. He takes a year off, and on the first day incorporates a company to hold the intellectual property rights of the software he will be creating. That way, no value is "created" at the point where a VC fund invests.

After two months, our friend receives a letter from the IRS. As he's working as the sole proprietor of his corporation, in which he is creating value for the company, and as he has no other jobs, the company is required by law to pay him a salary of € 40,000 per year. Of course, the company doesn't actually need to do this, as long as it pays taxes as if it did. The entrepreneur needs to pay income tax for a fictional income of € 40k per year. This is not how our friend wants to spend his savings, so he calls the company he used to work for and gets his job back. He still has to pay two months of income tax from his savings.

Another friend, having seen all this, thinks she's smarter. She writes a letter to the IRS asking them how to go about if you start a tech company, you work 12 months for free, living off your savings, and how to avoid being taxed before the company makes revenues and the entrepreneur gets an income from the company. It takes two months for the IRS to reply: "we are not a consulting agency, we are the IRS. We tax companies and people, but we don't advise you on how to arrange your administration. We refer you to your tax consultant". After conversations with four different fiscal consultants, having received four different opinions on how the IRS will act, the entrepreneur gives up and tells her boss to please not pay attention to her resignation letter.

But there's a happy ending,
The fourth entrepreneur in this little group of friends now knows how to do it. He spends a year developing a prototype product, he incorporates his company with the help of a well known VC fund, he hires a sales person and five programmers, and he's very very happy. By the way, he lives in San Francisco now, where he moved when he left her job and started working for his own company.

Saturday, July 05, 2014

Android programming: injecting a context using Robolectric and Roboguice

I have an Android project where I retrieve instances from a database. These classes are not under control of Roboguice, so I have no injection. If I use Roboguice.getInjector(..) I need a context, that I can supply from a static referenced context that I define elsewhere. However, I want to test the same classes in Robolectric, I need a different context.

I decided to do the following.
I create a ContextProvider like so:
public class ContextProvider {

    protected static Context context;

    public static Context getContext() {
        return context;
    }
}

I make a static reference to this in my app as
ContextProvider.getContext()
The implementation in my app is
public class ContextProviderImpl extends ContextProvider {

    @Inject
    public ContextProviderImpl(Context ctx) {
        context = ctx;
    }
}
and in my Robobuice modules file I have
    bind(ContextProvider.class).to(ContextProviderImpl.class);

In my Robolectric test project, the implementation is
public class TestContextProvider extends ContextProvider {

    public TestContextProvider() {
        context = Robolectric.application.getApplicationContext();
    }
}
and the modules file in the test project has
    bind(ContextProvider.class).to(TestContextProvider.class);
What happens is this:
In Roboguice you can implement a class (not interface) with another class that extends the first. The parent class provides the getter for the context instance, so it's the same in the project and the test project. The implementation sets the context field, in different ways in the project and the test project. I think this is an elegant way to have Roboguice injection in classes that can't have injection in the "normal" way.

There is another way of achieving this: you can use Guice AssistedInject to re-create the instance after you retrieve it from a database. That works, but it's more code and less elegant than this.

Tuesday, July 01, 2014

The art of not so good design

I have a waste basket, with a lid. Cost three euro's, so what can you say. Still, someone has spent some time to design the thing, and, hopefully, test the prototypes. Or not. The lid hangs from the middle, it's bent so its center of gravity is below the hinges and it automatically shuts. Unless you throw something in, then the lid goes past its balance point and stays open.

Dear waste basked designer, how hard is it to test a prototype of your waste basked by throwing in a piece of paper? How hard is it to keep looking at the lid until you notice it doesn't close by itself? How hard is it to change the shape by just half an inch to make sure it always closes?
You may say "how hard is it to bend over and close the lid yourself?". But it's not about that. Every morning I bend over and close the lid. The issue is that you designed it so I wouldn't have to close it myself, and you failed.

Rooting a Nexus 5 (2)

I had rooted my Nexus 5, but the Android update before last made it lose root. I tried to reroot, but I ran into trouble because fastboot didn't recognize my device and the sdcard was not readable when trying to install from that.

I tried again today, this site (as before) proved very helpful.
I installed clockwordmod recovery again, then installed Clockworkmod SU via adb sideload (recovery said sdcard could not be accessed so I couldn't install from there). I used "adb sideload" rather than push the zip to the sdcard. Now the device has root again. In the mean time the device had upgraded to Android 4.4.4 but I don't think that mattered....

Btw, it turns out you can root a device even if it's encrypted. Or, that's what I did.

Thursday, January 23, 2014

UI design

I have an interesting UI design issue. In a project in which we develop an Android app, we were discussing the behavior of the back button.

The app has tabs in the actionbar. The behavior of the back button currently is to leave the app, independent of the tab you have pressed. My idea on the back button is that it should take you back to the home tab, then when you press it again, you leave the app. If in one of the tabs you have pressed a button or a list item which brings you deeper into the app, the back behavior should be as follows: first press brings you to the default page of the tab you have selected, next press brings you to the default page in the home tab (or whichever tab is selected when you enter the app), third press makes you leave the app.

In another project, we had to different landing pages, depending on the preferences of the user. I made it so that the back button brings you back to the landing page that is in your preferences, or the landing page that you have selected when you entered the app. I feel that this is the most intuitive behavior of the back button, even if, as a developer said, the button is not stricly bringing you "back".

Wednesday, January 22, 2014

Rooting an encrypted Android device

I wanted to root my Nexus 7, so I installed the right image and zip. But then the device didn't work any more. I think it's because the device was encrypted, and somehow after rebooting, the thing got stuck. I managed to get the right factory images from Google (I didn't backup the system, which you should always do), my defice is "flo" which is "razor". After I got the device back to a factory installation, rooting the device with clockworkmod was easy. Now I'll encrypt it again.

Wednesday, December 18, 2013

How to make ormlite and robolectric work together.
I use Roboguice also, this is my RoboInjectedTestRunner:

public class RoboInjectedTestRunner extends RobolectricTestRunner {

 public RoboInjectedTestRunner(Class testClass)
   throws InitializationError {
  super(testClass);
 }

 @Override
 protected Class getTestLifecycleClass() {
  return TestLifeCycleWithInjection.class;
 }

 public static class TestLifeCycleWithInjection extends DefaultTestLifecycle {

  @Override
  public void prepareTest(Object test) {
   Application application = Robolectric.application;

   AbstractModule eftelingModule = new EftelingModule();
   AbstractModule testModule = new TestModule();

   RoboGuice.setBaseApplicationInjector(application,
     RoboGuice.DEFAULT_STAGE,
     RoboGuice.newDefaultRoboModule(application), eftelingModule, testModule);

   RoboGuice.getInjector(application).injectMembers(test);
  }

 }
}


If you don't use Roboguice, you can use the default RobolectricTestRunner instead.
This is the annotations I have for my test classes:
@RunWith(RobolectricTestRunner.class)
@Config( shadows = {ShadowCaseSensitiveSQLiteCursor.class})
@UsingDatabaseMap(SQLiteMap.class)
public class APITest {
...
}

This is the ShadowCaseSensitiveSQLiteCursor: (it's from here:http://stackoverflow.com/questions/11833150/robolectric-testing-with-ormlite so please give that post a credit)
/**
 * Simulates an Android Cursor object, by wrapping a JDBC ResultSet.
 */
@Implements(value = SQLiteCursor.class, inheritImplementationMethods = true)
public class ShadowCaseSensitiveSQLiteCursor extends ShadowSQLiteCursor {
  private ResultSet resultSet;

  public void __constructor__(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
  }

  /**
   * Stores the column names so they are retrievable after the resultSet has closed
   */
  private void cacheColumnNames(ResultSet rs) {
    try {
      ResultSetMetaData metaData = rs.getMetaData();
      int columnCount = metaData.getColumnCount();
      columnNameArray = new String[columnCount];
      for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
        String cName = metaData.getColumnName(columnIndex);
        this.columnNames.put(cName, columnIndex - 1);
        this.columnNameArray[columnIndex - 1] = cName;
      }
    } catch (SQLException e) {
      throw new RuntimeException("SQL exception in cacheColumnNames", e);
    }
  }

  private Integer getColIndex(String columnName) {
    if (columnName == null) {
      return -1;
    }

    Integer i = this.columnNames.get(columnName.toLowerCase());
    if (i == null) return -1;
    return i;
  }

  @Implementation
  public int getColumnIndex(String columnName) {
    return getColIndex(columnName);
  }

  @Implementation
  public int getColumnIndexOrThrow(String columnName) {
    Integer columnIndex = getColIndex(columnName);
    if (columnIndex == -1) {
      throw new IllegalArgumentException("Column index does not exist");
    }
    return columnIndex;
  }

  public void checkPosition() {
    if (-1 == currentRowNumber || getCount() == currentRowNumber) {
      throw new IndexOutOfBoundsException(currentRowNumber + " " + getCount());
    }
  }

  @Implementation
  public void close() {
    if (resultSet == null) {
      return;
    }

    try {
      resultSet.close();
      resultSet = null;
      rows = null;
      currentRow = null;
    } catch (SQLException e) {
      throw new RuntimeException("SQL exception in close", e);
    }
  }

  @Implementation
  public boolean isClosed() {
    return (resultSet == null);
  }

  private Map fillRowValues(ResultSet rs) throws SQLException {
    Map row = new HashMap();
    for (String s : getColumnNames()) {
      row.put(s, rs.getObject(s));
    }
    return row;
  }

  private void fillRows(String sql, Connection connection) throws SQLException {
    //ResultSets in SQLite\Android are only TYPE_FORWARD_ONLY. Android caches results in the WindowedCursor to allow moveToPrevious() to function.
    //Robolectric will have to cache the results too. In the rows map.
    Statement statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
    ResultSet rs = statement.executeQuery(sql);
    int count = 0;
    if (rs.next()) {
      do {
        Map row = fillRowValues(rs);
        rows.put(count, row);
        count++;
      } while (rs.next());
    } else {
      rs.close();
    }

    rowCount = count;

  }

  public void setResultSet(ResultSet result, String sql) {
    this.resultSet = result;
    rowCount = 0;

    //Cache all rows.  Caching rows should be thought of as a simple replacement for ShadowCursorWindow
    if (resultSet != null) {
      cacheColumnNames(resultSet);
      try {
        fillRows(sql, result.getStatement().getConnection());
      } catch (SQLException e) {
        throw new RuntimeException("SQL exception in setResultSet", e);
      }
    }
  }
}

Sunday, December 08, 2013

My dad moved from Windows XP to Windows 8. Disaster. One thing Windows 8 doesn't seem to support is getting your old email (Outlook Express, from POP3) into your new mail program. Apparently, Windows users don't keep their email?

What I did to transfer his mail is the following.
I made a copy of the OutlookExpress mail file and put it on my computer. Evolution allows me to import it into a new mail box. Then I created an account on my dad's IMAP mail, and copied the mail from the import mailbox to his Inbox. Simple as that. Why can't Microsoft think of a way to keep your old email?

Saturday, November 02, 2013

The art of bad design

In the course of my life, I have used some ten different vacuum cleaners, if not more. We all use vacuum cleaners, and we all know what the common issues with vacuum cleaners are.
  1. The power cord.
    The cord of a vacuum cleaner tends to get tangled when you use it, and because it's longish, it gets tangled when you store the machine in a closet. That's why modern vacuum cleaners have a system that retracts the cord when you push a button or when you jerk the cord. This works fine on all vacuum cleaners. That is, it works fine when it's new. But lets consider your average three year old vacuum cleaner. My Dyson currently is two years old, and when I store it, the cord sticks out by about a foot. And this is ok, because in all other cleaners, it was worse. The cord retraction was tested with new cleaners. Apparently they don't test older machines.
  2. The wheels.
    A vacuum cleaner has two big wheels, and a small caster. This causes the device to follow your movements and not topple. The caster hops over the cord most of the time, it doesn't get stuck. As long as it's new. After a few years, or even after six months, the caster wears out, and it does get stuck when it hits the power cord. I've had a vacuum cleaner with a fine looking caster, made out of plastic, it looked like a half sphere with a little wheel sticking out. It stopped moving after half a year, and when I took it apart, every idiot could see that it couldn't have lasted long. It was badly designed and badly made. I replaced it with the cheapest and simplest caster from the hardware store, which outlasted the rest of the vacuum cleaner. Why didn't the manufacturer put in that caster? Is it because it would have added one euro to the price?
  3. The hose.
    When you walk around pulling the vacuum cleaner, you know that the device has a tendency of falling backwards when you pull it in the wrong direction. Which you do because you don't pay attention. Manufacturers come up with all kinds of solutions for this, and when you test your new vacuum cleaner, you're happy because it works perfectly well. But the connections between the hose and the device wear out quickly, or they get stuck because dust is collecting inside, so my two year old Dyson does topple when I make a wrong move. Which is stupid because if Mr Dyson would have spent an hour a week using an old vacuum cleaner, he'd know what the issues are and he could have started looking for proper solutions.
  4. The brush.
    I have cats. Cats drop mountains of hair in your home. Which you have to vacuum out. My vacuum cleaner, like all of them, has a brush with hairs on the edge and a whole that sucks in the middle. The cat hair gets sucked towards the brush, and it gets stuck between the floor and the brush, outside the brush, so it can't be sucked up into the machine. Every two minutes, I have to pause and remove the cat's hair from the brush. Mr Dyson apparently doesn't have cats. Or he would have made the brush inside out: it would have brushes that it rests on in the middle, and it would be sucking at the edges.
If I can think of that, why can't the average designer of vacuum cleaners? One thing I know. Vacuum cleaner designers don't do the vacuum cleaning at their place.

The art of bad design

I have a wastebin in my kitchen. It's a simple bin, mostly thin sheet metal, plastic inner bucket. The lid opens by a pedal at the bottom, so you don't have to touch it with your hands. It was rather cheap, though one of the most expensive ones the store had on display.
Now, someone has spent a couple of days designing the thing. They created a few prototypes, tested them, created some production models, tested them, then set off to mass produce them. Also, the designer probably designed wastebins of similar type before, and they obviously use one on a daily basis. You'd expect the bin to be perfect. But it's not.

First, the hinge that holds the lid protrudes a millimeter. When you lift the inner bucket from the bin, that millimeter is enough to make the bucket get stuck, you can't lift it out without wiggling and jerking it. Which, if the bin is full, causes content to fall out. There's no reason for the hinge to protrude. At the same cost, it could have been made so that it doesn't stick out. But they didn't.
The second issue is when you want to get the plastic bag out of the bucket. Most people use plastic bags in the wastebin, so they don't have to thoroughly clean the bin every time they empty it. The handle that you use to lift the bucket, is one millimeter smaller than the bucket itself. It sticks out to the inside. Which means that when you lift the bag out, the handle gets pulled up and you can't lift the bag. Again you have to use both hands and wiggle and jerk, causing garbage to spread over your kitchen floor. There's no reason why this handle should stick out to the inside. It's just a designer being thoughtless.

I'm surprised how a person who has experience designing wastebins and who uses one, and has used many in their life, can make such stupid and unnecessary mistakes. My theory is that they are not the person who does the kitchen work too often in their household, or they wouldn't have made these mistakes.

Thursday, September 19, 2013

The art of terrible design

I am not a chef. Not by profession, not by hobby. I don't like to cook, but I do like good food, so every day I make a proper dinner from fresh ingredients. I guess I am a "gourmet cook".

I have always used a natural gas stove, with four burners. That's the best, the gas has the right high temperature and it's easy and fast to adjust. Like, when you make hot chocolate, you heat the milk quickly, then you have half a second to switch if of when it boils.

After my last move, I got a Bosch electric plate. It's induction based, so it's hot, and it's quick to adjust. It has four stations, which most of the days is just enough for me. However, it has one major flaw.
The designer decided that the plate should be easy to clean, hence it has no protruding knobs, it has touch keys. Or rather, you don't just touch them, you have to press them, "+" for higher, "-" for lower. The buttons go from 0 to 9 in half steps, you push it 17 times for full heat. The designer also thought that a chef wants as little knobs or pushbuttons on the plate as possible. So, instead of having a plus and a minus button for each station, it has one plus and one minus, and a selection button with a light that indicates which station is currently "active". Now, imagine the little pan with milk for my hot chocolate. I put it on 9, I wait for it to boil. When it does, I push the station selector twice to select the milk pan, then I push the minus button 17 times to switch it off. Then I pour new milk in the pan and I clean the plate, because the boiling milk went all over the plate. Of course, after a while you find out there are shortcuts, instead of pressing 17 times, there's a way to switch it of in just a few steps. But including the selector, it's always five or six. And you shouldn't accidentally push the wrong button, or one button once too many, because that increases the total nummber of actions, and you do press the wrong button because you want to stop the milk from boiling over and the buttons are barely visible on the plate. The fastest way to switch off the milk pan is to switch off the plate alltogether. Then you have to switch it back on and switch back on the other stations.

My question is: why does a very expensive stove have such a lousy user interface? Why can't it just have four turning knobs? The answer is simple. The engineers and designers of the stove are not chefs. Or if they are, they have never used this stove, because if they had, they would have changed the user interface.
I propose the following add-on to my stove: a remote control with four turning knobs that let you adjust the stations fast, while maintaining the option of easy cleaning of the plate.