DD: Android Things Rubik’s Cube Solver (3)

I have received the next shipments, which includes a second claw and 2 more servos.

Now that I have 3 non-defective servos (and a 4th one still on transit), I can at least test the concepts of face twist and cube rotation. This is a first step towards manipulating a cube programmatically, and then solve it, but this step has proven to be more complex that anticipated, and for partly unexpected reasons.

One claw face twist

I received the servos first, so the first thing I tried was to have one functional claw perform a face twist while I held the cube. This test worked out quite nicely.

The code for that is actually quite simple:

fun twistFace() {
    claw.turnClockWise()
    Thread.sleep(600)
    claw.release()
    Thread.sleep(800)
    claw.resetRotation()
    Thread.sleep(600)
    claw.grab()
}

Given that setting the servo is an asynchronous operation, I am just having a Thread.sleep to check how long it does take to execute the move. I expect this to be cleaner as I move along.

And then a few days later I received the second claw, so it was time to test the same concept but with 2 claws.

One would thing that cube twists and cube rotations with 2 claws was going to be easy after the previous test.

Well, spoiler alert, I was wrong.

Using two claws

When I assembled the second claw, I noticed that the servos were quite different in terms of configuration. This may happen because they came from different manufacturers, even when they are built on the same spec. In any case I had to tweak the pulse duration individually for each claw to open and close the same way. I was hoping to have the same configuration for each claw, but it is not going to be possible.

After some trial and error, the current configuration for my servos is:

rightClaw = Claw(pca9685, 0, 1)
rightClaw.setGripPulseDurationRange(1.0, 2.5)
leftClaw = Claw(pca9685, 2, 3)
leftClaw.setGripPulseDurationRange(1.0, 2.0)

In the meantime I also created a very simple UI for the NXP iMX7D with 4 buttons to:

  • Reset the claws: putting both claws on the position open and centered on rotation
  • Grab the cube: The claws open and then close, but not fully, just enough to grab the cube
  • Twist: Same move as before, but the other claw is always holding
  • Turn: Same move, except that this time the second claw releases and then grabs it again}

And -as the naive engineer I am- once the opening and closing of both claws was properly configured. I went straight away to try a face twist and this is what happened.

The servos have some “room” and when they release, the reattachment position is different. Not by much, but enough to make the re-grab at the end of the face twist fail.

I did not notice that I was subcounsciously correcting this myself when I was the one holding the cube.

So, to just be able to visualize this problem, I added a new method to the app, one that simply releases and regrabs the cube, first one claw and then the other.

Even this simple test proved that it was going to be trickier than expected.

So, where am I now and what is next?

Still missing one more servo, maybe once I have it properly screwed to the claw the movement gap will be smaller, but as I said on the tweet, I am afraid I’ll need proper brackets.

The physical part of this project is turning out to be the most challenging one so far and I want to think about it, maybe use wood and proper brackets is the solution, but I also want to have some rails to adjust the claw distance…. and no, Lego does not have enough resolution to do that, a Lego dot is too big.

The next goal is to be able to scramble a cube using a few different face moves. Once I get passed the first twist I reckon it will be easy.

I’m also afraid I’ll have to swallow those words.

DD: Android Things Rubik’s Cube Solver (2)

Time for an update! I just received the first claw with 2 servos and I had the urge to put it together as soon as I saw the package.

The specs of the claw stated that it was 55mm wide when open, and most modern cubes are ~56mm, so -as expected- it is too small to handle a standard cube, but hey, it was cheap.

Since I was expecting this, I had already ordered some mini cubes (30mm) and in the meantime I also have some 2x2x2 to try with, so not too much of an issue. I even got a spare mini DaYan, which a pretty good speed cube, and you can soften the springs to make it easier to move and require less torque from the servos (which I indeed did)

Now, into the details: I got the claw with 2 MG996 servos. As it happens these servos have a range of 180 degrees, which means that:

  • I need to tweak the pulse duration range to get the complete angle radio
  • I will only be able to do 2 types of moves, either R & R’ or R and 2R, but not the 3 of them, since I’d need at least and angle range of 270 for that (to go from -90 to 180

Anyway, I got the claw doing the basic move, which is (starting from 0 degrees turn and cube held):

  • Turn 90 degrees
  • Release
  • Get back to 0 degrees
  • Grab

These movements are what is needed to perform a turn of one face and the return to the start position.

One important point to make is that whenever you use a motor, you should use an external power source and not the 5v output from a board. That output is meant to power electronic circuits and can’t drain much power. Best case you’ll reboot the board, worse case you’ll damage it. So here it is, again, in bold:

When using any motor, use an external power source

While Android Things has 2 PWM outputs I could, in theory, have used them directly, but looking ahead I know I’ll need 4 PWM, so it is a good idea to have them connected to a PWM expander, a PCA9685, for which I wrote a driver for Android Things a while ago as part of PlattyThings.

Note how it has special pins for power input (use them!) and it has pins to connect up to 16 servos to it.

Now into the bad news: one servo was defective. As soon as any minor torque was needed it started spinning loose. That has made impossible to test actual turning of the face with a cube while holding the cube myself.

On the bright side, I did learn how a servo is built internally, which is interesting.

While I do have some other motors, nothing really fit. I have a 360 continuous rotation servo, but that would need me timing the twist, I also have some stepper motors, but that will require a lot of GPIO ports and a different connection to the claw (it is designed to fit with a servo) and finally some mini servos (MG90S), which I suspect do not have enough torque, so I did not even try.

Ultimately I decided to order a new servo from another model – a MG946- that in theory has a wider rotation range to solve both problems.

Other considerations is if I should make the structure for the solver using cardboard or Lego. I just tried and the servos fit quite well into the Lego system, so maybe that’s what it’s going to be.

And that is it for now until the next shipment arrives. It will probably be some mini cubes or the second claw.

DD: Android Things Rubik’s Cube Solver (1)

Some days ago I decided to create a Rubik’s Cube Robot Solver using Android Things.

This is not a new project, many people has made Rubik’s cube solvers with PCs, Lego, Arduino, etc. But apparently there has not been one done on Android Things yet.

To my surprise, many people asked me to document the journey, so here we are, doing a Developer Diary of ATRUCUSO (Temporary name).

The first step was to decide on the shape of the robot. After looking at different ideas online, I decided to go for one that has 2 claws at 90 degrees, similar to this one:

Image result for rubiks cube robot

Next step was to order some pieces. I found robot claws with 2 servos on AliExpress for a reasonable price (about 20 euros for 2 claws and 4 servos) but the problem is that they are too small, they are 55 cm when fully open, which is the size of a standard Rubik’s cube, so I had to buy a mini cube as well.

In addition to those, given that the Android Things Developer Kits only have 2 PWM outputs, I will need a PWM expander, but I already have a few of those at home :).

The claws look like this (proper photo upon arrival)

Image result for robot claw aliexpressGiven that it will take about a month until they arrive, I have to start thinking about the other areas.

The first architecture decision is to separate the project in 3 areas that are independent:

  • Reader: Which will be a camera and some sort of color detection.
  • Solver: I am planning on having more than one of those, but the initial one will use some variant of the advanced Fridritch (CFOP) method.
  • Manipulator: This is the interface with the cube. It will include the moves to be performed as well as allow for cube rotations.

Some interesting notes:

  • The cube manipulator will implement the same interface as the internal representation of the cube for the solver, but the solver will have just an internal object that represents the cube (discussion about data structures on a follow up post)
  • This separation will allow for intermediate steps along the way, such a cube scrambler, or a robot controller via Android Things with all the options.
  • The selection of the 2 claws model makes some moves much simpler than others, so I will probably be looking at algorithms specialized for one-hand solving based on U, U’ 2U, R, R’ and 2R movements.

And that’s it for now. I might start tinkering with the data structure for the cube while I wait for the parts to arrive.

TBC.

Kendama – A game analysis

I’ve been playing Kendama for over 6 years, and I’ve seen many people getting really hooked into it. As game designer / developer I was curious about it and analysed the game from the game design point of view. Let me share the results.

Let’s get to the basics: kendama is a traditional Japanese skill toy similar to ball and cup, except that it has 3 cups and a spike. This is how a kendama looks like:

Royal-Kendama-The-Void-Pink

The toy itself is several hundreds years old. Not only it is still played today, but its popularity is on the rise and there is a thriving community behind it.

When a game this old survives, it surely has some good game mechanics built in. The ones I found more interesting are onboarding, flow, appeal to player types and metagame.

Onboarding

Kendama is really straight forward to understand. There is a ball with a hole and there is a spike. Everybody gets that (although spike is not the first trick you should learn). Since there are 3 cups as well, people naturally gets into using those and not just the spike.

As players gets more familiar with the kendama, they usually realise of the other rims which you can also use that are not obvious on first sight.

There is an official list of tricks and levels that you can follow as a guided onboarding. These skill levels have been created by the Japanese Kendama Association. You can check the list of tricks and skills in the BKA page.

Most kendamas include some information of the basic tricks, sometimes the skill levels and even some websites with tutorials in the packaging.

While the skill levels are a great way to do your user journey right, they are limited.

The Internet in general and youtube in particular are key factors of the growth Kendama has seen in the recent years.

On one side, there is the amount of video tutorials but on the other side is exploring what is possible beyond the basic tricks by watching edits from players. Once you master the basics, you want new challenges and those are very easy to find online. Just in case, there are over 100 tricks with videos included in KendamApp.

In summary, onboarding for kendama was always easy, but the internet has played a large role in pushing the limits and helping people realise what is possible making the path to mastery easier to discover.

Flow

Flow is a state when the challenge you are trying to achieve is within your reach, but requires some effort. If it is to difficult, it generates anxiety, if too easy, boredom. When the difficulty is right you enter flow, and being in the flow feels great.

It is important to note that to stay on the flow the challenges must adapt to your skill as it improves.

flowYou can read more about flow in the article: Cognitive Flow: The Psychology of Great Game Design.

Kendama is really good at getting you in the flow and keeping you in. In this aspect it is not different from most juggling disciplines as it is self-driven and presents a continuous challenge.

Flow is the best way to improve any skill since while your skills improve and you are always presented with a challenge that is within your reach but requires you some effort to master it. Motivation and sense of progression are always there.

Let’s get into the 4 aspects that a game must have to provide flow:

Concrete goals with manageable rules

When you are trying to learn a new trick the goal is very specific. Tricks have very simple descriptions and are easy to understand, even the advanced ones.

There are many simple tricks. As you advance, tricks can be a composition of other tricks or a sequence or a modification, still concrete, still manageable rules.

Only demand actions that fit within a player’s capabilities

It is up to the player to choose the trick to practice. If a trick is too hard, the player can step down and try a simpler version, when a trick becomes too easy you can step up for something harder.

There is always a new trick or a variation that you can try. As it is all player-driven -unless training for a skill level- the risk of getting into the anxiety or boredom zones is very small.

There is a caveat that comes with being player driven: it needs motivation. This is actually solved by the game itself. Hitting a trick gives a dopamine high and the sense of progression helps making it addictive. On top of that, there is some exercise, so there are endorphins as well.

As we already covered on the onboarding section, there are hundreds of tricks, providing an almost unlimited challenge.

There are also different types of tricks so you can choose based on your preferences, aerial, string, balance, etc.

Clear and timely feedback on player performance

When you try a trick you either hit or you miss, the feedback is instantaneous and extremely clear (as happens with most real world games).

There are some important effects in the way you practice kendama too:

  • Each try is very quick.
    • It is easy to try again.
    • There is low impact of failure.
  • Anticipation of success: When you see the kendama getting right into place your brain anticipates the possibility of success and generates dopamine. This happens before the outcome is determined, so you get it even if you miss.
  • It is based on intrinsic motivation: you want to land the trick.

Remove any extraneous information that inhibits concentration

This is meant mainly for video games, but you’ll be surprised how much focus you can get when playing kendama for a trick you haven’t managed yet.

Types of Players

Bartle taxonomy of player types is a  well known classification of the different player types and I think kendama appeals to all of them.

385px-Character_theory_chart.svg

This classification is generally used to determine how a game is targeted to a certain type of players regarding how the interact with the game and with other players.

Achievers

These are players who prefer to gain “points”, levels, equipment and other concrete measurements of succeeding in a game.

For these players, the skill levels is a great fit. You have a level that you can improve and when you do so in front of an examiner, you get a diploma. You start from 10 Kyu to 1 Kyu (beginner levels) and then from 1st Dan up to 6th Dan.

Dave Mateo is a good example of a player of this type. He keeps pushing himself to add more spins on the tricks, like a 11-turn jumping stick.

The main difference with killers is that killer want to beat other players while achievers battle themselves to improve.

Explorers

Explorers prefer discovering areas, creating maps and learning about hidden places.

When it comes to kendama, explorers naturally go into freestyle, trying new combinations of tricks and pushing towards finding their own personal style, many times inventing new tricks.

Iijima Hiroki is an explorer, he went on and created his own style. You can learn more about him on: Portrait of a Kendama Samurai.

Socializers

There are a multitude of gamers who choose to play games for the social aspect, rather than the actual game itself. They gain the most enjoyment from a game by interacting with other players.

The kendama community is quite large and attending real life events usually means meeting other players in person whom you have only interacted with online. There is a lot of people that go to those just to be with other players, no need to even do a trick.

Jake Wiens is a socializer. He runs a weekly meetup in San Francisco just to meet people and expand the kendama love.

Killers

They thrive on competition with other players.

While this is the least common part of kendama, there are competitions like the Japanese Kendama Open, Kendama World Cup, European Kendama Open, and many more where you can compete against very good players and win prizes.

In addition to that, there are small competitive games that you can play on a group.

A good example of a killer is Tomoya Mukai. He is at his best when competing on stage.

Metagame

Kendama is not just about doing tricks. There are other aspects of it that also appeal to players.

  • Collecting: There are many kendama brands, some of them are discontinued and considered collector pieces. Players start getting more and more kendamas to try the differnet brands and before you know, you have started a collection.
  • Customizing: There is a large community of people that paint/tune their own kendamas to make them special, some of them sell them afterwards.
  • Modding: A kendama is composed of 3 parts, each one of them has a weight. How the weight is distributed impacts how a kendama plays. Some players mix and match several kendamas until they have one with the perfect distribution for their taste.

Putting all this into place, I’d say kendama fits very well into modern game design and it is only logical that its popularity has been on the rise and that players get hooked easily.

Putting Platty Soft on hold

This post is part announcement, part post-mortem and part rant, but which post-mortem does not have its share of ranting?

The announcement

I’ll have started working for Daqri recently, and since Platty Soft is a one man company, it will be put on hold. This means that all the projects will receive only maintenance releases which I’ll do in my spare time, instead of as part of my daily job as I’ve been doing until now.

For MTG Tracker, this means that new sets will be added, but most likely there won’t be any new features (maybe some small ones, but don’t hold your breath).

The post-mortem

I’ve been running Platty Soft for 4 years. My original idea was to do contractor work to guarantee an income and then dedicate the rest of the time to my own projects, namely apps and games.

Ideally, those apps and games would gradually generate more income so I could do less contracting work and more of my own projects until I could just work on my projects full time.

Obviously, it has not worked as expected.

727446

Along these years I’ve improved MTG Tracker a lot, released Codemon and updated it, co-created AppAffinity (now discontinued) and also published a few small apps like KendamApp, Learn To Hoop and Spin Off.

A while ago it was already clear for me that the plan was not going to work. In fact I started advising people to not go indie (or at least be aware of the difficulties) already at the end of 2013 with the talk The (mobile) Indie Game Developer Survival Guide. You can watch the video from the talk at DroidConNL.

All in all, the indie road is a very tough one. There has been a perfect storm forming for some time now which I’ve been calling “The Appocalypse: the apocalypse of the apps”. Funny enough, people are now talking about Zombie apps, which fits into the theme. I already talked about the Gold (but not the rush) being over also in 2013.

However, things have gone from bad to worse. Over the past year, the monthly gross income I was getting from all my apps and games combined has been decreasing at an alarming rate. To the point of it being 1/3 of what it was a year ago. That is, going from 1200 EUR a month to just 400.

With the figures of one year ago, it was hard to consider that I was making “a living” out of it, but with the current ones it is definitely impossible.

Obviously all my projects are in long tail now, but that long tail is getting thinner in mobile.

To put it in perspective, there was only one time when the income was lower: the first month when I released MTG Tracker Pro. Yes, back to 2011, and at that time MTG Tracker was only one app I had.

But I know what you are asking. Why did I kept doing it? Well, there were several reasons:

  • I had an international move planned from Amsterdam to Dublin, so I didn’t want to get a full time job to just quit short after.
  • I had a nice contractor work with Squla, which is an awesome startup, that allowed me great flexibility.
  • Contractor work pays well, so I didn’t need to change that. I just stopped making new projects.
  • Once I was already decided to get a full time job, I got contacted by Packt to write a book about Game Development for Android, which had kept me busy for a few extra months.
  • I hadn’t found a really interesting full-time job until past week.

Now, let’s get ranting

The income from Google Play comes mostly from MTG Tracker, and then a little bit from Codemon.

MTG Tracker has never recovered the top search spot it had a few years ago (after it was taken down by WoTC), and that is the largest factor in its income decrease. I am not “demanding” to be at the top of the search, but right now the app is around position 20 (depending of the search criteria) and for at least 15 of the apps that appear before it MTG Tracker has:

  • Better average rating
  • More downloads
  • More ratings
  • Better ratings per download ratio
  • Updates more often

This rant about the crap search results on Google Play is not new. It was even one of the reasons I made AppAffinity back in 2012! And it has not improved. If something, it has become worse.

I have talked about this with people that works at Google Play and they seem to agree with me, I rank good in all the factors that are considered important, yet I am appearing very low in the search. Not being one of the top 5 apps is close to not existing. I am sure I got most of my downloads from word of mouth.

In the case of Codemon, while it has a decent conversion rate and ARPPU, the volume has been quite small to pay the time I invested in building it, but at least it paid all the other people that I hired to make it happen. Still, the past 2 months have seen a huge drop in In-App Purchases.

I have one more project in progress: GeoDefense. It is a cooperative GPS based tower defense, currently in private beta, and given the current situation, it may never get out of that state.

Now, talking about new projects. I considered for a while making games based on third party IP. I talked with the webcomics Spindrift and El Sistema D13, but ultimately I was not sure any of those projects was going to even cover expenses, and I was feeling tired.

The thing is, unless you put serious effort (and money) in promotion, your app is going to disappear in the swarm that Play Store is (and the App Store, for what is worth). Your only chance is to go for a very niche market, like Codemon does for barcode scanning games, and then you still need to be lucky and worry about many things.

So, it is clear to me that Google and Apple need to take action and fix the search of their app stores or you’d keep reading this stories from more and more indie developers. It has been a winner takes all for a long while, but it is evolving to have less winners and they take even more.

Let’s put everything together: Increasing user acquisition cost, flooded market, huge marketing budget for the ones on top to remain on top, almost non-existing visibility for the rest, a race to bottom in prices… Do I need to continue?

It is just not worth it.

Ultimately the reason why I decided to move on was my feelings each time I thought about a new possible project on the last year.

When thinking about a new project I was not feeling excitement or joy, but despair. Despair about how I was going to deal with visibility, discoverability and promotion. And that was not even considering the headaches of monetization. Sadly, this is a quite common feeling among indie developers today.

The only time I have not felt that way is when I worked on the  CounterClockWise Watchface I made for Android Wear a few weeks ago. The reason is that I built it for myself and didn’t care about monetization, downloads, reach and so on. I just did it because I wanted to and it was merely 2 days of work. It got 50 downloads in a week, actually, more than I thought it would.

That’s not how I want to feel about a new project. And if what it needs to be done to get interested in them again is to make them side projects and don’t give a shit about who finds them, so be it.

keep-calm-and-don-t-give-a-shit-46

But now, for the foreseeable future I’ll be doing some innovation at Daqri, and I’m really excited about it.

Book: Mastering Android Game Development

I’ve been busy the past few months writing a book titled Mastering Android Game Development with focuses on using the Android SDK for building games. The book is now available online.

4757_MockupCover_Normal

If you are an intermediate-level Android developer who wants to create highly interactive and amazing games with the Android SDK, then this book is for you.

This book is a progressive, hands-on guide to developing highly interactive and complex Android games from scratch. You will learn all the aspects of developing a game using a space shooter game as the example that will evolve with you through the chapters. You will learn all about frame-by-frame animations and resource animations. You will also create beautiful and responsive menus and dialogs and explore the different options for playing sound effects and music in Android, the basics of creating a particle system, how to configure and use Google Play Game Services on the developer console and port our game to the big screen.

Snapping items on a horizontal list

The Problem

We want to have a horizontal scrolling view that holds several items and that snaps to the selected one once the scroll stops, placing it centered on the screen.

Essentially something that works like this:

ezgif.com-crop

In old versions of Android we had the Gallery class that allowed us to do something similar, but it has been deprecated. It is also similar to what you can do with a ViewPager, but those are normally designed for full width Fragments.

The Solution (TL;DR)

What we will do is to listen for the event of scrolling stop on the list. Then we will check which item is currently in the middle of the screen and make the list scroll until it is positioned on the center.

The code for this example is available on GitHub as the project SnappingList

The detailed solution

You probably want something more detailed, so let’s get into it.

Some considerations

We are going to use a RecyclerView because the OnScrollListener allows us to know the pixels that have been scrolled, and that is not possible with a standard ListView. Its OnScrollListener does not provide enough information. Besides, a RecyclerView is more fancy.

We will know the with of the items on the list beforehand. This is very important to be able to calculate the center of the screen and which item is the selected one.

We are going to have two extra items in the list, one at the beginning and one at the end, to be able to position the first and last items centered on the screen. The width of these extra items need to be large enough for it.

The list items

As we mentioned, we will have two different items in the list. The normal ones and the ones we will add at the beginning and the end.

The layout for the extra items is defined in the layout file list_item_padding.xml and looks like this:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/padding_item_width"
    android:layout_height="match_parent">
</FrameLayout>

On the other hand, the normal items we are going to use are just a square shape in the background and a number in the center. They are defined in the layout list_item.xml which looks like this:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/item_width"
    android:background="@drawable/bgr"
    android:layout_height="match_parent">

    <TextView
        style="@android:style/TextAppearance.DeviceDefault.Large"
        android:id="@+id/item_text"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</FrameLayout>

These layouts have the width defined as a dimension. This allows us to tweak it for different screen sizes and also to be able to read the width in code without the need of waiting for the views to be measured. The values we are using for the example are:

<resources>
    <dimen name="item_width">200dp</dimen>
    <dimen name="padding_item_width">300dp</dimen>
</resources>

The Adapter

Then, we are going to create an adapter that just adds the two extra items. We call it ExtraItemsAdapter and the code is like this:

public class ExtraItemsAdapter
        extends RecyclerView.Adapter<ViewHolder> {

  private static final int VIEW_TYPE_PADDING = 1;
  private static final int VIEW_TYPE_ITEM = 2;

  private final int mNumItems;

  public ExtraItemsAdapter(int numItems) {
    mNumItems = numItems;
  }

  @Override
  public int getItemCount() {
    return mNumItems+2; // We have to add 2 paddings
  }

  @Override
  public int getItemViewType(int position) {
    if (position == 0 || position == getItemCount()-1) {
      return VIEW_TYPE_PADDING;
    }
    return VIEW_TYPE_ITEM;
  }

  @Override
  public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    // create a new view
    if (viewType == VIEW_TYPE_ITEM) {
      View v = LayoutInflater.from(parent.getContext())
        .inflate(R.layout.list_item, parent, false);
      return new ViewHolder(v);
    }
    else {
      View v = LayoutInflater.from(parent.getContext())
        .inflate(R.layout.list_item_padding, parent, false);
      return new ViewHolder(v);
    }
  }

  @Override
  public void onBindViewHolder(ViewHolder holder, int pos) {
    if (getItemViewType(pos) == VIEW_TYPE_ITEM) {
      // We bind the item to the view
      holder.text.setText(String.valueOf(pos));
    }
  }
}

The adapter receives a number that says how many elements to display as a parameter of the constructor.When asked for the item count it will return that number+2.

When asked to create a view, it uses the item view type to check if it is either a padding or a normal item.

To use this on a real world example, you should pass a list of items instead of just an integer and do proper binding inside onBindViewHolder.

Controlling the RecyclerView

Now that the basics are set, we can move into the really interesting part: handling the RecyclerView.

As we mentioned before, the idea is that when the scroll is stopped, we calculate which item is the current position, then, make the list scroll to that position.

To be able to calculate all that, we need some initialization, mainly to know the width of the items (both normal and extra ones) and also the padding needed, which is calculated based on the screen width and the item width.

We also keep track of the current amount of scroll -in pixels- of the RecyclerView in the allPixels variable.

All this can be done inside the onCreate method of the Activity.

Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
firstItemWidth = getResources().getDimension(R.dimen.padding_item_width);
itemWidth = getResources().getDimension(R.dimen.item_width);
padding = (size.x - itemWidth) / 2;

allPixels = 0;

We also need to initialize the RecyclerView by creating and setting a LayoutManager.

final RecyclerView items = (RecyclerView) findViewById(R.id.item_list);
LinearLayoutManager itemslayoutManager = new LinearLayoutManager(getApplicationContext());
itemslayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
items.setLayoutManager(itemslayoutManager);

Finally, we set the OnScrollListener to the RecyclerView:

items.setOnScrollListener(new RecyclerView.OnScrollListener() {

  @Override
  public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    super.onScrollStateChanged(recyclerView, newState);
    synchronized (this) {
      if (newState == RecyclerView.SCROLL_STATE_IDLE) {
       calculatePositionAndScroll(recyclerView);
      }
    }
  }

  @Override
  public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    super.onScrolled(recyclerView, dx, dy);
    allPixels += dx;
  }
});

This listener does two things:

  • When the scroll stops (the scroll state changes to SCROLL_STATE_IDLE), it calls calculatePositionAndScroll to make the list snap.
  • When the list scrolls, it updates the value of allPixels.

Then we have the utility method calculatePositionAndScroll which is where the action is:

private void calculatePositionAndScroll(RecyclerView recyclerView) {
  int expectedPosition = Math.round((allPixels + padding - firstItemWidth) / itemWidth);
  // Special cases for the padding items
  if (expectedPosition == -1) {
    expectedPosition = 0;
  }
  else if (expectedPosition >= recyclerView.getAdapter().getItemCount() - 2) {
    expectedPosition--;
  }
  scrollListToPosition(recyclerView, expectedPosition);
}

private void scrollListToPosition(RecyclerView recyclerView, int expectedPosition) {
  float targetScrollPos = expectedPosition * itemWidth + firstItemWidth - padding;
  float missingPx = targetScrollPos - allPixels;
  if (missingPx != 0) {
    recyclerView.smoothScrollBy((int) missingPx, 0);
  }
}

To calculate the position we use the values of allPixels, padding,  firstItemWidth and itemWith. Note that we are rounding the result.

We check for the special cases of the first and last items -the extra ones- and then tell the list to scroll to that position.

Scrolling to the position calculates the point in which the list needs to be positioned for a specific item, subtracts the value of allPixels from it and tells the list to do a smooth scroll.

Finally, we initialize the adapter with the number of items we want to be displayed.

ExtraItemsAdapter adapter = new ExtraItemsAdapter(NUM_ITEMS);
items.setAdapter(adapter);

While the code works, there are a couple of situations we want to fix to make it nicer.

Finishing touches

The first problem is that rotation does not work properly. RecyclerView does remember the state, but the value of allPixels gets reset when the Activity is destroyed. This is very easy to fix, we just need to save and restore it using the methods from the Activity.

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  allPixels = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS);
}

@Override
protected void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  outState.putFloat(BUNDLE_LIST_PIXELS, allPixels);
}

The other finishing touch is to make the list start in a selected position, since now it starts at the beginning of the list, where an extra item is.

If we call calculatePositionAndScroll directly it will create a wrong state where the value of allPixels is incorrect because the view has not been completely measured yet. To fix that we have to call that method once the layout has been completed and we do that via the OnGlobalLayoutListener class.

ViewTreeObserver vto = items.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    items.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    calculatePositionAndScroll(items);
  }
});

I recommend that you place this code inside onResume (as oposed to onCreate) to ensure that it is called after onRestoreInstanceState. Although the layout is most likely never completed before onResume is called, the logic of positioning the list to the right position makes more sense inside onResume.

Final considerations

There are some considerations for this to be used on real world applications. Nothing major, but still worth commenting:

  • We chose the size of the items for an optimal view on a Nexus 5 in landscape. You should consider qualifying the dimensions based on the screen smallestWidth.
  • Removing the ViewTreeObserver has two different methods, one that is valid for minSDK < 16 and another one for SDK 16 on. This example only considers minSDK 16.

You can see all the code in the SnappingList project on GitHub.

Google Developer Expert

I can proudly say that since January, I have become a Google Developer Expert for Android (GDE in short). The first -and at the moment only- one in Ireland.

Google Experts are a global network of experienced product strategists, designers, developers and marketing professionals actively supporting developers, startups and companies changing the world through web and mobile applications.

As part of the activities of the GDE program I spent some time at the Office Hours during Google I/O helping people and I also gave a 20 minutes talk in the sandbox about Playful Design (slides coming shortly).

MTG Tracker 6.1 – Supporting casual formats

We just published version 6.1 of MTG Tracker, and is all about casual formats.

We were the first ones to support EDH more than 3 years ago. Long before it got the name changed to Commander. We love casual formats, that’s why It was time to include some other formats that are getting more and more popular lately.

  • Tiny Leaders is getting steam quickly, it is a smaller Commander mode with cards of converted mana cost of 3 or less, decks of exactly 50 card, 25 life points and no Commander damage. Check it out at the official site.
  • Pauper is a format when you can only use commons. Very cheap to build, lots of fun. It has been a format for Magic Online for a while.
  • Duel Commander is a modified version of Commander designed to play one-on-one. Different ban list and 35 life. Check the official site for details.

These formats have now a slot under the formats section, a deck type that checks for invalid cards and in the case of Tiny Leaders and Duel Commander, a game mode.

New Formats

Also, business as usual, the latest set “Dragons of Tarkir” has been added to the app. The cards from “Duel Decks: Elspeth Vs Kiora” have been also added.

Enjoy.

MTG Tracker 6.0 – Material Design

MTG Tracker has been updating a lot, usually UI changes are made gradually, but sometimes they are big. This update is one of the big ones, and I want to take the chance to look back to all the journey.

MTG Tracker UI journey

The app started with a single screen with a life counter for 2 players. Nothing really flashy. New features were added as tabs, Mana Pool, then Stats, then Decks. Tabs were cool on the early days of mobile apps.

1st_versionThen the ActionBar was presented, and with that, MTG Tracker moved to ActionBar + Dashboard. That is the other time the app had a considerable redesign.

dashboard_4.2

Moving forward, and adding features, at some point the app had so many features that a Dashboard was not enough. Also, a new pattern was emerging: Menu Drawers. So, we added one, not a big change, but still important.

02_dashboard_with_options

MTG Tracker with Material Design

And with this, we get to Material Design, the big push from Google with Lollipop. We are adopting quite some new patterns. An image is worth a thousand words.

material design_small

Toolbar replaces ActionBar

The new toolbar has a more obvious drawer icon and the icon is removed from the bar, leaving some space for options and titles. It also has a very nice animation when opening the menu drawer.

There used to be a blue bar under the action bar for extra options on some screens. This is now part of the toolbar, giving am ore consistent look and feel.

Bonus points: Decks also have a Quick Return Header pattern, which hides part of the header when you scroll down and shows it again when you scroll up.

Floating Action Button

In most of the screens, there is a floating action button for the most prominent action (like add a new deck or add cards to a deck or a list).

Popup Menu replaces Quick Action Menu

The extra options for elements on a list were displayed with a quick action menu, which was cool when we started using it, but now everyone is moving to the popup menu with 3 dots, which is also more clean, so that has changed as well.

New simpler, cleaner icons

We also changed the icons of the home screen and the drawer to simpler, more clear versions of them.

Search is now an option on the menu drawer as well as in the main screen. It is much more accessible there than from the icon on the action bar.

device-2015-01-12-144523

Deck Edit is no more

Deck edit has disappeared. It was confusing and was not adding much value. The decks can be edited now at any time. As it was for Card Lists and Trades.

Something else?

I want to change some other parts of the user flow, but that will have to wait another update. No spoilers on that one.

But of course, there is a bit more:

  • Added cards from Fate Reforged
  • EDH banned list has been updated.
  • Fixed a problem when adding the card “_______” to a deck or list.