Yet Another [à compléter]

Android Material Image Loading

Among the plethora of seemingly impossible things in the Material Design specification, there is one that piqued my curiosity this morning.

It’s something everybody does at least once, if not everywhere, in his app which is loading and transitioning images.

Now to make this more glamour we generally all went with the classical, battle-tested approach of shifting the opacity of our image container to announce the change (that was even one of my first Android tip).

But the new approach taken by Material and detailed in the “Loading Images” section goes a lot further than this by also throwing some image levels manipulation in the mix.

The process is summarized in the following graph:

It outlines a 3-steps process where a combination of opacity, contrast/luminosity and saturation is used in concert to help salvage our poor users eyesight.

Android has always supported image manipulation through the ColorFilter class that can be set on any drawable and on some view classes (ImageView for instance).

When used with its 4x5 ColorMatrix-based implementation ColorMatrixColorFilter, you can virtually implement any kind of image transformation provided you grok the way vector/matrix multiplication work (head to the ColorMatrix documentation for the resulting equations).

The only thing that was a limiting factor is that a filter is initialized once and for all. If you want to change the effect you need to create a new instance of the filter (which initialize a native counterpart) and replace the old version with the new one.

Obviously this is a complete downer when you do animations because you don’t want to stress out the GC and GPU during those phases by creating a new instance of the filter at every refresh step.

But thanks to Lollipop and the fact that @hide can’t stop us, there is actually a new public method allowing us to update a filter after the fact.

Armed with this knowledge, we can now set out to create a custom ITypeEvaluator to tweak our filter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Refer to http://developer.android.com/reference/android/graphics/ColorMatrix.html
// for a list of the matrix indexes
class AlphaSatColorMatrixEvaluator : Java.Lang.Object, ITypeEvaluator
{
	ColorMatrix colorMatrix = new ColorMatrix ();
	float[] elements = new float[20];

	public ColorMatrix ColorMatrix {
		get { return colorMatrix; }
	}

	public Java.Lang.Object Evaluate (float fraction, Java.Lang.Object startValue, Java.Lang.Object endValue)
	{
		// There are 3 phases so we multiply fraction by that amount
		var phase = fraction * 3;

		// Compute the alpha change over period [0, 2]
		var alpha = Math.Min (phase, 2f) / 2f;
		elements [19] = (float)Math.Round (alpha * 255);

		// We substract to make the picture look darker, it will automatically clamp
		// This is spread over period [0, 2.5]
		const int MaxBlacker = 100;
		var blackening = (float)Math.Round ((1 - Math.Min (phase, 2.5f) / 2.5f) * MaxBlacker);
		elements [4] = elements [9] = elements [14] = -blackening;

		// Finally we desaturate over [0, 3], taken from ColorMatrix.SetSaturation
		float invSat = 1 - Math.Max (0.2f, fraction);
		float R = 0.213f * invSat;
		float G = 0.715f * invSat;
		float B = 0.072f * invSat;

		elements[0] = R + fraction; elements[1] = G;            elements[2] = B;
		elements[5] = R;            elements[6] = G + fraction; elements[7] = B;
		elements[10] = R;           elements[11] = G;           elements[12] = B + fraction;

		colorMatrix.Set (elements);
		return colorMatrix;
	}
}

Here is how you can set it up:

1
2
3
4
5
6
7
8
9
10
11
12
var imageView = FindViewById<ImageView> (Resource.Id.image);
var drawable = (BitmapDrawable)Resources.GetDrawable (Resource.Drawable.monkey);
var evaluator = new AlphaSatColorMatrixEvaluator ();
var filter = new ColorMatrixColorFilter (evaluator.ColorMatrix);
drawable.SetColorFilter (filter);

var animator = ObjectAnimator.OfObject (filter, "colorMatrix", evaluator,
                                        evaluator.ColorMatrix,
                                        evaluator.ColorMatrix);
animator.Update += (sender, e) => drawable.SetColorFilter (filter);
animator.SetDuration (2500);
animator.Start ();

And here is the result:

It’s probably safe to assume Google will come out with an official way for this pattern e.g. in a subsequent support library update. In the meantime, you can get cracking with this version.

Transitioning to Infinity

I remember clearly when I sat down at I/O this year, watching the introduction of Material design in the keynote unfold and how, at the time, I told myself there was no way we could implement all of this in Android.

One such aspect that I was highly skeptical of until very recently was the concept of animated transition.

We actually talked about this during our Material Design Evolve session with James and at the time of that talk, the only facility we had been given to do it was by keyframe animations which is incredibly clunky to maintain.

Meet our new private implementation friend, PathDataEvaluator.

This evaluator is able to understand the path definition used by vector drawable and create intermediary versions of it.

This means that given two specific path of a vector drawable, we can use an object animator to, not only animate transformation or style like we have seen before, but also the actual pathData of the vector itself.

Now before you get too excited, it’s not a miracle evaluator. There are two very strong requirements for it to work properly:

  • The path command list needs to be of the same size
  • Each command in that list needs to be of the same type

Basically, the evaluator treats the path data as an array of float extracted from each command parameters and use that to interpolate inbetweeners.

This essentially mean that when crafting your paths, you may need to get a bit creative in the amount of nodes you use in order to make those requirements fit.

As an example, this is what we are going to make:

Intuitively, based on our previously mentioned requirements, we can see there is already fundamental problem when we think of alternating between a standard play icon and a standard pause icon: they don’t have the same amounts of subparts (2 rectangles for pause, 1 triangle for play).

In addition, the pause icon rectangles necessitate 4 control points for each subpart whereas the triangle of the play icon only requires 3.

That’s where the tweaking part comes in, following are how I created those shapes in my editor to solve both issues (nodes handles are highlighted):

The work was actually focused on the play icon by first separating the triangle shape in two right sub-triangles and then adding a (normally unnecessary) extra node onto its hypotenuse.

Each node is also numbered as the order in which the path is drawn has an impact on the look and feel of the animation. With those numbers, you can visualize in your head how the nodes are going to move across the canvas to alternate between the two shapes.

Without further ado, here are the vector drawables we are using:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- In your strings.xml file -->
<resources>
	<string name="pause_vector_path">M12,10L20,10L20,38L12,38ZM28,10L36,10L36,38L28,38Z</string>
	<string name="play_vector_path">M16,24L38,24L27.3,30.8L16,38ZM16,10L27.3,17.2L38,24L16,24Z</string>
</resources>

<!-- Resources\drawable\ic_play.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:height="48dp"
	android:width="48dp"
	android:viewportHeight="48"
	android:viewportWidth="48">
	<group>
		<path android:name="d"
			android:fillColor="@color/white_primary"
			android:pathData="@string/play_vector_path" />
	</group>
</vector>

<!-- Resources\drawable\ic_play.xml is identical except
     replace pathData with the second string resource -->

The path data themselves are stored as a string resources. This makes it easier to use them in different places without having to copy and paste the entire thing.

Notice also how my commands are ordered (M stands for moveto and L for lineto) to reflect what the editor diagram showed and also how each command type maps to the same command type between the two definitions.

Since we want to create a transition we need a container for this that handle state using the Android standard state tracking mechanism. This type of container is called a state-list and, as we already covered in the Evolve video above, we now also have an equivalent version that supports animated transition between state: AnimatedStateListDrawable.

Here is my definition for the play/pause button state (based on the state_checked presence):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize="true">
    <item
        android:drawable="@drawable/ic_pause"
        android:state_checked="true"
        android:id="@+id/pause_state" />
    <item
        android:drawable="@drawable/ic_play"
        android:id="@+id/play_state" />
	<transition android:fromId="@id/play_state" android:toId="@id/pause_state" android:reversible="true">
		<animated-vector android:drawable="@drawable/ic_play">
			<target android:name="d" android:animation="@anim/play_pause" />
		</animated-vector>
	</transition>
</animated-selector>

You can watch the Evolve video again for a longer explaination of how this works but essentially we are now naming our various <item /> elements with an id so that we can reference them for animation in <transition /> blocks.

Inside the transition, you’ll find our known companion AnimatedVectorDrawable referencing this object animator definition:

1
2
3
4
5
6
7
<!-- Resource\anim\play_pause.xml -->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
	android:duration="250"
	android:propertyName="pathData"
	android:valueFrom="@string/play_vector_path"
	android:valueTo="@string/pause_vector_path"
	android:valueType="pathType" />

Key aspect is the valueType attribute that is set to pathType (the only actual way to can reference the non-public PathDataEvaluator class) and the property name set to pathData to change the corresponding member in the vector drawable. For the rest, you can tweak this like any other animator.

Last piece is to show this transition in something. The default Android framework offers a CheckedTextView for those cases but no CheckedImageButton (because using Checkbox can in some occasions result int a bit of pain with positioning). It’s easy to implement though:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class CheckedImageButton : ImageButton, ICheckable
{
	static readonly int[] CheckedStateSet = {
		Android.Resource.Attribute.StateChecked
	};
	bool chked;

	// Add standard constructors

	public override bool PerformClick ()
	{
		Toggle ();
		return base.PerformClick ();
	}

	public void Toggle ()
	{
		Checked = !Checked;
	}

	public bool Checked {
		get {
			return chked;
		}
		set {
			chked = value;
			RefreshDrawableState ();
			Invalidate ();
		}
	}

	public override int[] OnCreateDrawableState (int extraSpace)
	{
		var drawableState = base.OnCreateDrawableState (extraSpace + 1);
		if (Checked)
			MergeDrawableStates (drawableState, CheckedStateSet);
		return drawableState;
	}
}

You can use this class and simply set the android:src attribute to the animated state-list drawable.

Also don’t forget to set the android:background of the button to use the theme value ?android:selectableItemBackgroundBorderless to get the nice ripple effect on touch.

To conclude, you can still use the keyframe approach when doing animated state-list transition if you actually want to invest the time crafting each intermediary state maybe because it does a complex movement that can’t be naively interpolated.

But if you don’t need to, we now have an easy, fast and cumberless way to create beautiful state transitions using vector drawables.

Animating to Infinity

In my previous post, I covered the basics of a new type of Android drawable introduced in Lollipop for scalable graphics.

In and by itself this is already a very powerful new addition. But to see what else we can do with this, let’s reuse the vector drawable example from the previous post:

1
2
3
4
5
6
7
8
9
10
11
<vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:height="96dp"
	android:width="96dp"
	android:viewportHeight="48"
	android:viewportWidth="48" >
	<group>
		<path
			android:fillColor="@color/black_primary"
			android:pathData="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z" />
	</group>
</vector>

I didn’t touch on it at the time but you may have noticed that our path element is actually contained inside another element called a group.

For the sake of displaying vector drawable, this is not a very interesting element. Now if I paste below the list of other attributes you can set on <group/>, you should see something emerging:

Extra group attributes
rotation, scaleX, scaleY, translateX, translateY.

You will have surely recognized those are the same attributes we use to manipulate our View instances when animating them.

Enter, Animated Vector Drawable.

Animated Vector Drawables are actually more of a meta-type bridging several other pieces together much like a State List Drawable (and like the later, they also are drawables themselves).

Here is an example of an animated vector drawable that we will use later on (also stored in your drawable resource folder):

1
2
3
4
5
6
7
8
9
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:drawable="@drawable/bluetooth_loading_vector" >
	<target
		android:name="circleGroup"
		android:animation="@anim/circle_expand" />
	<target
		android:name="circlePath"
		android:animation="@anim/circle_path" />
</animated-vector>

Animated Vector Drawables are declared using the <animated-vector/> element. The first thing you need to tie in is which vector drawable the animated version is going to use as a base, this is set in the android:drawable attribute at the top level.

The rest of the file contains a bunch of different <target/> elements, those are where we are going to set up which animations are ran and on what part of the vector drawable they will be ran.

But first, here is the definition of the bluetooth_loading_vector referenced in the animated vector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8" ?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:height="64dp"
	android:width="64dp"
	android:viewportHeight="192"
	android:viewportWidth="192" >
	<group>
		<path
			android:fillColor="@color/vector_foreground_color"
			android:pathData="M87.3996,113.161L85.3665,112.191L85.3665,23.517L87.3996,22.5479L143.293,67.8542L87.3996,113.161ZM99.9498,84.2151L120.134,67.8542L99.9498,51.4934L99.9498,84.2151Z" />
		<path
			android:fillColor="@color/vector_foreground_color"
			android:pathData="M87.4147,169.459L85.3665,168.484L85.3665,79.6589L87.4147,78.684L143.277,124.071L87.4147,169.459ZM99.9498,140.484L120.15,124.071L99.9498,107.659L99.9498,140.484Z" />
		<path
			android:fillColor="@color/vector_foreground_color"
			android:pathData="M43.8495,129.345L103.262,81.2217L112.186,92.2392L52.7734,140.362L43.8495,129.345Z" />
		<path
			android:fillColor="@color/vector_foreground_color"
			android:pathData="M43.8495,62.2562L103.262,110.379L112.186,99.3616L52.7734,51.2387L43.8495,62.2562Z" />
	</group>
	<group
		android:name="circleGroup"
		android:pivotX="96.0"
		android:pivotY="96.0"
		android:rotation="0">
		<path
			android:name="circlePath"
			android:strokeColor="@color/vector_foreground_color"
			android:strokeWidth="16"
			android:strokeAlpha="1"
			android:trimPathEnd="0.7"
			android:pathData="M96,5.56405C145.946,5.56405 186.436,46.0536 186.436,96C186.436,145.946 145.946,186.436 96,186.436C46.0536,186.436 5.56405,145.946 5.56405,96C5.56405,46.0536 46.0536,5.56405 96,5.56405Z" />
	</group>
</vector>

And what it looks like when rasterized:

This vector drawble is a bit longer than the previous one, but there are two main things to notice:

  • The drawable is arranged in two distinct groups
  • One group and one path have a name attached, mapping back to the animated vector’s target elements

The way we have layed-out and named our vector drawable means we can now directly reference the relevant parts we want to manipulate.

For instance in our case, we are only interested in the ring around the Bluetooth logo which is why only this piece is actually named in our vector drawable.

To close the loop (so to speak), here are the two animation definition files referenced by the animated vector drawable (placed each in your anim resource folder):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!-- Resources\anim\circle_expand.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="scaleX"
		android:valueFrom="1.0"
		android:valueTo="2.5"
		android:valueType="floatType"
		android:repeatCount="-1" />
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="scaleY"
		android:valueFrom="1.0"
		android:valueTo="2.5"
		android:valueType="floatType"
		android:repeatCount="-1" />
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="rotation"
		android:valueFrom="0"
		android:valueTo="360"
		android:valueType="floatType"
		android:repeatCount="-1" />
</set>

<!-- Resources\anim\circle_path.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="trimPathEnd"
		android:valueFrom="0"
		android:valueTo="1"
		android:valueType="floatType"
		android:repeatCount="-1" />
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="strokeAlpha"
		android:valueFrom="1"
		android:valueTo="0"
		android:valueType="floatType"
		android:repeatCount="-1" />
</set>

Since this is meant as a continous animation, we must ensure that the repeat count of each animator is -1 (a synonym for ‘infinite’) so that the process runs forever.

In the remaning bits, we are simply animating a bunch of property on both the group (for transformations) and on the path (for the stroke style) like we would on a normal view.

The last thing to do is load the animated vector drawable into a continuously animating View, typically a ProgressBar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/colorPrimary"
    android:padding="48dp"
    android:clipChildren="false"
    android:clipToPadding="false">
    <ProgressBar
        android:indeterminate="true"
        android:indeterminateDrawable="@drawable/animated_bluetooth_loading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />
</FrameLayout>

And we arrive at this result:

Next up, we will see how we can apply some of this to make terrific transitions.

Scaling to Infinity

When I make an app, I spend my time between two worlds.

In the first, I produce sequences of meaningul characters for a virtual machine in your favorite IDE, Xamarin Studio. In the second, I use my best friend Inkscape (and lately also Affinity) to help me think, iterate and produce the front face of my app.

The main challenge of this form of development is to find the best way to ease the content transition between both worlds.

In Android, what’s essentially the apocalypse of screen form-factor makes it particularily painful to work with that visual assets part of your app.

Where the world was happily spread between ldpi, mdpi and hdpi a few years back, today we just don’t know how many ‘x’ we are going to be able to fit in front of ‘hdpi’ (at the time of this writing we reached xxxhdpi).

I have actually already talked about this subject and, at the time, released my own library to try to tackle this issue.

But today we can finally rejoice as Google gave developers a blessed way to create unlimited scalable graphics with the recent release of Lollipop (or Android 5.0).

This new types of drawable is unoriginaly called Vector Drawables and, as with any other drawable, they are natively understood by the Android toolchain which make their use as seamless as feasible (i.e. you can reference them in layouts, styles, acquire them through Resources.GetDrawable, …).

This is an example of a vector drawable (store them in your Resources/drawable folder):

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8" ?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:height="96dp"
	android:width="96dp"
	android:viewportHeight="48"
	android:viewportWidth="48" >
	<group>
		<path
			android:fillColor="@color/black_primary"
			android:pathData="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z" />
	</group>
</vector>

Which draws this well known media player action icon:

The beef of a vector drawable is in its various <path/> elements and more precisely its pathData attribute. As arcane as it may look, it actually defines the full blown shape of the icon in the very concise SVG Path data format.

Don’t worry too much though, you won’t have to write that down by yourself (you may need to tweak it though for more funky things but more on that in a later post).

Since it’s based on the same specification than used in SVG itself, as long as you can grab a SVG version of your graphic you will directly be able to copy and paste it into a vector drawable XML.

As a matter of fact, the icon example that I pasted above came from Google’s official material design icon pack which also contains SVG for every icon:

1
2
3
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
    <path d="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z"/>
</svg>

Notice also how the viewportHeight and viewportWidth values in the vector drawable reflect what’s in the width/height/viewBox parameter of the SVG. As long as those values are correctly defined, you can play with the android:width and android:height part of your drawable the way you want.

Together with the new general tinting facility in Lollipop, you can now iterate over your assets even faster without leaving your IDE and without having to re-generate 6 variants of your graphics.

Ada Lovelace Day 2014

Today is Ada Lovelace day, a special occasion to recognize women in our community. I want to take this opportunity to also highlight someone exceptional I have known for a while.

Her name is Andrea Magnorsky.

Andrea Magnorsky

Andrea is an Irish .NET programmer. She’s currently following one of her passion – game development – and is closing production with her studio Digital Furnace (ex-BatCat) on their next title Onikira.

To say that Andrea is a community person is an understatement. In the span of a few years she has not only created popular event/group like Global Gamecraft (game jam contest) or Dublin ALT.NET but she is also continously looking for new ways to teach people through numerous talks and meetups like Functional Kats.

She is curious of everything and always avid to learn. These days you can see her sailing along with F# and functional programming for instance.

It’s no surprise that she was recognized several times in Ireland as a top person in her field. Microsoft also recently awarded her an MVP award.

It’s thanks to people like Andrea that I’m happy to be part of the community that is software development.

Muncho

Muncho would have loved to conclude this but he had another emergency

A Tale of Connected Dots

This past week, we organized Evolve 2014 in Atlanta to talk about all things mobile with Xamarin.

Being a mobile conference, we of course made our own app so that attendees could track out the sessions and the geography of the event.

We also launched something a bit different in the form of a treasure hunt app. Thanks to Estimote, we used a combination of iBeacon and QR codes spread throughout the conference intertwined with challenges along the way until you reach the end of the quest and a special prize.

The main interface of that quest app is a serie of connected dots:

Evolve Quest screenshot

As you progress through the game, the content scrolls naturally to reveal more odd-shaped arrangement of those dots.

Originally, this screen was just a big tall image containing everything. This is suboptimal on Android for a couple of reason like some graphic chip not supporting big “texture” or simply the need to rescale at runtime on most screen.

Ultimately it’s also not fun because there is really nothing you can do with a big image.

Hint: this is NOT what we shipped (on Android at least).

Rather, what attendees could see on their Android phone at the conference was this:

A much more satisfying, softly animated, connected system of dots. The current game position is also highlighted with a discrete pulse.

We will open-source the entire Quest application at some point but I wanted to share how we did that specific bit that is entirely exclusive to Android.

View Source Code

Since this is a really custom animation, it’s a bit different than the other type of animation samples I have already shown.

For this type of thing, I usually rely on a very simple animator that gives me a floating value between 0 and 1 so that I can do my own tweening in code. I also manage to pass this value by hijacking one of the standard view animatable property so that I don’t need to expose more information to the Java bridge:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Interpolator instantiation
shiverAnimator = ObjectAnimator.OfFloat (this, "scaleX", 0, 1);
shiverAnimator.RepeatCount = ValueAnimator.Infinite;
shiverAnimator.AnimationRepeat += (sender, e) => shiverIndex++;

// Property hijack
public override float ScaleX {
	get { return currentShiverInterpolation; }
	set {
		currentShiverInterpolation = value;
		Invalidate ();
	}
}

In our case, I then use this value to compute for each drawing pass an offset that is added to the base position of every dots:

1
2
3
4
5
6
7
8
9
10
11
// Expressed in dp units
int[] shiverOffsets = new[] { 9, 3, -8, 4, -7, 6 };

// Extra applied offset code
int ApplyShivering (int dotIndex, int value)
{
	var off = shiverOffsets [(dotIndex + shiverIndex) % shiverOffsets.Length]
		* currentShiverInterpolation;
	value += (int)Math.Round (off);
	return value;
}

The wave that you see on the highlighted pin comes from a GradientDrawable that I create from a shape drawable XML definition as follow:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:shape="ring"
	android:useLevel="false"
	android:innerRadiusRatio="2">
	<solid android:color="#99ffffff" />
</shape>

The reason for using a drawable like this is to avoid redoing common drawing code and also taking advantage of the ring shape type automatic radius scaling (notice the innerRadiusRatio attribute) to create a nice spreading effect as the ring grows larger.

The ring size and color is dynamically computed from a slightly interpolated (quadratic or accelerated) value coming from our shared shiver interpolator:

1
2
3
4
5
6
7
8
9
10
11
var dur = shiverAnimator.Duration / 4;
var fraction = (float)(shiverAnimator.CurrentPlayTime % dur) / dur;
// Cheap interpolation
fraction *= fraction;

var rippleRad = (int)(radius * (5 * fraction + 1)) - 8;
ripple.SetBounds (x - rippleRad, y - rippleRad, x + rippleRad, y + rippleRad);

var color = Color.Argb ((int)Math.Round (0x99 * (1 - fraction)),
                        0xFF, 0xFF, 0xFF);
((GradientDrawable)ripple).SetColor (color.ToArgb ());

If you have been to my Mastering Time and Space session, you’ll likely also have detected the presence of a custom interpolator in there that is set on the shared shivering animator. The code is following:

1
2
3
4
5
6
7
8
class TriangleWave : Java.Lang.Object, ITimeInterpolator
{
	public float GetInterpolation (float input)
	{
		var t = input * 2;
		return (float)(2 * Math.Abs (t - Math.Floor (t + 0.5)) * (1 - 2 * Math.Floor (t)));
	}
}

This interpolator generates a triangle pattern that is repeatable (i.e. it both starts and ends at the same position for the dot) so that the animation can run continuously:

Triangle curve

Build Your Own Continuous Integration Pole

If you live in Boston and happen to walk around the Commons when evening falls, you may have noticed a strange glow coming from the top of one the building along the park:

A Mysterious Glow on Boston

This eerie light comes from our designer pole, an experiment I did to provide cheap feedback over our build status in our continuous integration system:

Le Pole

The setup is based around 3 main components: a Raspberry Pi, a 5V relay module board and a basic 12V RGB LED strip. You can buy all those parts directly from Amazon for about $112 total (of which $77 was for a premium Raspberry Pi package but it’s up to you).

Setup

No matter the RGB LED strip “brand” you end up buying (it all seems to be from the same generic supplier), the wiring is pretty simple and consist of a common ground and a wire for each color channel (red, green, blue) so 4 wires in total.

Thus in the simplest form we can very easily turn on and off those channels using a bunch of relays. Each build status (success, test failure and build failure) will be mapped to a specific color (green, blue, red respectively).

A relay is the basic building block to control a power circuit (the 12V DC from the LED strip power adapter) from a control circuit (the 3.3V DC of the Raspberry Pi). There are a bunch of different designs available (magnetic, solid state, …) depending on the amount of power you are controlling. For this type of setup pretty much anything should work (it’s just LEDs after all).

A relay module

Those relays are then mounted on a board like the one in the picture to conveniently expose a control interface that you can plug to your Raspberry Pi GPIO ports. That’s the final product you can buy (unless you want to do your own PCB).

Below is a close-up of the wiring between all the different pieces, the GPIO ports are exported to the breadboard and then connected onto the relay module control circuit.

Close-up wiring

I also made a more schematic version so that you can see which GPIO ports are used:

Wiring schema

The common of the LED strip (the white cable) is connected directly to the power source (we don’t care about controlling it). All the other color wires go in and out of their own relay.

If you are short on connectors, you may have noticed a little trick I use which is to go scavenge your old computers for IDE connectors (what you used to plug your old harddrive, floppy disk readers and the likes). They fit perfectly well and have the same type of wire that standard Dupont cables.

Below is the part of the code that controls the relay module to turn on and off the LEDs:

using System;
using System.Threading;
using RaspberryPiDotNet;

namespace WrenchBerry
{
	public static class Ledder
	{
		public enum Color {
			Green,
			Orange,
			Red
		}

		static GPIOPins[] pinMapping = new GPIOPins[] {
			GPIOPins.V2_GPIO_17,
			GPIOPins.V2_GPIO_27,
			GPIOPins.V2_GPIO_22,
		};

		public static void FlashLed (Color color, int times = 3)
		{
			var pin = pinMapping [(int)color];
			for (int i = 0; i < times; i++) {
				GPIO.Write (pin, true);
				Thread.Sleep (500);
				GPIO.Write (pin, false);
				Thread.Sleep (500);
			}
		}

		public static void TurnOn (Color color)
		{
			var pin = pinMapping [(int)color];
			GPIO.Write (pin, false);
		}

		public static void TurnAllColorOff ()
		{
			foreach (var pin in pinMapping)
				GPIO.Write (pin, true);
		}
	}
}

The code use the RaspberryPi.Net library to directly access the chip memory controlling the GPIO pins.

The main program is then a super-simplistic infinite loop over the CI results:

using System;
using System.IO;
using System.Linq;

namespace WrenchBerry
{
	class MainClass
	{
		const Ledder.Color NoColor = (Ledder.Color)(-1);
		static Ledder.Color currentColor = NoColor;

		public static void Main (string[] args)
		{
			var lane = args [0];

			while (true) {
				try {
					var builds = CISystem.GetStateListFromLane (lane);
					if (builds == null || builds.Length == 0 || !builds.Any (IsFinishedState)) {
					    Console.WriteLine ("Lane or fetching is fucked up");
					    return;
					}
					var firstBuilt = builds.First (IsFinishedState);
					Console.WriteLine (DateTime.Now.ToString ("u") + " - " + firstBuilt);

					Ledder.Color color = GetColorForState (firstBuilt);

					if (color != NoColor && currentColor != color) {
					    Ledder.TurnAllColorOff ();
					    Ledder.FlashLed (color);
					    Ledder.TurnOn (color);
					}
					currentColor = color;
				} catch (Exception e) {
					Console.WriteLine (e);
				}

				System.Threading.Thread.Sleep ((int)TimeSpan.FromMinutes (2).TotalMilliseconds);
			}
		}

		static Ledder.Color GetColorForState (DBState state)
		{
			Ledder.Color color = NoColor;
			switch (state) {
			case DBState.Failed:
				color = Ledder.Color.Red;
				break;
			case DBState.Issues:
				color = Ledder.Color.Orange;
				break;
			case DBState.Success:
				color = Ledder.Color.Green;
				break;
			}
			return color;
		}

		static bool IsFinishedState (DBState state)
		{
			return state == DBState.Failed || state == DBState.Issues || state == DBState.Success;
		}
	}
}

All that’s left to do is to make yourself a nice little storage area and, why not, spice up the system a little bit. For instance in our setup (pictured below) I also hooked up a small speaker.

Final Setup

As an example our current playlist consist of: the Imperial March (build failure), Mass Effect Reaper sound (test failure) and, last but not least, our friend He-Man (build success).

Happy (hardware) hacking!