<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Gnuf on Code]]></title><description><![CDATA[Writings, of a technical nature.]]></description><link>https://gnuf.dev/</link><image><url>https://gnuf.dev/favicon.png</url><title>Gnuf on Code</title><link>https://gnuf.dev/</link></image><generator>Ghost 4.48</generator><lastBuildDate>Fri, 17 Apr 2026 14:55:07 GMT</lastBuildDate><atom:link href="https://gnuf.dev/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Pixel-y Panel Project: Spritesheets for Animation]]></title><description><![CDATA[Making animations from spritesheets using CircuitPython's displayio module and the TileGrid class. ]]></description><link>https://gnuf.dev/pixel-y-panel-project-spritesheets-for-animation/</link><guid isPermaLink="false">63fbbfb60e624176808a4645</guid><category><![CDATA[pixely panel project]]></category><category><![CDATA[hardware]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Sun, 26 Feb 2023 21:30:10 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2023/02/Screenshot-2023-02-26-at-21.36.18.png" medium="image"/><content:encoded><![CDATA[<img src="https://gnuf.dev/content/images/2023/02/Screenshot-2023-02-26-at-21.36.18.png" alt="Pixel-y Panel Project: Spritesheets for Animation"><p>At the company I work for, we recently launched a <a href="https://shopify.supply/products/display">pixel-y status board called &quot;Display&quot;</a> that immediately caught my eye:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2023/02/display.jpeg" class="kg-image" alt="Pixel-y Panel Project: Spritesheets for Animation" loading="lazy" width="831" height="501" srcset="https://gnuf.dev/content/images/size/w600/2023/02/display.jpeg 600w, https://gnuf.dev/content/images/2023/02/display.jpeg 831w" sizes="(min-width: 720px) 720px"></figure><p>It runs a custom app on the <a href="https://tidbyt.com/">Tidbyt</a> platform and was a successful <a href="https://www.kickstarter.com/projects/tidbyt/retro-display">Kickstarter</a> in 2021. While it has a lot going for it (dimmable display, mobile app configuration, SDK and 3rd party apps, beautiful wood case), at $180 USD, it&apos;s still more than I want to spend.</p><p>A <a href="https://twitter.com/Shopify/status/1618610890336776197">tweet</a> I saw about it featured an animated character on the display which got me interested immediately. I dove right into a <a href="https://learn.adafruit.com/tombstone-matrix-portal/code-the-sprite-sheet-animation-display">spritesheet animation tutorial</a> on Adafruit. The basic idea is to use a single bitmap image that has all the frames on it, then use the <a href="https://learn.adafruit.com/circuitpython-display-support-using-displayio/tilegrid-and-group">displayio.TileGrid</a> class to conveniently cycle through the frames. The finished demo:</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/7Iohqgb9LH4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="Spritesheet animation using CircuitPython"></iframe></figure><p>Here&apos;s some notes on how I did it:</p><ul><li>A <code>TileGrid</code> represents a grid of images taken from a source bitmap. The bitmap is partitioned into equally sized &quot;tiles&quot;, and any of these sub-regions can be shown as an image in the <code>TileGrid</code>. </li><li>To choose a tile from the bitmap to be shown on a <code>TileGrid</code>, and to reference the bitmap&apos;s tiles are indexed in <strong>row-major</strong> order. For example, suppose we have a bitmap that is 256 x 256 pixels, and we define the tile size to be 64 x 64 pixels. Each of the bitmap tile regions would be numbered from 0 to 15. </li><li>The tutorial encourages you to use a vertical spritesheet, because it&apos;s easy to create and simplifies indexing. But a horizontal one works just as well. For example, my spritesheet lives in a bitmap of 160 x 32 pixels, so each one of the five tiles is 32 x 32 pixels. Since we only need to display one frame at a time, we make the <code>TileGrid</code> have a width and height of 1:</li></ul><pre><code class="language-python">bitmap = displayio.OnDiskBitmap(filename)
sprite = displayio.TileGrid(
    bitmap,
    pixel_shader=bitmap.pixel_shader,
    width=1,
    height=1,
    tile_width=32,
    tile_height=32
)
frame_count = int(bitmap.width / 32)</code></pre><ul><li>To make the sprite move back and forth across the screen, I update the sprite&apos;s <code>x</code> member, keeping track of the current direction and when it&apos;s moved off-screen. In addition, I mirror the character when it is walking in the opposite direction by toggling the sprite&apos;s <code>flip_x</code> attribute: this eliminates the need to have a second, flipped version of the spritesheet.</li></ul><pre><code class="language-python">def advance_frame():
    global current_frame
    current_frame = current_frame + 1
    if current_frame &gt;= frame_count:
	    current_frame = 0
    sprite_group[1][0] = current_frame

    if sprite_group[1].x &lt; -32 or sprite_group[1].x &gt; 64:
    	sprite_group[1].flip_x = not sprite_group[1].flip_x
    direction = -1 if not sprite_group[1].flip_x else 1

    sprite_group[1].x += direction</code></pre>]]></content:encoded></item><item><title><![CDATA[How to Make a Battery-Powered Lightbox Photo Frame]]></title><description><![CDATA[Step by step tutorial demonstrating how to make a self-contained lightbox to illuminate photos or other art. Using a shadowbox and battery-powered LED light strips, learn all the materials and steps on how to make one for yourself.]]></description><link>https://gnuf.dev/how-to-make-a-self-contained-lightbox-photo-frame/</link><guid isPermaLink="false">61b7f3a0ee64f70689f8cf90</guid><category><![CDATA[how-to]]></category><category><![CDATA[lightbox]]></category><category><![CDATA[picture frame]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Fri, 17 Dec 2021 15:08:57 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2021/12/lightbox-on.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://gnuf.dev/content/images/2021/12/lightbox-on.jpg" alt="How to Make a Battery-Powered Lightbox Photo Frame"><p>One of my pandemic projects has been to decorate my office with a pixel theme (see <a href="https://twitter.com/gnufmuffin/status/1420792633027448835">this Twitter thread</a> for photos). In the evenings, I like to browse artist portfolios, hunting for just the right pieces to hang on the wall.</p><p><a href="http://www.osbornegraphics.com/">Stephen Osborne</a> made a series of illustrations a few years ago imagining the &quot;cyberpunk Toronto of the future&quot; called <a href="http://osbornegraphics.com/neodotcity.html">Neodot City</a>, which is also his handle on<a href="https://www.instagram.com/neodotcity/"> Instagram</a>. I was delighted by the local angle and aesthetic, and bought two of them on his <a href="https://www.redbubble.com/people/neodotcity/shop">RedBubble shop</a>. Here&apos;s what one of the 8&quot; by 12&quot; metallic photographic prints looks like:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211205_153923350.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211205_153923350.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211205_153923350.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211205_153923350.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211205_153923350.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Joe Doubles, part of the Neodot City series by pixel artist Stephen Osborne</figcaption></figure><p>Looking for an interesting way to display this print, I came across a video by Adam Bowie describing <a href="https://www.youtube.com/watch?v=4CiOjfycbVk">How To Hack a Lightbox From an IKEA Ribba Frame</a>. The basic idea was to take apart a shadow box, tape LED light strips to the sides of the frame, then illuminate the artwork by diffusing the light from the interior.</p><p>Here&apos;s a sneak peek of the final result:</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/DBu6h00Vo8Y?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><p>The first step was a trip to <a href="https://canada.michaels.com/">Michaels</a>, a massive arts-and-crafts chain store to get the <a href="https://canada.michaels.com/en/fundamentals-black-11x14-shadowboxes-by-studio-decor/10500658.html">11&quot; by 14&quot; shadow boxes</a> ($25 CAD for two) and <a href="https://canada.michaels.com/en/single-mat-11in-x-14in--8in-x-10in-opening/10083808.html">mattes with 8&quot; by 10&quot; openings</a> ($7 CAD each):</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211205_154016068.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211205_154016068.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211205_154016068.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211205_154016068.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211205_154016068.jpg 2400w" sizes="(min-width: 720px) 720px"></figure><p>Michaels actually sells 11&quot; by 14&quot; mattes with 8&quot; by <strong>12</strong>&quot; openings, but they were out of stock everywhere. Without an angled matte cutter, I used an Olfa snap-blade knife to (poorly) enlarge the 10&quot; opening to a 12&quot; long one.</p><p>I didn&apos;t need the shadow box spacer insert so I took that out and also bent the flexible points out of the way:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211205_162708418.NIGHT.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211205_162708418.NIGHT.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211205_162708418.NIGHT.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211205_162708418.NIGHT.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211205_162708418.NIGHT.jpg 2400w" sizes="(min-width: 720px) 720px"></figure><p>Then, I mounted the print to the matte using masking tape. <strong>This was a mistake</strong>. It isn&apos;t meant to hold any weight and doesn&apos;t even stick that well. It might be okay to just check alignment, but peeling it off started to remove bits of the matteboard material.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211205_163116404.NIGHT.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211205_163116404.NIGHT.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211205_163116404.NIGHT.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211205_163116404.NIGHT.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211205_163116404.NIGHT.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Mounting the print to the matte with masking tape. <strong>Don&apos;t do this</strong>.</figcaption></figure><p>In the YouTube video, Bowie used LED strips powered by a wall plug, but I wanted the freedom to be able to place the frame anywhere, without a cord hanging out of it. There are plenty of LED light strips out there, but the ones I ended up getting were <a href="https://www.lightinthebox.com/en/p/led-light-strips-usb-interface-or-aa-battery-box-power-supply-flexible-2835-smd-per-meter-60-leds-8mm-warm-white-cold-white-5v-led-light-strip_p8249135.html">from lightinthebox.com</a> ($10 CAD each). I got the ones powered by three AA batteries, with warm white LEDs, in a 2m length (since the shadow box perimeter was 2 x (11&quot; + 14&quot;) = 50&quot; = 127cm). The strips have adhesive on the back, and I also folded the framing points back to keep them out of the way.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211205_164756559.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211205_164756559.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211205_164756559.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211205_164756559.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211205_164756559.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Attaching the LED light strip to the inside edge of the shadow box.</figcaption></figure><p>One thing to consider is where you want the battery pack to end up (more on this below). I definitely wanted it at the bottom, so I started by taping the LEDs closest to the battery pack, beginning at the bottom of the frame (relative to how the artwork is being displayed). Here&apos;s the finished lights:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211205_165556023.NIGHT.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211205_165556023.NIGHT.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211205_165556023.NIGHT.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211205_165556023.NIGHT.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211205_165556023.NIGHT.jpg 2400w" sizes="(min-width: 720px) 720px"></figure><p>I also replaced the masking tape with paper tape (Duck Brand EZ Tear Paper Tape, $10 CAD) cut into narrow widths:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211209_022911718.NIGHT.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211209_022911718.NIGHT.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211209_022911718.NIGHT.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211209_022911718.NIGHT.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211209_022911718.NIGHT.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Using paper tape to mount the artwork to the matteboard.</figcaption></figure><p>This is also a good time to check the illumination of your artwork, to see if photograph or artwork is too thick, or if LED lights are bright enough to shine through.</p><p>To allow the light to illuminate the interior, I needed to ensure the backing board was positioned higher than the light strips. I initially tried stacking flat, self-stick rubber bumpers (that you stick to a cabinet door so it doesn&apos;t slam onto the frame), but I needed four or five, and the adhesive wasn&apos;t very strong. </p><p>Instead, I ended up trimming pieces of styrofoam, and gluing them to the backing:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211205_172122016.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211205_172122016.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211205_172122016.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211205_172122016.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211205_172122016.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Gluing styrofoam spacers to the backing board.</figcaption></figure><p>Shaving styrofoam pieces is messy, and I had a hard time making each one the same height. But it was close enough to get the backing board flush with the edge (close enough anyway!):</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211205_195034819.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211205_195034819.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211205_195034819.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211205_195034819.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211205_195034819.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>The spacers position the backing board close to flush with the frame&apos;s edge.</figcaption></figure><p>For aesthetic reasons, I didn&apos;t want to be able to see the individual LEDs, so it was important to diffuse the light inside the shadow box. I used foil tape ($5 CAD for 30 ft) on the inside of the backing board. You could also add an opaque vellum or acrylic sheet, but I wanted to avoid anything thick, as I didn&apos;t have a lot of depth to work with.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211205_195851390.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211205_195851390.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211205_195851390.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211205_195851390.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211205_195851390.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Using foil tape on the backing board to diffuse the light.</figcaption></figure><p>The last thing to figure out was where to put the battery pack. There is an on/off switch on the <em>opposite</em> side of where the battery door is. If I put the battery pack on the outside of the frame, the battery door needs to face outwards, and the whole thing offset to access the switch. </p><p>I tried this idea using a Command Strip to tape the pack to the back of the frame, threading the wire through a small notch:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211209_024623837.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211209_024623837.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211209_024623837.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211209_024623837.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211209_024623837.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Attaching the LED battery pack to the outside of the frame with a Command Strip.</figcaption></figure><p>However, when putting the frame on the wall, it tilted backwards too much:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211209_024911958.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="2667" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211209_024911958.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211209_024911958.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211209_024911958.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211209_024911958.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>The thick battery pack makes the frame tilt backwards on the wall.</figcaption></figure><p>I decided to try storing the battery pack <em>inside </em>the frame. I cut out three sides from the backing board to make a door, then used paper tape to reinforce the hinge. The width was enough to slip in the battery pack, and the height was just slightly less so the pack wouldn&apos;t fall out. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211209_023029292.NIGHT.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211209_023029292.NIGHT.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211209_023029292.NIGHT.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211209_023029292.NIGHT.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211209_023029292.NIGHT.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Battery pack placed inside the frame, with a door cut-out for access.</figcaption></figure><p>If you&apos;re wondering why there&apos;s a little tape flag attached to the battery pack, it&apos;s to make it easier to remove it to change the batteries:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211209_023122550.NIGHT.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211209_023122550.NIGHT.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211209_023122550.NIGHT.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211209_023122550.NIGHT.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211209_023122550.NIGHT.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>The tape flag makes it easier to remove the battery pack.</figcaption></figure><p>I was initially worried that you would be able to see the shadow of the pack inside the frame, or that it would block too much light, but it wasn&apos;t a problem. </p><p>Once I was happy with the positioning, I taped the backing board to the frame using more paper tape. This would block any light leakage, and more importantly, held the frame and its contents together, so it could be hung.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211209_024413981.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211209_024413981.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211209_024413981.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/12/PXL_20211209_024413981.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/12/PXL_20211209_024413981.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Taping the backing board to the frame.</figcaption></figure><p>That&apos;s pretty much it! I was quite pleased with the result. The neon signage in the composition looks really good when it&apos;s illuminated.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/12/PXL_20211217_141757393.NIGHT-COLLAGE.jpg" class="kg-image" alt="How to Make a Battery-Powered Lightbox Photo Frame" loading="lazy" width="1535" height="2046" srcset="https://gnuf.dev/content/images/size/w600/2021/12/PXL_20211217_141757393.NIGHT-COLLAGE.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/12/PXL_20211217_141757393.NIGHT-COLLAGE.jpg 1000w, https://gnuf.dev/content/images/2021/12/PXL_20211217_141757393.NIGHT-COLLAGE.jpg 1535w" sizes="(min-width: 720px) 720px"><figcaption>Completed lightbox picture frame: with lights off (top), and lights on (bottom)</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Pixel-y Panel Project: Clock & Serial Console]]></title><description><![CDATA[Wherein I get a basic clock running on the MatrixPortal and figure out how to read output from print() statements using screen.]]></description><link>https://gnuf.dev/pixel-y-panel-project-clock-and-serial-console/</link><guid isPermaLink="false">6102100011092413be34b90e</guid><category><![CDATA[pixely panel project]]></category><category><![CDATA[hardware]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Thu, 29 Jul 2021 23:36:00 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2023/02/matrixportal-clock-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://gnuf.dev/content/images/2023/02/matrixportal-clock-2.jpg" alt="Pixel-y Panel Project: Clock &amp; Serial Console"><p></p><p>A neat thing about the MatrixPortal after you install CircuitPython on it, is that it&apos;s super easy to start writing projects. Plugging in the board mounts a USB mass storage device called <code>CIRCUITPY</code>:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/07/CIRCUITPY-folder-structure.png" class="kg-image" alt="Pixel-y Panel Project: Clock &amp; Serial Console" loading="lazy" width="1060" height="1144" srcset="https://gnuf.dev/content/images/size/w600/2021/07/CIRCUITPY-folder-structure.png 600w, https://gnuf.dev/content/images/size/w1000/2021/07/CIRCUITPY-folder-structure.png 1000w, https://gnuf.dev/content/images/2021/07/CIRCUITPY-folder-structure.png 1060w" sizes="(min-width: 720px) 720px"></figure><p>Whatever functionality you need, you just copy from the Library Bundle on your host computer into the <code>lib</code> folder on the board. Your project code lives in the root folder; here, it&apos;s just called <code>code.py</code>. Any changes to the filesystem cause the MatrixPortal to restart and execute the new code.</p><p>This may seem a bit weird if you&apos;ve never done embedded programming before, but it&apos;s because you don&apos;t have a shell (or even a traditional operating system!) running on the board. So no access to handy tools like <code>pip</code> or Homebrew to fetch dependencies. It also means that you&apos;ll have to pay more attention to inter-library dependencies.</p><p>Anyway, I tried out one of the simple projects, <a href="https://learn.adafruit.com/network-connected-metro-rgb-matrix-clock">Network Connected RGB Matrix Clock</a>. Here are some of the things I tweaked:</p><ul><li>I customized the clock with a <a href="https://en.wikipedia.org/wiki/Glyph_Bitmap_Distribution_Format">BDF</a> font downloaded from <a href="https://github.com/Tecate/bitmap-fonts">https://github.com/Tecate/bitmap-fonts</a> </li><li>To make the clock display 24-hour time, I commented out the lines that subtracted 12 from the <code>hours</code>. I also adjusted the label text to always format the hours with 2 digits.</li><li>I used the <a href="https://learn.adafruit.com/fancyled-library-for-circuitpython?view=all">adafruit_fancyled</a> library with a custom palette to slowly change the colours of the display.</li><li>Don&apos;t forget to get a free <a href="https://io.adafruit.com/">Adafruit IO</a> account, and include your username and key in <code>secrets.py</code>. <code>matrixportal.network.get_local_time()</code> makes a network call to the Adafruit IO time server to get the time.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2021/07/PXL_20210729_025233088.jpg" class="kg-image" alt="Pixel-y Panel Project: Clock &amp; Serial Console" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/07/PXL_20210729_025233088.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/07/PXL_20210729_025233088.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/07/PXL_20210729_025233088.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/07/PXL_20210729_025233088.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Tada! Now I know what time it is.</figcaption></figure><p>One thing I couldn&apos;t figure out is where the output of debug <code>print()</code> calls go. What is <code>stdout</code> on the board hooked up to!?</p><p>I found the answer in the <a href="https://learn.adafruit.com/welcome-to-circuitpython/">Welcome to CircuitPython</a> guide: the board exposes a <a href="https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console">serial console</a> over the USB interface. The docs suggest using <a href="https://learn.adafruit.com/welcome-to-circuitpython/installing-mu-editor">Mu</a>, a simple Python editor that auto-detects the MatrixPortal and has built-in support for the serial console.</p><p>But since I&apos;m a die-hard vim user, I wanted to <a href="https://learn.adafruit.com/welcome-to-circuitpython/advanced-serial-console-on-mac-and-linux">connect to the serial console</a> using <a href="https://www.gnu.org/software/screen/manual/screen.html">screen</a>. </p><!--kg-card-begin: html--><script id="asciicast-c0LFwnasECINR0D3EIMpHn4jU" src="https://asciinema.org/a/c0LFwnasECINR0D3EIMpHn4jU.js" async></script><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Pixel-y Panel Project: Intro]]></title><description><![CDATA[Wherein I embark on my first ever hardware project, programming an LED panel. Using the Adafruit MatrixPortal M4, I'm going to explore the world of LED matrices, Arduino, and CircuitPython.]]></description><link>https://gnuf.dev/pixelly-panel-project-intro/</link><guid isPermaLink="false">60ff5f0b11092413be34b7a0</guid><category><![CDATA[hardware]]></category><category><![CDATA[pixely panel project]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Tue, 27 Jul 2021 03:00:37 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2021/07/PXL_20210727_015536726-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://gnuf.dev/content/images/2021/07/PXL_20210727_015536726-2.jpg" alt="Pixel-y Panel Project: Intro"><p>I recently decided that my home office should be &quot;pixel&quot; themed (think low-resolution, 256-colour graphics from the 1990s). I got inspired after buying and framing this &quot;pixorama&quot; <a href="https://pixelypixels.com/collections/prints/products/dothingstellpeoplepixorama">poster</a>. I even ostentatiously re-arranged my office furniture, so meeting participants would see something more interesting than closet doors.</p><p>To include some <em>actual</em> pixels into my office, I researched displays that could show a clock or the weather or animated GIFs. The leader in this space seems to be <a href="https://www.divoom.com/">Divoom</a>: their <a href="https://www.divoom.com/product/Pixoo-Max.html">Pixoo-Max</a> display is a slick 32x32 LED matrix controlled by a mobile app. However, there&apos;s no public API, so you&apos;re limited to what the app can do. </p><!--kg-card-begin: markdown--><p><img src="https://cdn.shopify.com/s/files/1/0082/4105/3814/products/S-4-1-699229_600x.jpg?v=1664500985" alt="Pixel-y Panel Project: Intro" loading="lazy"></p>
<!--kg-card-end: markdown--><p>Another option was the <a href="https://store.lametric.com/products/lametric?variant=34703088974">LaMetric Time</a>, a beautiful, minimal clock with big chunky LEDs designed for displaying a little text. It&apos;s got a documented API, an <a href="https://lametric.com/en-US/market/bestfortime">app market</a>, and lots of contributed <a href="https://ifttt.com/lametric">IFTTT integrations</a>. But $200 USD is pretty steep for something like this.</p><!--kg-card-begin: markdown--><p><img src="https://lametric.com/sites/default/files/2020-11/d_weather-min_1.jpeg" alt="Pixel-y Panel Project: Intro" loading="lazy"></p>
<!--kg-card-end: markdown--><p>There were a couple of dead ends: the <a href="https://creativeartsandtechnology.com/product/pixel-led-art-frame/">PIXEL LED Art Frame</a> started out on <a href="https://www.kickstarter.com/projects/alinke/pixel-interactive-led-art">Kickstarter</a> but no longer appears to be sold. The <a href="https://glanceclock.com/">Glance Clock</a> is mostly a clock, with a Bluetooth-enabled display that syncs with your watch; it&apos;s a bit pricey and not very customizable. The <a href="https://www.bastelbunker.de/pixel-it/">Pixel It</a> is a rough clone of the LaMetric Time, put together from parts you buy yourself, and seemed too complicated for me.</p><p>I browsed <a href="https://www.adafruit.com/">Adafruit</a> but a lot of the projects using LED matrices seemed daunting, especially since I know virtually nothing about hardware, don&apos;t know how to solder, etc. Finally, I discovered the <a href="https://learn.adafruit.com/adafruit-matrixportal-m4?view=all">Matrix Portal M4</a>:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/07/PXL_20210727_015536726.jpg" class="kg-image" alt="Pixel-y Panel Project: Intro" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/07/PXL_20210727_015536726.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/07/PXL_20210727_015536726.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/07/PXL_20210727_015536726.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/07/PXL_20210727_015536726.jpg 2400w" sizes="(min-width: 720px) 720px"></figure><p>This excerpt from the website was what sold me on this board:</p><blockquote>Folks love Adafruit&apos;s <a href="https://www.adafruit.com/category/327">wide selection of RGB matrices</a> and accessories for making custom colorful LED displays... and Adafruit RGB Matrix Shields and FeatherWings can be quickly soldered together to make the wiring much easier.<br><br>But what if we made it <em>even easier</em> than that? <strong><strong>Like, no solder, no wiring, just instant plug-and-play? </strong></strong>Dream no more - with the <strong><strong>Adafruit Matrix Portal add-on for RGB Matrices</strong></strong>, there has never been an easier way to create powerful internet-connected LED displays.</blockquote><p>Sign me up! Err, I mean, place an order from <a href="https://elmwoodelectronics.ca/">Elmwood Electronics</a>, a Canadian source for Maker electronics, including Adafruit products. This is what I bought:</p><!--kg-card-begin: markdown--><ul>
<li>64x32 RGB LED Matrix - 3mm pitch (<strong>$59 CAD</strong>)<br>
<em>The pitch is the distance between the LEDs, so it dictates the overall dimensions of the matrix. I wanted mine to live on a bookshelf, not in the windshield of a truck.</em></li>
<li>Adafruit MatrixPortal M4 (<strong>$35</strong>)<br>
<em>This is the board that drives the LED matrix. It has a CPU, flash, SRAM, WiFi coprocessor, accelerometer, and a bunch of other hardware goodies that are, uh, good to have.</em></li>
<li>Raspberry Pi 4 Power Supply in White (<strong>$13</strong>)<br>
<em>The power supply makes the LEDs light up. It has a USB-C connector, and I chose white to match my bookshelf!</em></li>
</ul>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/07/PXL_20210727_021056392.NIGHT.jpg" class="kg-image" alt="Pixel-y Panel Project: Intro" loading="lazy" width="2000" height="1500" srcset="https://gnuf.dev/content/images/size/w600/2021/07/PXL_20210727_021056392.NIGHT.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/07/PXL_20210727_021056392.NIGHT.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/07/PXL_20210727_021056392.NIGHT.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/07/PXL_20210727_021056392.NIGHT.jpg 2400w" sizes="(min-width: 720px) 720px"></figure><p>Tonight, I followed the Adafruit setup guide, and got the MatrixPortal connected to the LED matrix, and installed <a href="https://circuitpython.org/board/matrixportal_m4/">CircuitPython</a> 6.3.0 on it.</p><p>Whee, what fun! Next, I&apos;ll try to get a demo app running to ensure the panel is working, and to familiarize myself with the development environment.</p>]]></content:encoded></item><item><title><![CDATA[How to livestream on Google Meet with multiple cameras and background music from your MacBook]]></title><description><![CDATA[Step-by-step guide on how to livestream with multiple cameras, microphone, and background music. Using your MacBook and free software, I'll guide you through setting up the video, audio, and overlays, for a professional looking livestream for Google Meet. ]]></description><link>https://gnuf.dev/how-to-livestream-on-google-meet-with-multiple-cameras-and-background-music-from-your-macbook/</link><guid isPermaLink="false">6075952b70a0d45a9d90edc5</guid><category><![CDATA[how-to]]></category><category><![CDATA[google meet]]></category><category><![CDATA[livestream]]></category><category><![CDATA[OBS]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Tue, 27 Apr 2021 15:14:53 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2021/04/livestream-setup.png" medium="image"/><content:encoded><![CDATA[<img src="https://gnuf.dev/content/images/2021/04/livestream-setup.png" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook"><p>I was recently asked to lead a live baking class online. My mind immediately went to a setup that went <em>way</em> beyond a phone on a tripod. I wanted:</p><ul><li>A camera feed of me presenting</li><li>An overhead camera feed showing the countertop and my hands</li><li>An external microphone to capture me talking</li><li>A Bluetooth headset for me, so I can hear participants as I&apos;m moving around</li><li>High quality background music</li><li>On screen indicator of the current track</li></ul><h1 id="solution">Solution</h1><p>I knew the basics of <a href="https://support.zoom.us/hc/en-us/articles/206618765-Zoom-video-tutorials">Zoom</a>, <a href="https://support.apple.com/en-ca/HT204380">FaceTime</a> and <a href="https://support.google.com/a/users/answer/9300131?hl=en">Google Meet</a>, but the above configuration went beyond what I&apos;ve done in the past. </p><p>Happily, I was able to get it all working. I&apos;ll share my journey with you below, in three sections:</p><ol><li><a href="#video">Video</a>: Use OBS software to composite multiple camera feeds and other on-screen elements, publishing it as a virtual camera for Meet</li><li><a href="#audio">Audio</a>: Use Soundflower and Ladiocast to combine and route multiple audio sources to the livestream and to headphones</li><li><a href="#overlays">Overlays</a>: Use a browser extension to scrobble from YouTube Music to Last.fm, then have OBS show a hosted website that fetches the current track</li></ol><p>I&apos;m using a <strong>Mac</strong>, so these directions won&apos;t be directly applicable if you&apos;re using Windows or Linux, but will hopefully point you towards your own solution.</p><h1 id="video">Video</h1><h2 id="open-broadcaster-software-obs">Open Broadcaster Software (OBS)</h2><p>I first heard about <a href="https://obsproject.com/">OBS</a> from our CEO last year: </p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">The reason why this is valuable is that we are all working from home and OBS allows some really good home video production with picture in picture and quality audio mixing. With this feature it could easily be used with video conferencing software like Meet/Zoom.</p>&#x2014; Tobi Lutke &#x1F333;&#x1F332;&#x1F6D2;&#x1F579; (@tobi) <a href="https://twitter.com/tobi/status/1242641155407458305?ref_src=twsrc%5Etfw">March 25, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure><p>That sounded like what I was looking for, so my first step was to <a href="https://obsproject.com/download">download OBS Studio</a> (Version 26.1.2, 64-bit) onto my MacBook Pro (macOS 11.2.3). </p><p>Running OBS for the first time should trigger the <strong>Auto-Configuration Wizard</strong>. If not, find it in the <strong>Tools</strong> menu.</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/OBS-Auto-Config-Wizard-2.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="1224" height="1140" srcset="https://gnuf.dev/content/images/size/w600/2021/04/OBS-Auto-Config-Wizard-2.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/OBS-Auto-Config-Wizard-2.png 1000w, https://gnuf.dev/content/images/2021/04/OBS-Auto-Config-Wizard-2.png 1224w" sizes="(min-width: 720px) 720px"></figure><p>OBS can work directly with many streaming services, but Google Meet isn&apos;t one of them. Select <strong>I will only be using the virtual camera</strong> (more on what that means in a moment). Click <strong>Next</strong> to continue, then click on <strong>Apply Settings </strong>to complete the wizard.</p><p>Now, you should see the main OBS screen. Yours might not have anything under Scenes, Sources, or Audio Mixer.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://gnuf.dev/content/images/2021/04/OBS-Blank-Main-Screen.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="2000" height="1540" srcset="https://gnuf.dev/content/images/size/w600/2021/04/OBS-Blank-Main-Screen.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/OBS-Blank-Main-Screen.png 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/OBS-Blank-Main-Screen.png 1600w, https://gnuf.dev/content/images/size/w2400/2021/04/OBS-Blank-Main-Screen.png 2400w" sizes="(min-width: 1200px) 1200px"></figure><p>If you&apos;ve read ahead the OBS <a href="https://obsproject.com/wiki/OBS-Studio-Quickstart">Quickstart</a> guide, it recommends downloading an extra app to capture system audio. <strong>Ignore that advice for now.</strong></p><h2 id="scenes">Scenes</h2><p>In OBS, a scene is a surface on which you arrange the sources you want to display: camera feeds, web pages, images, text, etc. </p><p>Create your initial scene by clicking on the + button under the Scenes section at the bottom of the window, giving it a descriptive name.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://gnuf.dev/content/images/2021/04/OBS-Add-New-Scene.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="2000" height="1507" srcset="https://gnuf.dev/content/images/size/w600/2021/04/OBS-Add-New-Scene.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/OBS-Add-New-Scene.png 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/OBS-Add-New-Scene.png 1600w, https://gnuf.dev/content/images/2021/04/OBS-Add-New-Scene.png 2274w" sizes="(min-width: 1200px) 1200px"></figure><h2 id="cameras">Cameras</h2><p>MacBooks have a built-in front-facing camera, so I&apos;m going to use that as the one filming me. Let&apos;s add it as a video source in OBS, by clicking on the + button under Sources again, choosing <strong>Video Capture Device</strong>:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://gnuf.dev/content/images/2021/04/OBS-Sources-Add-Video-Capture-Source.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="2000" height="1502" srcset="https://gnuf.dev/content/images/size/w600/2021/04/OBS-Sources-Add-Video-Capture-Source.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/OBS-Sources-Add-Video-Capture-Source.png 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/OBS-Sources-Add-Video-Capture-Source.png 1600w, https://gnuf.dev/content/images/2021/04/OBS-Sources-Add-Video-Capture-Source.png 2280w" sizes="(min-width: 1200px) 1200px"></figure><p>Make sure <strong>Create new</strong> is selected, and name it <strong>Built-in Camera</strong>. On the next screen, you&apos;ll actually select the physical camera to associate with this device. On the Device dropdown, click on <strong>FaceTime HD Camera (Built-in)</strong> (yours might have a slightly different name). You should see a live preview of the feed immediately:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/Add-Video-Device-Properties.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="1662" height="1656" srcset="https://gnuf.dev/content/images/size/w600/2021/04/Add-Video-Device-Properties.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/Add-Video-Device-Properties.png 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/Add-Video-Device-Properties.png 1600w, https://gnuf.dev/content/images/2021/04/Add-Video-Device-Properties.png 1662w" sizes="(min-width: 720px) 720px"></figure><p>After clicking OK, you&apos;ll see your new source in the Sources list, and your camera feed surrounded by a red box on the scene canvas. Feel free to play around with resizing and repositioning it:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://gnuf.dev/content/images/2021/04/OBS-First-Camera.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="2000" height="1540" srcset="https://gnuf.dev/content/images/size/w600/2021/04/OBS-First-Camera.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/OBS-First-Camera.png 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/OBS-First-Camera.png 1600w, https://gnuf.dev/content/images/size/w2400/2021/04/OBS-First-Camera.png 2400w" sizes="(min-width: 1200px) 1200px"></figure><p>For the overhead camera, I&apos;m going to use a <a href="https://www.logitech.com/en-us/products/webcams/c930e-business-webcam.960-000971.html">Logitech C930e webcam</a>, mounted on an <a href="https://support.neewer.com/product/10093268/">adjustable desktop suspension boom</a>. In OBS, add another video capture source, naming it something like <strong>Overhead Camera</strong>. Select the webcam in the Device dropdown, then click OK. On the main screen, you&apos;ll see your second camera feed in a red box. Play around with resizing and repositioning it. </p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://gnuf.dev/content/images/2021/04/OBS-Scene-with-two-cameras.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="2000" height="1524" srcset="https://gnuf.dev/content/images/size/w600/2021/04/OBS-Scene-with-two-cameras.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/OBS-Scene-with-two-cameras.png 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/OBS-Scene-with-two-cameras.png 1600w, https://gnuf.dev/content/images/size/w2400/2021/04/OBS-Scene-with-two-cameras.png 2400w" sizes="(min-width: 1200px) 1200px"></figure><p>To change which feed is on top, drag the order of the sources or click the up and down buttons.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://gnuf.dev/content/images/2021/04/OBS-Changing-Source-Order-2.gif" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="640" height="439"></figure><h2 id="virtual-camera-for-google-meet">Virtual Camera for Google Meet</h2><p>So far so good. Let&apos;s try it out in a Google Meet call. While you could share your OBS window or entire screen, the quality wouldn&apos;t be great. The better way to do this is to take advantage of the &quot;virtual camera&quot; in OBS. That broadcasts whatever is currently being shown in the current scene as if it were a camera connected to your laptop!</p><p>To see what I mean, in OBS, under the Controls section at the bottom, click <strong>Start Virtual Camera</strong>.</p><p>Next, from a browser logged into your Google account, visit the URL <code><a href="g.co/meet">g.co/meet</a></code>, and click <strong>New Meeting</strong> | <strong>Start an Instant Meeting</strong>. At the bottom of the screen, click the &#x2807;(three dots) button to open More Options. Click on <strong>Settings</strong>, then <strong>Video</strong>. In the Camera dropdown, select <strong>OBS Virtual Camera</strong>:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/Meet-Settings-Video-OBS-Virtual-Camera.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="1632" height="1332" srcset="https://gnuf.dev/content/images/size/w600/2021/04/Meet-Settings-Video-OBS-Virtual-Camera.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/Meet-Settings-Video-OBS-Virtual-Camera.png 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/Meet-Settings-Video-OBS-Virtual-Camera.png 1600w, https://gnuf.dev/content/images/2021/04/Meet-Settings-Video-OBS-Virtual-Camera.png 1632w" sizes="(min-width: 720px) 720px"></figure><p>Close the settings window, and voil&#xE0;!</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://gnuf.dev/content/images/2021/04/Google-Meet-picture-in-picture.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="2000" height="1429" srcset="https://gnuf.dev/content/images/size/w600/2021/04/Google-Meet-picture-in-picture.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/Google-Meet-picture-in-picture.png 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/Google-Meet-picture-in-picture.png 1600w, https://gnuf.dev/content/images/size/w2400/2021/04/Google-Meet-picture-in-picture.png 2400w" sizes="(min-width: 1200px) 1200px"></figure><p>Look at that! Your Google Meet camera is now a live composite of <strong><em>two</em> </strong>video feeds. Back in OBS, play around with the sources in the scene, and watch it reflect immediately in your Meet call. Invite a friend (or use another device) to join the call, so you can see what it looks like as a participant.</p><p>From here, you can experiment with adding more sources to your scene. For example, a Text source could display your ingredient list. </p><p>For more help, browse through the <a href="https://obsproject.com/wiki/Home">OBS Wiki</a> especially the <a href="https://obsproject.com/wiki/Sources-Guide">Sources Guide</a>.</p><h1 id="audio">Audio</h1><p>Turing our attention to the audio now, we&apos;ll be speaking into a microphone, and also playing music on the computer. That&apos;s <strong>two</strong> input sources. We want our participants on the livestream to hear both, and maybe listen in on just the music through our headphones: <strong>two</strong> output sinks. </p><p>Most software can only pay attention to audio from <strong>one</strong> input<strong> </strong>at a time, and apps usually can select <strong>one</strong> output at a time. </p><p>See (hear?) the challenge we need to resolve?</p><p>After much searching, I stumbled upon Megan Lavengood&apos;s <a href="https://meganlavengood.com/2020/03/12/how-to-get-the-best-quality-audio-to-combine-with-your-voice-when-creating-video-or-video-chatting/">blog post</a>. She describes a detailed solution for teaching music classes online. The directions below are adapted from her article.</p><h2 id="capturing-mac-sounds-as-input">Capturing Mac Sounds as Input</h2><p>First, we&apos;re going to route audio output from the computer <em>back<strong> </strong></em>to the computer so it can be used as an audio input. You might see this referred to as &quot;desktop audio capture&quot;. It seems straightforward, to allow people on a call to hear what your computer is playing, but this ability isn&apos;t built-in to Macs. </p><p>Install Soundflower (2.0b2) by carefully following <a href="https://github.com/mattingalls/Soundflower/releases">these directions</a> from the developer. </p><p>Afterwards, you&apos;ll have two new Soundflower devices. Click on the volume icon in the menu bar:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/Soundflower-devices.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="692" height="648" srcset="https://gnuf.dev/content/images/size/w600/2021/04/Soundflower-devices.png 600w, https://gnuf.dev/content/images/2021/04/Soundflower-devices.png 692w"></figure><p>To check that things are working, set your Sound Output to be <strong>Soundflower (64ch), </strong>as shown above. Then, play some music on your computer (from the Apple Music app, Spotify, it doesn&apos;t matter). Naturally, you won&apos;t hear anything. Open <strong>System Preferences | Sound | Input</strong>, select <strong>Soundflower (64ch)</strong>, and verify that the Input level is showing activity:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/Soundflower-64ch-working.gif" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="640" height="378"></figure><p>What Soundflower is doing, is capturing any music being sent to its output device, and making it available as an input device.</p><h2 id="directing-the-audio-output">Directing the Audio Output</h2><p>Next, we&apos;re going to install a utility that will let us choose any of our audio inputs, and assign them to any of our outputs. We&apos;ll have a nice interface to select which outputs go to the livestream, and which ones we&apos;ll monitor. </p><p><a href="https://apps.apple.com/us/app/ladiocast/id411213048?mt=12">Ladiocast</a> is available, free, from the Mac App Store. Go and install v0.13.0 then launch it. </p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/Ladiocast-initial.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="1482" height="1432" srcset="https://gnuf.dev/content/images/size/w600/2021/04/Ladiocast-initial.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/Ladiocast-initial.png 1000w, https://gnuf.dev/content/images/2021/04/Ladiocast-initial.png 1482w" sizes="(min-width: 720px) 720px"></figure><p>This is Ladiocast&apos;s mixer interface. The left side is where we&apos;ll configure all of our inputs. </p><p>For <strong>Input 1</strong>, we&apos;re going to select <strong>Soundflower (64ch)</strong>, the device our Mac is outputting sound to. Underneath the volume slider for this input, you&apos;ll see buttons representing which outputs this input should be mapped to. Select Main.</p><p>For <strong>Input 2</strong>, select the microphone you&apos;ll be using (external or built-in), then click on Main underneath.</p><p>Turning to the right side, let&apos;s configure our outputs. Under <strong>Main Output</strong>, select <strong>Soundflower (2ch)</strong>. This will be the device that the livestream will use as its &quot;microphone&quot;. &#xA0;It will contain the combined audio from whatever is playing on the computer (Input 1) and the microphone (Input 2).</p><p>For <strong>Aux Output 1</strong>, select your wired or Bluetooth headphones. You&apos;ll note that we didn&apos;t assign any inputs to this output. If we want to monitor the music, we could click on <strong>Aux 1</strong> underneath <strong>Input 1</strong>. Unlike teaching a music class, I don&apos;t want to hear the background music for my baking tutorial, but this allows you to toggle it.</p><p>Your mixer should look something like this now:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/Ladiocast-configured.gif" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="640" height="611"></figure><p>Go ahead and play some music on your computer, making sure your Sound Output is still set to Soundflower (64ch). Talk into your microphone too! Verify that the Main Output monitor bars are moving in accordance with the inputs. If you&apos;ve selected Aux 1 to monitor the music in your headphones (as I&apos;ve done in the animation above), you should see the monitor bars of Aux Output 1 move in time to the music.</p><h2 id="but-doesnt-obs-do-audio-mixing">But Doesn&apos;t OBS Do Audio Mixing?</h2><p>OBS does support sound mixing. However, configuring your sound independently via Soundflower and Ladiocast gives you more flexibility, so you don&apos;t have to use OBS when you just care about the sound. For example, if you want to use a single camera in Meet (the normal setup), but have background music playing, you don&apos;t need to involve OBS.</p><h2 id="testing-audio-in-google-meet">Testing Audio in Google Meet</h2><p>We&apos;re ready to test the audio component of our setup. Keep the music playing, and start another Google Meet instant meeting. In <strong>Settings | Audio</strong>, select <strong>Soundflower (2ch)</strong> as the Microphone, and your headphones as the Speakers. If it&apos;s available in your organization, make sure to <strong>turn off noise cancellation</strong>, as that will interfere with the clarity of the music.</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/Google-Meet-Soundflower-mic-1.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="796" height="649" srcset="https://gnuf.dev/content/images/size/w600/2021/04/Google-Meet-Soundflower-mic-1.png 600w, https://gnuf.dev/content/images/2021/04/Google-Meet-Soundflower-mic-1.png 796w" sizes="(min-width: 720px) 720px"></figure><p>Have someone else join your meeting. Start talking in your microphone, and ensure the participants can hear both the music and your voice clearly. Make sure you can hear what they are saying as well. </p><p>Experiment with adjusting your computer&apos;s output volume, and Ladiocast&apos;s volume sliders and buttons, to get the right levels for the inputs and outputs. </p><h2 id="were-almost-done">We&apos;re almost done!</h2><p>At this point, if you combine this audio setup with the virtual camera from the previous section, you have a pretty fancy Google Meet livestream setup: <strong>multiple camera feeds and</strong> <strong>background music, with great quality audio for both you and your viewers!</strong></p><p>But let&apos;s not forget the icing on the cake, a little <a href="https://www.merriam-webster.com/dictionary/chyron">chyron</a> showing the track that&apos;s currently playing!</p><h1 id="overlays">Overlays</h1><p>OBS supports other kinds of visual sources like text, the contents of a file, an image, a video, or even a web page. We want to show something like this at the bottom of our scene:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/now-playing-loop.gif" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="640" height="89"></figure><h2 id="now-playing-on-lastfm">Now Playing on Last.fm</h2><p>After a fruitless search, I quickly discovered that <a href="https://music.youtube.com/">YouTube Music</a> doesn&apos;t provide a way for apps to query what is currently playing.</p><p>However, xaymar&apos;s <a href="https://blog.xaymar.com/2020/02/04/a-now-playing-overlay-using-last-fm/">Now Playing Overlay using Last.FM</a> looked promising. It makes use of <a href="https://www.last.fm/">Last.fm</a>, a social music site, where users share what they are listening to, affectionately known as &quot;scrobbling&quot;.</p><p>The overlay reads the current artist and song from Last.fm, and displays it with album art and animations. Then, using an OBS Browser source, we can display the web page in our scene.</p><h2 id="scraping-for-scrobbling">Scraping for Scrobbling</h2><p>How do we get the current track from YouTube Music though? Last.fm has an entire <a href="https://support.last.fm/t/youtube-and-youtube-music-scrobbling/173">support page</a> about this. I downloaded <a href="https://chrome.google.com/webstore/detail/web-scrobbler/hhinaapppaileiechjoiifaancjggfjm">Web Scrobbler for Chrome</a>, a browser extension that will scrape what&apos;s playing from dozens of music services and publish it to Last.fm. </p><p>After installing the extension, I signed up for a free Last.fm account, then authorized the extension to talk to Last.fm. Web Scrobbler&apos;s options should look similar to this afterwards:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/Web-Scrobbler-option-page.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="948" height="908" srcset="https://gnuf.dev/content/images/size/w600/2021/04/Web-Scrobbler-option-page.png 600w, https://gnuf.dev/content/images/2021/04/Web-Scrobbler-option-page.png 948w" sizes="(min-width: 720px) 720px"></figure><p>Now, open up a browser tab, go to <code><a href="https://music.youtube.com/">music.youtube.com</a></code>, and start playing some music. Open up another tab, log in to <code>last.fm</code> and scroll down to your Recent Tracks. The currently playing track should be reflected:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/YouTube-Music-scrobbled-to-Last.fm-1.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="652" height="915" srcset="https://gnuf.dev/content/images/size/w600/2021/04/YouTube-Music-scrobbled-to-Last.fm-1.png 600w, https://gnuf.dev/content/images/2021/04/YouTube-Music-scrobbled-to-Last.fm-1.png 652w"></figure><h2 id="displaying-a-site-as-an-obs-browser-source">Displaying a Site as an OBS Browser Source</h2><p>Back to the now playing page by xaymar. His <a href="https://blog.xaymar.com/2020/02/04/a-now-playing-overlay-using-last-fm/">blog post</a> has customization details, but the most basic setup is to create a browser instance in your streaming software pointing to his web page, with your Last.fm username.</p><p>Here&apos;s the step-by-step. From the main OBS screen, add a <strong>Browser</strong> source:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/OBS-Add-Browser-Source.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="1216" height="810" srcset="https://gnuf.dev/content/images/size/w600/2021/04/OBS-Add-Browser-Source.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/OBS-Add-Browser-Source.png 1000w, https://gnuf.dev/content/images/2021/04/OBS-Add-Browser-Source.png 1216w" sizes="(min-width: 720px) 720px"></figure><p>Give it a name like &quot;<strong>Now Playing Overlay</strong>&quot;. </p><p>In the Properties dialog, set the following values:</p><ul><li>URL: <code>https://stream.xaymar.com/nowplaying.html?user={your_last_fm_username}</code></li><li>Width: <strong>1024</strong></li><li>Height: <strong>128</strong></li><li>Leave everything else unchecked</li><li>Leave the default Custom CSS</li></ul><p>If you still have music playing, and scrobbling to Last.fm, you should see something like this in the scene:</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/OBS-Now-Playing-Overlay.png" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="2000" height="1291" srcset="https://gnuf.dev/content/images/size/w600/2021/04/OBS-Now-Playing-Overlay.png 600w, https://gnuf.dev/content/images/size/w1000/2021/04/OBS-Now-Playing-Overlay.png 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/OBS-Now-Playing-Overlay.png 1600w, https://gnuf.dev/content/images/size/w2400/2021/04/OBS-Now-Playing-Overlay.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>That little rectangle is actually a live web page that will keep displaying whatever track you&apos;ve scrobbled to Last.fm! Try moving it around, resizing it, etc. and watch what happens a few seconds after the song changes.</p><p>Awe-some!</p><h1 id="conclusion">Conclusion</h1><p>So, in the end, I achieved what I set out to do: broadcast a Google Meet livestream that could show me and my countertop, let viewers listen to music and my directions, and even see what song was playing.</p><h2 id="what-does-the-physical-setup-look-like">What Does the Physical Setup Look Like?</h2><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2021/04/PXL_20210323_014649182--2-.jpg" class="kg-image" alt="How to livestream on Google Meet with multiple cameras and background music from your MacBook" loading="lazy" width="2000" height="2667" srcset="https://gnuf.dev/content/images/size/w600/2021/04/PXL_20210323_014649182--2-.jpg 600w, https://gnuf.dev/content/images/size/w1000/2021/04/PXL_20210323_014649182--2-.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2021/04/PXL_20210323_014649182--2-.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2021/04/PXL_20210323_014649182--2-.jpg 2400w" sizes="(min-width: 720px) 720px"></figure><p>I have an old Thunderbolt display connected to my MacBook via a Thunderbolt 3 (USB-C) to Thunderbolt 2 Adapter. It&apos;s sitting on a table behind the island.</p><p>The display serves as a USB hub, into which my Logitech webcam and Blue Yeti mic (both using USB-A connectors) are plugged in. </p><p>The webcam is mounted on an adjustable stand with extra height provided by some matzoh boxes.</p><p>On the day of the event, I put YouTube Music, Ladiocast and OBS on the laptop&apos;s display, and the Google Meet browser tab on the external display. This let me see everyone else on the big screen, and let me manage anything that might need adjusting on the MacBook&apos;s display.</p><h2 id="wow-is-it-really-this-complicated">Wow, Is It Really This Complicated?</h2><p>Yes? I don&apos;t know. But this is what I came up with.</p><p>Obviously, if you don&apos;t need a second camera or an overlay, you wouldn&apos;t even need OBS. You might want decent background music for your meditation class or whatever, and the Soundflower/Ladiocast combo is enough.</p><p>I was surprised with the complexity of displaying a &quot;Now Playing&quot; overlay in a livestream. If you know of an easier, less roundabout way, I&apos;d love to hear it.</p><h1 id="tips-and-troubleshooting">Tips and Troubleshooting</h1><ul><li>YouTube Music <a href="https://support.google.com/youtubemusic/answer/6313552?hl=en">will not play in the background</a> unless you have a Premium subscription, for example, a family plan. If your music keeps cutting out, this might be why. You can use a different service that is supported by the scrobbling extension.</li><li>If the music is intermittent or poor quality, check to see if Google Meet&apos;s <a href="https://support.google.com/meet/answer/9919960?co=GENIE.Platform%3DDesktop&amp;hl=en">Noise Cancellation</a> is enabled. <strong>Turn it off</strong>. Leaving it on will significantly degrade anything that isn&apos;t speech.</li><li>If your voice sounds like a robot to listeners, try switching the microphone you&apos;re using. This happened to me with a Bluetooth headset mic, and went away when I used the Blue Yeti. Just remember to adjust which microphone is being used for Input 2 in Ladiocast.</li><li>Keep your MacBook plugged in. Streaming, compositing, etc. is CPU intensive, and will drain battery very quickly.</li><li>If space permits, connect an external monitor so that you can have OBS running on your laptop, and your Meet call on the other display. That will let you see more of the apps, and reduce switching apps while you&apos;re busy presenting. </li><li>Test, test, test! I spent a couple of days setting up the software and hardware, and roped my wife and kids into helping me check the setup. You want to figure out the glitches well in advance of your event.</li></ul>]]></content:encoded></item><item><title><![CDATA[Books 📚 read in 2020, Part 1]]></title><description><![CDATA[Three-sentence reviews of books that I've read in the first half of 2020, including AI Superpowers; Chop Suey Nation; Deep Work; Thinking Inside the Box; The Language of Food; All the Birds In The Sky. ]]></description><link>https://gnuf.dev/books-for-2020-part-1/</link><guid isPermaLink="false">5f10acc954e6b20671b7508a</guid><category><![CDATA[books]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Wed, 05 Aug 2020 11:43:29 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2020/08/2020books-1.png" medium="image"/><content:encoded><![CDATA[<h3 id="ai-superpowers-china-silicon-valley-and-the-new-world-order">AI Superpowers: China, Silicon Valley, and the New World Order</h3><img src="https://gnuf.dev/content/images/2020/08/2020books-1.png" alt="Books &#x1F4DA; read in 2020, Part 1"><p>by Kai-Fu Lee<br>Houghton Mifflin Harcourt, 2018</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2020/08/Screen-Shot-2020-07-31-at-12.35.53-PM.png" class="kg-image" alt="Books &#x1F4DA; read in 2020, Part 1" loading="lazy"></figure><p>Lee, a seasoned researcher and executive, promotes a pro-China, pro-tech view of the world, and how artificial intelligence will change it in a pervasive way. He describes the gladiatorial startup world of China, and how its companies have leapt ahead of Silicon Valley in many aspects of digital life, from government services to ecommerce to social interaction. By the end of the book, a Stage 4 lymphoma diagnosis leads him to an epiphany, that his narrow, tech-focused world view alienated him from his family, and the importance of thinking about software&apos;s impact on society.</p><h3 id="chop-suey-nation-the-legion-cafe-and-other-stories-from-canada-s-chinese-restaurants">Chop Suey Nation: The Legion Cafe and Other Stories from Canada&#x2019;s Chinese Restaurants</h3><p>by Ann Hui<br>Douglas &amp; McIntyre, 2019</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2020/08/Screen-Shot-2020-07-31-at-12.36.18-PM.png" class="kg-image" alt="Books &#x1F4DA; read in 2020, Part 1" loading="lazy"></figure><p>On a road trip with her husband, Hui tries to figure out why <em><a href="https://en.wikipedia.org/wiki/Chop_suey">chop suey</a></em> exists in almost the same form across the vast expanse of Canada. She presents vignettes of restaurant owners and their journey to small towns, helping her to more deeply understand her own parents&apos; struggles as immigrants. Although she spends too much time on the logistics of the journey itself, I, as a 1st-generation Chinese-Canadian, found this to be a thought-provoking read, providing a voice to people whose stories aren&apos;t usually told.</p><h3 id="deep-work-rules-for-focused-success-in-a-distracted-world">Deep Work: Rules for Focused Success in a Distracted World</h3><p>by Cal Newport<br>Grand Central Publishing, 2016</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2020/08/Screen-Shot-2020-07-31-at-12.36.42-PM.png" class="kg-image" alt="Books &#x1F4DA; read in 2020, Part 1" loading="lazy"></figure><p>I didn&apos;t need the first part to convince me that being able to focus and concentrate on creative work was necessary in our modern economy. But the core teachings of part two of the book really resonated with me, on eliminating distractions and practising to work on difficult tasks, and I liked how Newport provides multiple strategies. Many of the anecdotes are about the habits of successful, well-resourced CEOs and philanthropists, but he doesn&apos;t provide any specific guidance for those of us who have to raise young children.</p><h3 id="thinking-inside-the-box-adventures-with-crosswords-and-the-puzzling-people-who-can-t-live-without-them">Thinking Inside the Box: Adventures with Crosswords and the Puzzling People Who Can&apos;t Live Without Them</h3><p>Adrienne Raphel<br>Penguin Press, 2020</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2020/08/Screen-Shot-2020-07-31-at-12.35.11-PM.png" class="kg-image" alt="Books &#x1F4DA; read in 2020, Part 1" loading="lazy"></figure><p>This enjoyable book zigs and zags from the origins of the crossword, to attending a national tournament, to the challenge of constructing a puzzle, and even a day-by-day chronicle of a crossword-themed cruise. You get a good insider&apos;s look at the people and process of producing the print and online New York Times crossword. Raphel lost me a bit when she strayed into describing some of the literary and cinematic depictions of cruciverbalists.</p><h3 id="the-language-of-food-a-linguist-reads-the-menu">The Language of Food: A Linguist Reads the Menu</h3><p>Dan Jurafsky<br>W.W. Norton, 2014</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2020/08/Screen-Shot-2020-07-31-at-12.37.06-PM.png" class="kg-image" alt="Books &#x1F4DA; read in 2020, Part 1" loading="lazy"></figure><p>This delightful journey into how our English words for food came about is filled with fact after interesting fact, written in a very personal and engaging way. One of the themes that Jurafsky touches on, is that the world is more connected culturally than we may think, as many of the foods or customs we&apos;ve adopted in North America, had their ancient origins in the Middle East. Some highlights for me, were his Yiddish and Cantonese examples, as well as the regression analyses he applies on menu writing.</p><h3 id="all-the-birds-in-the-sky">All the Birds In The Sky</h3><p>Charlie Jane Anders<br>Tor Books, 2016</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2020/08/Screen-Shot-2020-08-02-at-12.40.26-PM.png" class="kg-image" alt="Books &#x1F4DA; read in 2020, Part 1" loading="lazy"></figure><p>As a tech person, I was delighted by the extremely accurate descriptions of technology in this book, without any &quot;techno-babble&quot; or simplifications. Anders weaves an intricate tale set in San Francisco that incorporates witchcraft, young love, hubris, and humanity&apos;s relationship with technology and Mother Nature. Her attention to detail, turns of phrase, poignant observations, and wry dialogue all contributed to this being a superb and unique addition to the science fiction corpus.</p>]]></content:encoded></item><item><title><![CDATA[Books 📚 I Read in 2019]]></title><description><![CDATA[Brief reviews of books that I read in 2019, including The Attention Merchants, The Martian, River Town: Two Years on the Yangtze, Bad Blood: Secrets and Lies in a Silicon Valley Startup, Room, Small, Sharp, Software Tools, and Three Squares: The Invention of the American Meal.]]></description><link>https://gnuf.dev/books-i-read-in-2019/</link><guid isPermaLink="false">5e07c613b19e3c09c708619f</guid><category><![CDATA[books]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Mon, 06 Jan 2020 04:16:45 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2020/01/overlapbooks.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://gnuf.dev/content/images/2020/01/overlapbooks.jpg" alt="Books &#x1F4DA; I Read in 2019"><p>I re-discovered a love for long-form written works this year, but <strong>did not</strong> achieve my ambitious <a href="https://gnuf.dev/year-in-review-2018/#resolutions">goal</a> of reading one book per month this year.</p><h2 id="paper-vs-e-reader">Paper vs E-reader</h2><p>Because I am trying to reduce the distraction that a phone causes, I bought a Kobo Clara HD e-reader in April.</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/12/kobo.jpg" class="kg-image" alt="Books &#x1F4DA; I Read in 2019" loading="lazy"></figure><p>I chose this model because: </p><!--kg-card-begin: markdown--><ol>
<li>It&apos;s not an Amazon product</li>
<li>The Wirecutter recommended it</li>
<li>Library books can be borrowed on it</li>
<li>It lets you sync your Pocket articles.</li>
</ol>
<!--kg-card-end: markdown--><p>Now, on to the books:</p><h2 id="the-attention-merchants">The Attention Merchants</h2><p>by Tim Wu<br>Knopf Doubleday Publishing Group, 2016</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/12/book-attention-merchants.jpg" class="kg-image" alt="Books &#x1F4DA; I Read in 2019" loading="lazy"></figure><p>A chronicle of 150 years of advertisers systematically grabbing our attention, and selling it for profit. From Parisian theatre posters, to radio and TV dramas, to e-mail, video games, and smartphones, I was rapt with how advertising and entertainment have blended together so seamlessly. With ever more time willingly spent in front of screens, and more surfaces being infiltrated by ads, we need to keep our guard up, and ensure that our valuable attention remains our own.</p><h2 id="the-martian">The Martian</h2><p>by Andy Weir<br>Crown/Archetype, 2014</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/12/book-martian.jpg" class="kg-image" alt="Books &#x1F4DA; I Read in 2019" loading="lazy"></figure><p>A brilliant, funny, and imaginative story about a man who has to figure out how to keep himself alive when he is stranded on Mars. If you have an interest in space exploration, and chemistry or math, you might enjoy the verbose calculations and descriptions of how much oxygen or water is required to go from point A to point B. My 8-year old son and I enjoyed this book together, but we found it tedious at times, especially since I&apos;ve seen the movie adaptation.</p><h2 id="river-town-two-years-on-the-yangtze">River Town: Two Years on the Yangtze</h2><p>by Peter Hessler<br><a href="https://toronto.overdrive.com/search/publisherId?query=582&amp;sortBy=newlyadded">HarperCollins</a>, 2010</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/12/book-river-town.jpg" class="kg-image" alt="Books &#x1F4DA; I Read in 2019" loading="lazy"></figure><p>Hessler, a wonderful travel writer, spent two years in the early 2000s living and teaching in a small town in China while serving with the Peace Corps. He is a keen observer, and uses his struggle to learn how to read Chinese as a metaphor for understanding the rhythms of daily life in a rural village, his status as an outsider, and interactions with students. This book was on a list for anyone wanting to understand modern China, and I look forward to reading the two subsequent books he penned on living in the country.</p><h2 id="bad-blood-secrets-and-lies-in-a-silicon-valley-startup">Bad Blood: Secrets and Lies in a Silicon Valley Startup</h2><p>by John Carreyrou<br><a href="https://toronto.overdrive.com/search/publisherId?query=2440&amp;sortBy=newlyadded">Knopf Doubleday Publishing Group</a>, 2018</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/12/book-bad-blood.jpg" class="kg-image" alt="Books &#x1F4DA; I Read in 2019" loading="lazy"></figure><p>This book was a real page turner, and I told anyone within earshot about the story of Theranos, the now-defunct corporation that claimed to have invented a rapid blood test using a pinprick of blood. CEO Elizabeth Holmes was able to con so many prominent and successful people, who really wanted to believe in her charisma and vision. The anecdotes about the way she ran the company, how Theranos installed barely functional equipment in drug stores, and their pathetic attempts at cover-up, were a sickening reminder that snake-oil salesmen come in many different guises.</p><h2 id="room">Room</h2><p>by Emma Donoghue<br>Back Bay Books, 2013</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/12/book-room.jpg" class="kg-image" alt="Books &#x1F4DA; I Read in 2019" loading="lazy"></figure><p>This was an impulse read, a book that was lying on a shelf at a house we rented this summer. I had watched the movie a few years ago, but did not realize the printed narrative was told from the perspective of the 5-year old captive. Donoghue gives really distinct voices to the boy and his mother, and there is lots to read between the lines, in this absorbing and heart-wrenching tale.</p><h2 id="small-sharp-software-tools">Small, Sharp, Software Tools</h2><p>by Brian Hogan<br>Pragmatic Bookshelf, 2019</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/12/book-small-sharp-software-tools.jpg" class="kg-image" alt="Books &#x1F4DA; I Read in 2019" loading="lazy"></figure><p>Pragmatic Bookshelf often releases beta versions with a site to report errata, so I gave the manuscript a pretty thorough read as it was being edited. I&apos;m extremely comfortable using CLIs, and parts of the book were very basic, but this book still had new tricks to teach me. My main takeaway is that many UNIX tools have decades of history and still endure, so it definitely pays to spend time to learn (and learn more!) about how to use them.</p><h2 id="three-squares-the-invention-of-the-american-meal">Three Squares: The Invention of the American Meal</h2><p>by Abigail Carroll<br>Basic Books, 2013</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/12/book-three-squares.jpg" class="kg-image" alt="Books &#x1F4DA; I Read in 2019" loading="lazy"></figure><p>One of my favourite genres is food history, so this book&apos;s stories of what and how colonists in America ate, was fascinating. It walks through how mealtimes changed as workers shifted from an agrarian to a faster-paced, sedentary office life, and how industrial food production and advertising started exerting a stronger influence on families. I wish Carroll would write an afterword, as I would love to hear her commentary on the rise of &quot;cloud kitchens&quot; and food delivery in the 21st century.</p>]]></content:encoded></item><item><title><![CDATA[Year in Review: 2018]]></title><description><![CDATA[<h2 id="career">Career</h2><p>I continued looking into data science and machine learning this year. In March, I <a href="https://www.coursera.org/account/accomplishments/specialization/YJN4CYYUKXRJ">completed</a> all five courses of the <a href="https://www.deeplearning.ai/deep-learning-specialization/">Deep Learning Specialization</a> from deeplearning.ai, and also spent time playing around with <a href="https://firebase.google.com/products/ml-kit/">ML Kit for Firebase</a>: </p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Spoiler teaser for my talk tonight at <a href="https://twitter.com/GDGTOAndroid?ref_src=twsrc%5Etfw">@GDGTOAndroid</a> : ML Kit isn&apos;</p></blockquote></figure>]]></description><link>https://gnuf.dev/year-in-review-2018/</link><guid isPermaLink="false">5c2bda82919cf20572ab3b5c</guid><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Sat, 19 Jan 2019 23:01:29 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2019/01/2018-balloons-and-champagne_925x.jpg" medium="image"/><content:encoded><![CDATA[<h2 id="career">Career</h2><img src="https://gnuf.dev/content/images/2019/01/2018-balloons-and-champagne_925x.jpg" alt="Year in Review: 2018"><p>I continued looking into data science and machine learning this year. In March, I <a href="https://www.coursera.org/account/accomplishments/specialization/YJN4CYYUKXRJ">completed</a> all five courses of the <a href="https://www.deeplearning.ai/deep-learning-specialization/">Deep Learning Specialization</a> from deeplearning.ai, and also spent time playing around with <a href="https://firebase.google.com/products/ml-kit/">ML Kit for Firebase</a>: </p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Spoiler teaser for my talk tonight at <a href="https://twitter.com/GDGTOAndroid?ref_src=twsrc%5Etfw">@GDGTOAndroid</a> : ML Kit isn&apos;t all hype &#x1F389;, it can definitely learn a har gow &#x1F95F;. RSVP now: <a href="https://t.co/4qWfxwWFV0">https://t.co/4qWfxwWFV0</a> <a href="https://t.co/Jo2O60vKPE">pic.twitter.com/Jo2O60vKPE</a></p>&#x2014; Eric Fung (@gnufmuffin) <a href="https://twitter.com/gnufmuffin/status/1018911606649114624?ref_src=twsrc%5Etfw">July 16, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure><p>At the beginning of the year, I also started planning a career shift, from Android mobile development to something within the broad discipline of data science. I collected advice from attendees at the <a href="https://torontomachinelearning.com/">Toronto Machine Learning</a> &apos;Micro-Summit&apos;, pored over countless blogs and forums, had discussions with many colleagues, and did some serious thinking about whether I would have the time and energy to pursue a part-time graduate degree.</p><p>To find the right fit, I spent time messaging and chatting with various department heads, having lunch with various leads, spending several Hack Days with people from data teams, and even shadowing a colleague for a few hours. In the end, I was fortunate enough to be able to make a transition into a Data Scientist role within Shopify&apos;s <a href="https://www.shopify.com/careers/search?specialties%5B%5D=7&amp;keywords=&amp;sort=">Data Science &amp; Engineering</a> organization. One of the benefits of working in an organization that is supportive of your personal growth, is the possibility of such &quot;internal movement&quot;.</p><p>I look forward to a year filled with learning, and being able to start making contributions.</p><h2 id="speaking">Speaking</h2><p>I spoke at a number of Android conferences in 2018:</p><ul><li>Android Summit (Washington, DC): <strong>How the Command-Line Can Give You Superpowers</strong> (<a href="https://www.youtube.com/watch?v=95s4UM5lezk&amp;list=PLzO42HSbWr3JlGE4r7X8lfEe06arpBuhZ&amp;index=4">video</a>) (<a href="https://speakerdeck.com/efung/how-the-command-line-can-give-you-superpowers-1">slides</a>)</li><li>Ohio DevFest (Cincinnati, OH): <strong>Not Har Gow: Using Custom Models with ML Kit</strong> (<a href="https://www.youtube.com/watch?v=bNVQULL6Mzc&amp;index=2&amp;list=PLzO42HSbWr3JlGE4r7X8lfEe06arpBuhZ&amp;t=0s">video</a>) (<a href="https://speakerdeck.com/efung/not-har-gow-using-custom-models-with-ml-kit">slides</a>)</li><li>AndroidTO (Toronto, CA): <strong>How the Command-Line Can Give You Superpowers</strong> (<a href="https://www.youtube.com/watch?v=oVk3XjWI7ds&amp;index=3&amp;t=0s&amp;list=PLzO42HSbWr3JlGE4r7X8lfEe06arpBuhZ">video</a>) (<a href="https://speakerdeck.com/efung/how-the-command-line-can-give-you-superpowers">slides</a>)</li></ul><p>I got a lot of mileage out of the command-line talk as I had opportunities to incorporate feedback from our meetup and from work. Each time I presented for an outside audience, I continued to refine and polish it. I even gave it as a <a href="https://engineering.shopify.com/blogs/engineering/developer-talks-how-the-command-line-can-empower-you">webinar</a>! </p><p>Speaking of the <a href="https://www.meetup.com/ToAndroidDev/">meetup</a>, we grew our membership to over 900 people, and hosted 8 events this past year. The most exciting news was our inclusion into Google&apos;s <a href="https://developers.google.com/programs/community/gdg/">GDG Platform</a>, so we are now known as GDG Toronto Android.</p><p>I&apos;d like to continue giving talks in 2019, and will be looking at more non-Android focused conferences.</p><h2 id="writing">Writing</h2><p>Sadly, I don&apos;t have much to show in this area. Despite indicating an interest in previous years to practise writing more, I suffer from such severe writer&apos;s block, that it takes me weeks to write a few hundred words. Even submitting something for CfPs and then editing my talk drafts takes me weeks.</p><p>Perhaps I should try to write a short blog post every month, to practise the craft of writing and figure out how I can get words down faster.</p><h2 id="reading">Reading</h2><p>Another area where I expressed a desire to devote more time to, but did not succeed at. Discounting some fiction I read while I was on vacation, I spent my reading time on <a href="https://www.newyorker.com/">The New Yorker</a>, and the weekend New York Times and Globe and Mail.</p><h2 id="projects">Projects</h2><p>Running my own websites allows me to keep a toe dipped in the worlds of web development and system administration. In particular:</p><ul><li>This blog lets me play around with managing DNS records with <a href="https://www.hover.com/">Hover</a>, running a droplet on <a href="https://www.digitalocean.com/products/droplets/">DigitalOcean</a>, administering an Ubuntu Linux virtual machine, configuring <a href="https://www.nginx.com/">nginx</a>, securing connections with <a href="https://letsencrypt.org/">Let&apos;s Encrypt</a>, checking uptime with <a href="https://uptimerobot.com/">Uptime Robot</a>, and publishing with the wonderful <a href="https://ghost.org/">Ghost</a> blog platform.</li><li>My baking blog is a static site hosted on <a href="https://pages.github.com/">GitHub Pages</a>, built with <a href="https://jekyllrb.com/">Jekyll</a>. This means tinkering with Ruby, HTML and Sass to make the site look the way I want.</li></ul><h2 id="resolutions">Resolutions</h2><p>I don&apos;t usually make them, but this year, I want to. I will try to:</p><ul><li>Read one book per month</li><li>Write one technical blog post per month</li></ul>]]></content:encoded></item><item><title><![CDATA[AndroidTO 2018 Recap]]></title><description><![CDATA[<p>This year marked the 9th anniversary of <a href="http://www.androidto.com/">AndroidTO</a>, the largest Android developer conference in Canada. The organizers of <a href="https://www.meetup.com/GDG-Toronto/">GDG Toronto</a> and <a href="https://www.symbilitysolutions.com/symbility-intersect/">Symbility Intersect</a> put on a full-day lineup of fifteen 30-minute talks in the <a href="https://www.marsdd.com/">MaRS</a> Discovery District. </p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/androidto_crowd.jpg" class="kg-image" alt loading="lazy"></figure><p>There weren&apos;t a lot of sponsors at the tables outside the</p>]]></description><link>https://gnuf.dev/androidto-2018-recap/</link><guid isPermaLink="false">5c126f6341cddd18e10a4c90</guid><category><![CDATA[AndroidTO]]></category><category><![CDATA[conference]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Tue, 01 Jan 2019 20:44:57 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2019/01/androidto.png" medium="image"/><content:encoded><![CDATA[<img src="https://gnuf.dev/content/images/2019/01/androidto.png" alt="AndroidTO 2018 Recap"><p>This year marked the 9th anniversary of <a href="http://www.androidto.com/">AndroidTO</a>, the largest Android developer conference in Canada. The organizers of <a href="https://www.meetup.com/GDG-Toronto/">GDG Toronto</a> and <a href="https://www.symbilitysolutions.com/symbility-intersect/">Symbility Intersect</a> put on a full-day lineup of fifteen 30-minute talks in the <a href="https://www.marsdd.com/">MaRS</a> Discovery District. </p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/androidto_crowd.jpg" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><p>There weren&apos;t a lot of sponsors at the tables outside the auditorium, but two community groups, <a href="https://www.meetup.com/ToAndroidDev/">GDG Toronto Android</a>, and <a href="https://www.meetup.com/Android-Pub-Night/">Android Pub Night</a>, gave away stickers. Lunchtime was handled via <a href="https://www.ritual.co/">Ritual</a>, with attendees receiving a credit to go purchase a meal from a local restaurant. As in previous years, there was a live DJ helping with speaker intro/outro!</p><p>I was honoured to be accepted as a speaker during the morning half. Here&apos;s a summary of the talks I attended:</p><h1 id="accessibility-scale"><a href="http://www.androidto.com/#accessibility-at-scale">Accessibility @ Scale</a></h1><p>(<a href="https://speakerdeck.com/mallikapotter/accessibility-at-scale-droidcon-uk-2017">Slides</a>)</p><p>Mallika Potter (<a href="https://twitter.com/mallikaandroid">@mallikaandroid</a>) discussed how making apps accessible is a largely untapped area, despite its very large market. She emphasized that accessibility is not just beneficial for people with chronic conditions, or who are born with disabilities, because anyone can sustain a temporary impairment that requires accommodation.</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/mallika.jpg" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><p>One of the key points she made was that notifications and cues should have more than one sensory component. For example, a <strong>visual</strong> status bar notification should be accompanied by an <strong>auditory</strong> signal (and vice versa: avoid sound-only notifications, and provide a visual cue also). Another example is ensuring that videos contain captions.</p><p>Making changes incrementally and focusing on low-hanging fruit is important, because even small improvements can make your app usable for those that were previously unable to. Some quick fixes include using <a href="https://support.google.com/accessibility/android/answer/6283677?hl=en">TalkBack</a> to check for navigation issues, changing text size and density to look for clipping and crowding problems, and adding descriptions to UI controls.</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/EofRo0Xws2Q?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><h1 id="auditing-your-apks-like-a-black-hat-hacker">Auditing Your APKs Like a Black Hat Hacker</h1><p>(<a href="https://github.com/chmodxx/Slide-Decks/tree/master/AndroidTO-2018">Slides</a>)</p><p>Kristina Balaam (<a href="https://twitter.com/chmodxx_">@chmodxx_</a>) provided a whirlwind tour of how to think about security in Android apps, from the perspective of a malicious actor. She covered reverse engineering, attack surfaces, and best practices in app development.</p><p>Many of the tools of the app security trade are not used by everyday app developers. These include <a href="https://ibotpeaches.github.io/Apktool/">Apktool</a>, a tool that can disassemble compiled APK resources (like string and image resources, manifest files, and more); <a href="https://github.com/pxb1988/dex2jar">dex2jar</a>; <a href="https://github.com/JesusFreke/smali/wiki">smali</a> (an assembler/disassembler for the <a href="https://source.android.com/devices/tech/dalvik/dex-format.html">Dalvik executable format</a>); and <a href="http://jd.benow.ca/">JD</a> (a Java bytecode decompiler and analyzer). In addition to these low-level tools, Kristina mentioned <a href="https://github.com/mwrlabs/drozer">drozer</a>, a security testing framework to identify vulnerabilities in apps and devices, and a playground app called <a href="https://github.com/payatu/diva-android">Diva</a>, to get practise in hacking.</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/kristina.jpg" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><p>In listening to her describe a lot of the security problems that befell major app developers over the years, I got the feeling that it&apos;s really hard to get security right. Operating systems like Android get a host of new features every year, and keeping up with best practices is <strong>never-ending</strong>. Do watch her presentation to learn more about how other companies got it wrong, and the things you should start doing to get it right.</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/5XgZaS19jOU?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><h1 id="how-the-command-line-can-give-you-superpowers">How the Command-Line Can Give You Superpowers</h1><p>(<a href="https://speakerdeck.com/efung/how-the-command-line-can-give-you-superpowers">Slides</a>)</p><p>Over the last few years, I have encountered a lot of developers who only know Android Studio, and rarely spend any time in the command-line. Having grown up with Commodore BASIC, DOS, and Linux, the command-line is second nature to me. This summer, I decided to put together a talk focusing on how CLI tools can be helpful to Android developers.</p><p>This talk is in its 3rd or 4th iteration, as I&apos;ve given versions of it several times this year, refining and updating it each time.</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/oVk3XjWI7ds?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><h1 id="dynamic-app-modules">Dynamic App Modules</h1><p>(<a href="https://docs.google.com/presentation/d/1KJLVH1uTyIgf6_ZCyG1bzHtag9nDtXWytpd8eFm75Uw/edit?usp=sharing">Slides</a>)</p><p>Alex Saveau (<a href="https://twitter.com/supercilex">@SUPERCILEX</a>) gave us a deep dive into Google&apos;s new app serving model, <a href="https://developer.android.com/studio/projects/dynamic-delivery">Dynamic Delivery</a>, and the associated <a href="https://developer.android.com/guide/app-bundle/">App Bundle</a> upload format. The subtitle of his presentation was &quot;Building for Billions&quot;, and is apropos because the goal of dynamic delivery is to allow users to download optimized APKs containing only the code and resources they need. No more one-size-fits-all distribution!</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/alex.jpg" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><p>We learned about how app bundles are an evolution of <a href="https://developer.android.com/studio/build/configure-apk-splits">split APK</a> support, but takes it further by allowing the Play Store to <strong>automatically</strong> extract configuration-specific resources (e.g. screen density, CPU architecture, etc.) and feature-specific components into individual APKs, then sign and build them for you. We also learned about how apps need to be organized into base and feature modules, and how to handle older devices that don&apos;t support dynamic delivery.</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/I_y1H-8EmR8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><h1 id="applying-rx-best-practices-to-your-architecture">Applying Rx Best Practices To Your Architecture</h1><p>(<a href="https://speakerdeck.com/oldergod/applying-rx-best-practices-to-your-architecture">Slides</a>)</p><p>Beno&#xEE;t Quenaudon (<a href="https://twitter.com/oldergod">@oldergod</a>) gave a talk describing how to implement a Model-View-Intent architecture using RxJava. He has written about managing state with Rx <a href="https://medium.com/@oldergod/managing-state-with-rxjava-3ac66fbf3ff4">before</a>, and has given a <a href="https://www.youtube.com/watch?v=PXBXcHQeDLE">talk</a> at Droidcon NYC on this subject as well. </p><p>This is a pretty advanced talk, adopting an architectural pattern that isn&apos;t easy to wrap your head around, implemented using a library that has an even steeper learning curve. His main points were to avoid side effects so that functions are pure and easy to test, and to maintain a single unidirectional stream of events. </p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/benoit.jpg" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><p>If you are new to MVI and RxJava, there are many other resources out there to learn the basic concepts from (my favourites are <a href="http://hannesdorfmann.com/android/mosby3-mvi-1">this series on MVI</a> by Hannes Dorfmann, and <a href="https://www.oreilly.com/programming/free/rxjava-for-android-app-development.csp">this free ebook</a> on Rx by Matt Dupree). If you&apos;re looking for complete examples, you&apos;ll find Beno&#xEE;t&apos;s port of the Android Architecture TODO example in <a href="https://github.com/oldergod/android-architecture/tree/todo-mvi-rxjava">this repository</a> very useful, as well as <a href="https://github.com/kanawish/android-mvi-sample">this one</a> by my colleague Etienne.</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/n1viQvZVslE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><h1 id="adventures-of-an-android-developer-in-ios-land">Adventures of an Android Developer in iOS Land</h1><p>(<a href="https://speakerdeck.com/yuncheng13/adventures-of-an-android-developer-in-ios-land">Slides</a>)</p><p>Yun Cheng (<a href="https://twitter.com/yuncheng13">@yuncheng13</a>) does double-duty as an iOS <em>and</em> Android developer. In learning Android, she observed some differences in the tooling and language, and shared them with us in her talk. </p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/yun.jpg" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><p>Her first set of grievances were around <a href="https://developer.apple.com/xcode/">Xcode</a>, Apple&apos;s IDE. From its shortcomings with navigation and refactoring, to its poor autocomplete implementation, Yun could not think of <em>any</em> benefits of Xcode over Android Studio. Despite having alternative IDEs for writing Swift code (including JetBrains&apos; <a href="https://www.jetbrains.com/objc/">AppCode</a>), writing GUIs still requires Xcode to generate the machine-readable XML layouts. Speaking of Swift, she shared that it was nicer to use than Objective-C, especially with the latter&apos;s <a href="http://goshdarnblocksyntax.com/">block syntax</a>. (But perhaps <a href="http://goshdarnclosuresyntax.com/">closures</a> are just as confusing?)</p><p>Testing is one area where Android developers take mocking for granted. However, Swift does not allow reflection. Despite having fewer hardware and OS versions to test on, iOS developers still struggle with <a href="https://developer.apple.com/support/code-signing/">code signing</a> and <a href="https://developer.apple.com/testflight/">TestFlight</a>. Android developers have it much easier: build an APK, give it out.</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/8BCIoIW4OFk?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><h1 id="building-conversational-experiences-with-actions-on-google">Building Conversational Experiences with Actions on Google</h1><p>Nick Felker (<a href="https://twitter.com/handnf">@HandNF</a>) walked through how to develop <a href="https://developers.google.com/actions/">Actions on Google</a>, custom software to extend the functionality of Google&apos;s virtual <a href="https://assistant.google.com/">Assistant</a>.</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/nick.jpg" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><p>There&apos;s a lot of &quot;tech&quot; that goes into the Assistant: NLP, AI, speech processing and synthesis. Being able to leverage all of that, and developing your own experience from a <a href="https://developers.google.com/actions/templates/">template</a> in just a few hours is pretty magical. However, it&apos;s a pretty different mindset: you need to consider that people talking to the Assistant expect a real-time response, and may be having other chats at the same time. <a href="https://designguidelines.withgoogle.com/conversation/">Conversation design</a> involves thinking about all of these things so that interaction is natural and intuitive to users. And, since some connected devices have screens, existing visual UI/UX principles also need to be considered.</p><p>As Amazon did with Alexa, Google is encouraging developers to try writing Actions by offering <a href="https://developers.google.com/actions/community/">incentives</a> (Cloud credits and a T-shirt) if you publish before the end of July 2019. </p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/AIjgAtwdmGU?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><h1 id="machine-learning-life-after-the-tutorial">Machine Learning: Life After the Tutorial</h1><p>Kyri Paterson spoke about machine learning, data quality, and some of the mistakes companies have made when adding artificial intelligence to their products. She pointed out that ML algorithms are only as good as the data they are trained on, and that practitioners should be aware of pitfalls such as mislabelled data, <a href="https://en.wikipedia.org/wiki/Feature_scaling">feature scaling</a>, <a href="https://en.wikipedia.org/wiki/Oversampling_and_undersampling_in_data_analysis">correcting for bias</a> in data sets using techniques like <a href="https://jair.org/index.php/jair/article/view/10302">SMOTE</a>, and data sanitization.</p><p>From the prejudice in <a href="https://www.reuters.com/article/us-amazon-com-jobs-automation-insight/amazon-scraps-secret-ai-recruiting-tool-that-showed-bias-against-women-idUSKCN1MK08G">Amazon&apos;s AI recruiting tool</a> to the racist utterances of <a href="https://www.theverge.com/2016/3/24/11297050/tay-microsoft-chatbot-racist">Microsoft&apos;s Tay chatbot</a> to the implications of police wearing <a href="https://www.theverge.com/2018/3/12/17110636/china-police-facial-recognition-sunglasses-surveillance">facial recognition glasses</a>, there are many examples of imperfect technology powered by AI in our world today. Understanding what ML can and cannot do, and knowing that algorithms (and not just humans) can be biased, must be part of what data scientists consider nowadays.</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/JOwYysCgi9s?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><h1 id="poor-programming-patterns-and-how-to-avoid-them">Poor Programming Patterns and How to Avoid Them</h1><p>(<a href="https://speakerdeck.com/aliceyuan/droidcon-android-poor-programming-patterns">Slides</a>)</p><p>Alice Yuan (<a href="https://twitter.com/names_alice">@Names_Alice</a>) gave several examples of code smells and poor programming in apps that she&apos;s worked on, and outlined solutions for them. A nice touch was her own hand-drawn illustrations on many of the slides.</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/alice.jpg" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><p>The first issue she discussed was the classic <a href="https://en.wikipedia.org/wiki/Composition_over_inheritance">inheritance vs composition</a> design principle. A poor choice makes adding new features difficult, and leads to duplication of logic. Another poor choice was choosing an event bus over the <a href="https://en.wikipedia.org/wiki/Observer_pattern">observer pattern</a>, as the former does not enforce that a listener is around when an event occurs. On Android, there are many choices available to implement observers and listeners (including <a href="https://developer.android.com/topic/libraries/architecture/livedata">LiveData</a> and <a href="https://github.com/ReactiveX/RxJava">RxJava</a>). </p><p>Alice also pointed out data consistency issues when attempting to cache values inside fragments, putting model-dependent logic in view layers. Her suggested solution was to use the <a href="https://martinfowler.com/eaaCatalog/repository.html">repository</a> pattern, a central source-of-truth for models. Note that the <a href="https://developer.android.com/jetpack/docs/guide">Jetpack guide to app architecture</a> demonstrates a repository. Finally, she also chastised anyone not writing unit tests for their app. In her case, it was tightly coupled, poorly designed code that made it hard to test. By separating concerns, and using a good architecture pattern, she was able to improve understandability, promote code reusability, and ensure her code was testable.</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/dX_d6qdHsb0?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><h1 id="flutter-in-real-life-for-real-people">Flutter: In Real Life, For Real People</h1><p>Two of the organizers of AndroidTO, Mark Reale (<a href="https://twitter.com/markreale">@markreale</a>) and Jeff Corcoran, closed the conference with an amusing talk about their journey of learning <a href="https://flutter.io/">Flutter</a>. Mark was convinced to starting learning Google&apos;s latest mobile SDK after listening to a <a href="https://fragmentedpodcast.com/episodes/118/">podcast</a>, but quickly learned that Google&apos;s own tutorials needed a lot of polishing.</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/jeff_mark.jpg" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><p>Having two major mobile platforms to develop for means double the cost and effort for companies wanting apps on iOS and Android. Flutter is the latest attempt to solve the problem of building apps for two platforms from a single codebase. Mark and Jeff have hosted Flutter Study Jams in the past, relying on the educational resources that Google and Udacity provide. They quickly found that many of the materials were out-of-date and incomplete. It was <em>not</em> the case that you don&apos;t need any programming or development experience to get started with <a href="https://www.dartlang.org/">Dart</a> and Flutter, despite the website&apos;s claims.</p><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/P5YzEcg6xJ8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><p>Mark put together a repository that outlined <a href="https://github.com/markreale/the-flutter-journey">his journey</a>, and Jeff ended up publishing an open-source <a href="https://github.com/mimicmobile/flutter-conference-app">conference app</a> which was used for AndroidTO itself. (Watch the video to hear why there was no iOS version!)</p><figure class="kg-card kg-image-card"><img src="https://gnuf.dev/content/images/2019/01/androidto2018-1.png" class="kg-image" alt="AndroidTO 2018 Recap" loading="lazy"></figure><h1 id="closing">Closing</h1><p>Proceeds from this AndroidTO went to support the <a href="https://www.sickkidsfoundation.com/waystodonate/majorgiftsendowmentsandfoundations/techcommunityinitiative">SickKids Tech&amp;Innovation initiative</a>. I also learned about a charitable organization in Canada called the <a href="http://upsidefoundation.ca/">Upside Foundation</a>, aimed at early stage startups, for them to donate equity, which is then distributed to registered charities upon a liquidation event. </p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">While we run this conference every year to support and further the <a href="https://twitter.com/hashtag/Android?src=hash&amp;ref_src=twsrc%5Etfw">#Android</a> community, we&apos;re incredibly happy that this year we are also able to support <a href="https://twitter.com/sickkids?ref_src=twsrc%5Etfw">@sickkids</a>&apos; <a href="https://twitter.com/hashtag/TechForSickKids?src=hash&amp;ref_src=twsrc%5Etfw">#TechForSickKids</a> initiative with this $10,000 donation! From our community to theirs!<a href="https://twitter.com/hashtag/AndroidTO?src=hash&amp;ref_src=twsrc%5Etfw">#AndroidTO</a> <a href="https://twitter.com/hashtag/DevFest18?src=hash&amp;ref_src=twsrc%5Etfw">#DevFest18</a> <a href="https://t.co/FlAb7OaTpg">pic.twitter.com/FlAb7OaTpg</a></p>&#x2014; AndroidTO (@androidTO) <a href="https://twitter.com/androidTO/status/1059942823720640512?ref_src=twsrc%5Etfw">November 6, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure>]]></content:encoded></item><item><title><![CDATA[Ohio DevFest 2018 Recap]]></title><description><![CDATA[A recap of Ohio DevFest 2018, an annual community-run conference that takes place in Ohio. Talks are about cloud, Web, agile, mobile, Kotlin, Firebase, soft skills, and more.]]></description><link>https://gnuf.dev/ohio-devfest-2018-recap/</link><guid isPermaLink="false">5bd1cbb3d243e41087e56951</guid><category><![CDATA[conference]]></category><category><![CDATA[review]]></category><category><![CDATA[DevFest]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Fri, 02 Nov 2018 14:25:37 GMT</pubDate><media:content url="https://code.gnufmuffin.com/content/images/2018/11/IMG_20181102_080242.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://code.gnufmuffin.com/content/images/2018/11/IMG_20181102_080242.jpg" alt="Ohio DevFest 2018 Recap"><p>A few weekends ago, I attended a <a href="https://devfest.withgoogle.com/">DevFest</a> jointly organized by <a href="https://www.meetup.com/cincy-android/">GDG Cincinnati</a> and <a href="https://www.meetup.com/GDG-Columbus/">GDG Columbus</a>. <a href="https://ohiodevfest.com/">Ohio DevFest</a> 2018 was held in the <a href="https://www.uc.edu/eventservices/venues/tangeman.html">Tangeman University Center</a> at the University of Cincinnati, and offered a full day of talks, organized into four tracks. Many local speakers and attendees from across the state showed up at 08:00 to get the day started.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2018/11/IMG_20181013_085133.jpg" class="kg-image" alt="Ohio DevFest 2018 Recap" loading="lazy"><figcaption><a href="https://photos.app.goo.gl/ugY5psRi3YKKwyZJ7">Photo </a>by Ohio DevFest</figcaption></figure><p>I skipped the first time slot of the morning, to do some last minute tweaking on my slides!</p><h1 id="not-har-gow-using-custom-models-with-ml-kit">Not Har Gow: Using Custom Models with ML Kit</h1><p>(<a href="https://speakerdeck.com/efung/not-har-gow-using-custom-models-with-ml-kit">Slides</a>)</p><p>I&apos;m fortunate to be encouraged and financially supported by <a href="https://www.shopify.com/careers">my employer</a> to speak at conferences. Although Shopify <a href="https://www.cantechletter.com/2017/06/machine-learning-transforming-shopify-says-paradigm-capital/">does use machine learning</a> in its core products, my interest recently has been at the intersection between ML and mobile.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://gnuf.dev/content/images/2018/11/IMG_20181013_095803-1.jpg" width="2000" height="1500" loading="lazy" alt="Ohio DevFest 2018 Recap" srcset="https://gnuf.dev/content/images/size/w600/2018/11/IMG_20181013_095803-1.jpg 600w, https://gnuf.dev/content/images/size/w1000/2018/11/IMG_20181013_095803-1.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2018/11/IMG_20181013_095803-1.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2018/11/IMG_20181013_095803-1.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://gnuf.dev/content/images/2018/11/IMG_3106.CR2--1-.jpg" width="2000" height="1333" loading="lazy" alt="Ohio DevFest 2018 Recap" srcset="https://gnuf.dev/content/images/size/w600/2018/11/IMG_3106.CR2--1-.jpg 600w, https://gnuf.dev/content/images/size/w1000/2018/11/IMG_3106.CR2--1-.jpg 1000w, https://gnuf.dev/content/images/size/w1600/2018/11/IMG_3106.CR2--1-.jpg 1600w, https://gnuf.dev/content/images/size/w2400/2018/11/IMG_3106.CR2--1-.jpg 2400w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>My view from the lectern (L), <a href="https://photos.app.goo.gl/ugY5psRi3YKKwyZJ7">Photo </a>by Ohio DevFest (R)</figcaption></figure><p>For my talk, I wanted to walk through how to use a custom TensorFlow Lite model with <a href="https://developers.google.com/ml-kit/">ML Kit</a>. One way to demonstrate this, is to take an existing image classification sample and retrain the underlying model. In my case, I used pictures of dumplings. For the live demo, I purchased <a href="https://www.seriouseats.com/2011/04/guide-to-dim-sum-dumplings-siu-mai-bao-chinese-steamed-buns.html">dim sum</a> from a <a href="https://www.orientalwok.com/">local restaurant</a>, brought some on-stage, and streamed the app&apos;s live camera view with the help of <a href="https://www.vysor.io/">Vysor</a>.</p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Learning about Firebase ML Kit from <a href="https://twitter.com/gnufmuffin?ref_src=twsrc%5Etfw">@gnufmuffin</a> at <a href="https://twitter.com/hashtag/OhioDevFest18?src=hash&amp;ref_src=twsrc%5Etfw">#OhioDevFest18</a> - and he just offered us dim sum &#x1F60D; <a href="https://t.co/lqUT8179xp">pic.twitter.com/lqUT8179xp</a></p>&#x2014; Kat Fairbanks (@DerKatzenbar) <a href="https://twitter.com/DerKatzenbar/status/1051120161850368000?ref_src=twsrc%5Etfw">October 13, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure><p>Getting feedback is usually a gift, so I was pleased when several people took the time to approach me to ask some questions, both after my talk and at the after-party as well.</p><h1 id="refactoring-saving-your-code-from-itself">Refactoring: Saving Your Code From Itself</h1><p>(<a href="https://github.com/makerlorien/ConferencePresentations/blob/master/Refactoring%20-%20Saving%20Your%20Code%20From%20Itself%20V2.pdf">Slides</a>)</p><p>The first presentation I attended was by Lorien Rensing (<a href="https://twitter.com/makerlorien">@MakerLorien</a>). She spoke about concrete techniques to refactor code, as well as quantitative methods to measure code maintainability. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2018/11/IMG_3154.CR2.jpg" class="kg-image" alt="Ohio DevFest 2018 Recap" loading="lazy"><figcaption><a href="https://photos.app.goo.gl/ugY5psRi3YKKwyZJ7">Photo</a> by Ohio DevFest</figcaption></figure><p>One of the oldest books on this subject is by <a href="https://en.wikipedia.org/wiki/Martin_Fowler">Martin Fowler</a>, who just <a href="https://martinfowler.com/articles/refactoring-2nd-ed.html">completed the 2nd edition</a> of <em>Refactoring: Improving the Design of Existing Code</em>. Lorien quoted a concise <a href="https://martinfowler.com/bliki/DefinitionOfRefactoring.html">definition</a> of refactoring given by Fowler:</p><blockquote>a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior</blockquote><p>We reviewed a number of <a href="https://en.wikipedia.org/wiki/Code_smell">code smells</a>, things that are possibly indicative of a deeper problem: long methods (hard to understand), poorly named symbols (not meaningful), duplicated code (maintenance headaches), and poor organization.</p><p>Coming from a non-Microsoft world, the <a href="https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-values?view=vs-2017">maintainability index</a> was new to me, but it&apos;s a metric that&apos;s been in Visual Studio for over a decade. It complements other measurements like <a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity">cyclomatic complexity</a> and <a href="https://en.wikipedia.org/wiki/Source_lines_of_code">SLOC</a>.</p><p>I also learned about the <a href="https://github.com/emilybache/GildedRose-Refactoring-Kata">Gilded Rose Refactoring Kata</a>, an exercise to give you <strong>deliberate practice</strong> in refactoring. Also, JetBrains offers a code-analysis and refactoring extension for .NET developers called <a href="https://www.jetbrains.com/resharper/">ReSharper</a>.</p><h1 id="slaying-the-long-lived-branch">Slaying the Long-Lived Branch</h1><p>(<a href="https://github.com/katzenbar/talks/blob/master/2018/ohio-dev-fest/slides.pdf">Slides</a>)</p><p>As a fan of small, focused pull requests, I looked forward to hearing this talk by Kat Fairbanks (<a href="https://twitter.com/DerKatzenbar">@DerKatzenbar</a>) about dealing with branches that incorporate a significant amount of work. In her particular case, she was asked to review <strong>3 weeks</strong> of work over <strong>90 commits</strong>, which, when deployed, led to a production error requiring a rollback.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2018/11/IMG_3191.CR2.jpg" class="kg-image" alt="Ohio DevFest 2018 Recap" loading="lazy"><figcaption><a href="https://photos.app.goo.gl/ugY5psRi3YKKwyZJ7">Photo </a>by Ohio DevFest</figcaption></figure><p>It&apos;s obvious that a massive set of changes is painful to review, and even the most experienced reviewer can miss something in the sea of code they are examining. Kat brought up the <strong>human element</strong> in code review. Feedback can be hard to give to someone, and a conscientious reviewer might be reluctant to point out a problem, because it would result in another QA pass.</p><p>Some sensible strategies Kat suggested were not adding small, unrelated changes to a pull request; keeping refactoring separate from feature work; and finding a balance between fewer, but larger PRs, and the cognitive overhead of many small, unrelated PRs.</p><p>On the topic of rolling out a huge set of changes into production, we learned about a few ways to reduce risk when doing so. One was the concept of <a href="https://martinfowler.com/articles/feature-toggles.html">feature toggles</a>, deploying functionality that can be turned on and off. Of course, adding conditionals introduces its own complexity.</p><p>Another way to roll out large features is to do so <strong>incrementally</strong>, by breaking down large features into smaller chunks, and introducing them one at a time. Kat recommended reading GitHub&apos;s <a href="https://githubengineering.com/upgrading-github-from-rails-3-2-to-5-2/">blog post</a> about how they upgraded their 10-year old code base over 1.5 years to the most recent version of Rails. (Shopify&apos;s engineering blog also has an article about <a href="https://engineering.shopify.com/blogs/engineering/upgrading-shopify-to-rails-5-0">how we upgraded to Rails 5</a> last year.)</p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">.<a href="https://twitter.com/DerKatzenbar?ref_src=twsrc%5Etfw">@DerKatzenbar</a> gave a great talk on version control and long lived branches! <a href="https://t.co/a7Xq98x57S">pic.twitter.com/a7Xq98x57S</a></p>&#x2014; Daniel Posthuma (@DanielJamesPost) <a href="https://twitter.com/DanielJamesPost/status/1051177990518960129?ref_src=twsrc%5Etfw">October 13, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure><h1 id="large-scale-resource-planning-and-management">Large Scale Resource Planning and Management</h1><p>I went to this session mostly out of curiosity, as I know nothing about how &quot;the Cloud&quot; is actually built. Arathi Mani spoke about how Google&apos;s datacenters are physically planned, built, and managed.</p><p>One of the first slides in her deck raised several dozen questions related to datacenters including: Where should you build it? Where do you get power from? Do you use existing hardware or design your own? Consider that every new feature in every web app (e.g. Gmail) needs its compute and storage needs to be <strong>forecast</strong>, in order to plan when to build another datacenter (each of which costs $200 million USD). Arathi mentioned an example of when a forecast is wrong, the <a href="https://cloud.google.com/blog/products/gcp/bringing-pokemon-go-to-life-on-google-cloud">launch of Pok&#xE9;mon Go</a>.</p><p>There were so many eye-opening facts and figures in this talk:</p><ul><li>Google&apos;s 3-year trailing <a href="https://www.investopedia.com/terms/c/capitalexpenditure.asp">capital expenditure</a> is a staggering $40 billion</li><li><a href="https://www.sans.org/private-training/course/physical-penetration-testing">Physical pen tests</a> are conducted annually</li><li>Collectively, the world&apos;s datacenters use 2% of all <strong>electricity</strong> (3% according to <a href="https://www.forbes.com/sites/forbestechcouncil/2017/12/15/why-energy-is-a-big-and-rapidly-growing-problem-for-data-centers/">this Forbes article</a>)</li><li><a href="https://deepmind.com/blog/deepmind-ai-reduces-google-data-centre-cooling-bill-40/">AI can be used</a> to improve Power Usage Effectiveness, the ratio between energy used to run computers versus cooling and other overhead</li></ul><p>This talk felt like a <a href="https://www.google.ca/about/datacenters/">promotion for Google&apos;s datacenters</a>, but I still enjoyed learning about what it takes to operate at Google&apos;s scale.</p><h1 id="panel-non-traditional-paths-to-technology">Panel: Non-Traditional Paths to Technology</h1><p>The last session I attended featured a panel of women and men who didn&apos;t land in tech in the &quot;traditional&quot; way, i.e. study computer science or programming at school. Their previous careers included receptionist, audiologist, marketer, legal assistant, and sheet metal fabricator. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2018/11/IMG_3252.CR2.jpg" class="kg-image" alt="Ohio DevFest 2018 Recap" loading="lazy"><figcaption><a href="https://photos.app.goo.gl/ugY5psRi3YKKwyZJ7">Photo </a>by Ohio DevFest</figcaption></figure><p>I learned about some of the local programs to get you started on a tech career:</p><ul><li><a href="https://maxtrain.com/career/">Cincy Code IT Bootcamp</a></li><li><a href="https://weare.techohio.ohio.gov/2017/07/31/i-c-stars-offers-new-opportunities-in-the-technology-industry/">i.c.stars</a>, which offers a free bootcamp to qualified interns</li><li><a href="https://operationcode.org/about">Operation Code</a>, a non-profit that helps the military community</li></ul><p>With so much instructional content available online, someone raised a question about whether a bootcamp or <a href="http://mooc.org/">MOOC</a> was better. Of course, the answer completely depends on the learner, but someone pointed out that in-person interaction, like learning with other students, and networking with people at meetups, can be important. While changing careers requires learning new skills, there are character traits such as grit, resilience, being curious, being confident, etc. that can help you succeed in whatever you do.</p><h1 id="fireside-chat">Fireside Chat</h1><p>I wandered into the last event of the day, which turned out to be a casual Q&amp;A with all of the speakers.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2018/11/IMG_3262.CR2.jpg" class="kg-image" alt="Ohio DevFest 2018 Recap" loading="lazy"><figcaption><a href="https://photos.app.goo.gl/ugY5psRi3YKKwyZJ7">Photo </a>by Ohio DevFest</figcaption></figure><p>Some tidbits from our discussion:</p><ul><li>Don&apos;t stay at your first job too long (not for 9 years!)</li><li>You have the ultimate responsibility for your own career</li><li>Everyone thinks machine learning is the <em>next big thing</em></li></ul><h1 id="after-party">After Party</h1><p>The day ended with beer and pizza at the wonderful <a href="https://rhinegeist.com/visit-us/taproom/">Rhinegeist Taproom</a>. I didn&apos;t know that Cincinnati has an almost 200-year history of breweries, thanks to waves of German immigrants arriving in the mid-1800s.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://gnuf.dev/content/images/2018/11/IMG_20181013_181751.jpg" class="kg-image" alt="Ohio DevFest 2018 Recap" loading="lazy"><figcaption>Who knew <a href="https://en.wikipedia.org/wiki/Cornhole">cornhole</a> is so popular?</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Android Summit 2018 Recap]]></title><description><![CDATA[A recap of Android Summit 2018, an annual Android conference that takes place in the Virginia/DC area. Speakers are organized into three tracks: development, design, and, testing.]]></description><link>https://gnuf.dev/android-summit-2018-recap/</link><guid isPermaLink="false">5b7578086774890696520ba0</guid><category><![CDATA[conference]]></category><category><![CDATA[review]]></category><category><![CDATA[Android Summit]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Tue, 21 Aug 2018 20:30:00 GMT</pubDate><media:content url="https://code.gnufmuffin.com/content/images/2018/08/IMG_20180816_170851.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://code.gnufmuffin.com/content/images/2018/08/IMG_20180816_170851.jpg" alt="Android Summit 2018 Recap"><p>Last week, I attended and spoke at <a href="https://androidsummit.org/">Android Summit</a>, in Tysons Corner, VA, just outside of Washington, DC. This was my first time at the annual event, which was celebrating its 4th anniversary.</p>
<p>The conference had two development tracks, a design track, and a testing track. During the introductory remarks, <a href="https://twitter.com/jonesmej">Michael Jones</a> reiterated the message that apps are made collaboratively by people in different disciplines, and encouraged us to mingle and attend sessions from different tracks.</p>
<p>This year, proceeds from the event went to <a href="https://www.womenwhocode.com/">Women Who Code</a>, and, with an intentional reduction in swag, Android Summit was able to donate over $8000 to this not-for-profit.</p>
<p>I thought the conference was really well run, with lots of dedicated volunteers staffing the registration table, herding attendees between the two floors of the hotel, and ensuring the A/V was set up for the speakers. The venue is directly connected to a really large mall which had an enormous Italian restaurant where we had the speaker dinner. Nearby, I was within walking distance to a <a href="http://locations.harristeeter.com/va/mclean/302/">24 hour grocery</a>, several restaurants, and a <a href="https://www.wmata.com/rider-guide/stations/tysons-corner.cfm">Metrorail station</a>.</p>
<h1 id="keynote">Keynote</h1>
<p><a href="https://www.claireslattery.com/">Claire Slattery</a> opened the conference. She is the Director of Performance at <a href="https://speechlesslive.com/">Speechless</a>, which performs shows to help people become better presenters. Her keynote was called, <strong>Creative Release: Why Improv Thinking Matters for Development</strong>. The title refers to applying techniques from <a href="https://en.wikipedia.org/wiki/Improvisational_theatre">improv theatre</a>, such as listening with curiosity, making others look and feel good, and taking care of yourself, etc. to our work.</p>
<p>Speechless&apos; scientist-in-residence is Charles Limb, who has studied what happens to our brains when musicians improvise (<a href="https://www.ted.com/talks/charles_limb_your_brain_on_improv">TEDx talk</a>, <a href="https://www.pri.org/stories/2018-05-23/your-brain-improv">PRI podcast</a>). She encouraged us to work on changing the &quot;volume&quot; in our brains, by turning <em>up</em> our self-expression and creativity, and turning <em>down</em> the voice of our self-critic.</p>
<p><img src="https://gnuf.dev/content/images/2018/08/IMG_20180816_095818.jpg" alt="Android Summit 2018 Recap" loading="lazy"></p>
<p>Claire led us through a number of small group, stand-up exercises, where we practised celebrating mistakes, listening actively, building presence, and creatively connecting our work and our interests.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/lulgR9XSUEU?feature=oembed" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></figure><!--kg-card-begin: markdown--><h1 id="positivesumdevelopingmobileappswithprivacyasabaselineneed">Positive Sum &#x2014; Developing Mobile Apps with Privacy as a Baseline Need</h1>
<p>As I am transitioning to a new discipline soon, I decided to attend mostly non-development talks. This talk, on privacy and GDPR, was given by Noble Ackerson (<a href="https://twitter.com/nobleackerson?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor">@nobleackerson</a>).</p>
<p>Noble went through a few &quot;privacy by design&quot; principles: consent (asking for permission to collect data), agency (allowing users to control their data), progressive disclosure (collect data as you need it, not all up front), governance (being able to monitor what&apos;s collected).</p>
<p>While it may be a burden to be careful with data, privacy principles align with good UX in most apps, so that we can gain trust from our users by being transparent with how their data is being used.</p>
<p>During the Q&amp;A, I learned of several people to pay attention to who are very involved in privacy:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Ann_Cavoukian">Ann Cavoukian</a></li>
<li><a href="https://theintercept.com/2016/03/30/edward-snowden-noam-chomsky-glenn-greenwald-a-conversation-on-privacy/">a conversation about privacy with Edward Snowdon, Noam Chomsky, and Glenn Greenwald</a></li>
</ul>
<!--kg-card-end: markdown--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/jBAO-_M_7rI?feature=oembed" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></figure><!--kg-card-begin: markdown--><h1 id="excellenceprinciplesthatproducequalitymobilesolutions">Excellence Principles that Produce Quality Mobile Solutions</h1>
<p>I went from the design track over to the testing track for my 2nd talk of the day. This one was given by Elvin Rakhmankulov, Head of Mobile Competency at <a href="https://www.epam.com/">EPAM</a>.</p>
<p>As a developer who has worked on teams practising varying levels of &quot;process&quot;, it was interesting to hear from a seasoned veteran. Elvin breezed through a few list-heavy slides with best practises accumulated after working on dozens of client projects.</p>
<p>Some things I learned about were:</p>
<ul>
<li>the <a href="https://c4model.com/">C4 Model</a> for describing software architecture. I&apos;ve struggled with trying to balance between high-level vs. detailed diagrams of how the pieces of an app fit together, so this framework looks helpful.</li>
<li><a href="https://www.oreilly.com/ideas/how-to-set-up-and-run-your-own-architectural-katas">architectural katas</a>, practice exercises for software architects</li>
<li>a &quot;continuous inspection&quot; dashboard for software quality called <a href="https://www.sonarqube.org/">Sonarqube</a>. It presents the results of a lot of static analysis tools on your code and revision control usage.</li>
<li>a nicer way of describing <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">using your own apps</a> (which Wikipedia attributes to Jo Hoppe):</li>
</ul>
<blockquote>
<p>drinking your own champagne</p>
</blockquote>
<!--kg-card-end: markdown--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/S4NG1LaLymI?feature=oembed" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></figure><!--kg-card-begin: markdown--><h1 id="kotlinmultiplatform">Kotlin Multiplatform</h1>
<p>This topic is a perennial favourite amongst mobile developers, as it deals with how to build apps for iOS and Android from one codebase. Kevin Galligan, from Touchlab (and organizer of <a href="https://www.nyc.droidcon.com/">droidconNYC</a>) sped through the state of <a href="https://kotlinlang.org/docs/reference/multiplatform.html">Kotlin Multiplatform</a>. It was based roughly on <a href="https://medium.com/@kpgalligan/sqlite-sqldelight-%EF%B8%8F-kotlin-multiplatform-f24fe7cba338">this post</a>.</p>
<p>Not only can iOS (native) and Android (JVM) be targeted, but Kotlin multiplatform can render to JavaScript as well. Kevin went on a bit of a rant about a lot of the unhelpful discussion around cross-platform development, that often compares apples to oranges, missing the more nuanced reality.</p>
<p><img src="https://gnuf.dev/content/images/2018/08/IMG_20180816_132942.jpg" alt="Android Summit 2018 Recap" loading="lazy"></p>
<p>His own contribution to the field is <a href="https://github.com/touchlab/knarch.db">knarch.db</a>, an implementation of SQLite for iOS/macOS that is similar to the Android API, written in Kotlin/Native. In general, at this point in time, library support is what&apos;s missing, so this is a good area for open-source contributors to get involved in. Still, there&apos;s enough support available, that he <a href="https://medium.com/@kpgalligan/droidcon-nyc-app-da868bdef387">built</a> the Droidcon NYC app for Kotlin multiplatform.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/z2GxHHOyrqk?feature=oembed" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></figure><!--kg-card-begin: markdown--><h1 id="materialdesigninteractions">Material Design Interactions</h1>
<p>I went back to the design room to watch <a href="https://www.amandalegge.me/">Amanda Legge</a>, Principal Experience Designer at Capital One, do a live demo using <a href="http://principleformac.com/">Principle</a>. In around a half an hour, she built <strong>three</strong> animated and interactive components that you would find in modern Android apps.</p>
<p>Her demos included making a circular progress spinner with in/out animations, replicating a parallax effect from Google Calendar, and a complicated drag interaction that modified text opacity and scaled another element.</p>
<p><img src="https://gnuf.dev/content/images/2018/08/IMG_20180816_142425.jpg" alt="Android Summit 2018 Recap" loading="lazy"></p>
<p>One significant limitation of Principle is that it does not directly output files (e.g. XML layouts, drawables, animations) that can be imported into Android projects. By contrast, Zeplin can now <a href="https://support.zeplin.io/zeplin-101/developing-android-projects-using-zeplin">generate resources and snippets</a> for Android.</p>
<p>You can find more Principle tutorials on Amanda&apos;s <a href="https://www.youtube.com/channel/UC6M99erdCS1EPf6a0SrCkTg/">YouTube channel</a>.</p>
<h1 id="howthecommandlinecangiveyousuperpowers">How the Command-Line Can Give You Superpowers</h1>
<p>I spent the next time slot polishing my slides, before giving my talk. I&apos;ve uploaded the <a href="https://speakerdeck.com/efung/how-the-command-line-can-give-you-superpowers-1">slides</a> and the video recording will be available soon.</p>
<p><img src="https://gnuf.dev/content/images/2018/08/IMG_20180816_144047.jpg" alt="Android Summit 2018 Recap" loading="lazy"></p>
<!--kg-card-end: markdown--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/95s4UM5lezk?feature=oembed" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></figure><!--kg-card-begin: markdown--><h1 id="ghostinthemachine">Ghost in the Machine</h1>
<p>My colleague, Etienne Caron, discussed <a href="https://developers.google.com/ar/">ARCore</a> and his experiments with 3D printing and augmented reality.</p>
<p>Inspired by the <a href="https://github.com/zugaldia/android-robocar">android-robocar</a> project, Etienne started with a <a href="https://www.servocity.com/junior">Junior Runt Rover</a>. He used <a href="https://www.autodesk.com/products/fusion-360/overview">Fusion 360</a> to design a frame, and a low-cost 3D printer to realize it. This allowed him to strap on a steroscopic camera and other components.</p>
<p>Then, he wrote an Android app to calibrate the vehicle&apos;s motion and track it via a marker. Then, he could tap at a point on the phone&apos;s viewfinder, to make the car roll to that spot!</p>
<p><img src="https://gnuf.dev/content/images/2018/08/IMG_20180816_165716.jpg" alt="Android Summit 2018 Recap" loading="lazy"></p>
<p>The source code for this project and other VR and AR experiments can be found at <a href="https://github.com/kanawish/mixed-reality-toolbox">https://github.com/kanawish/mixed-reality-toolbox</a></p>
<!--kg-card-end: markdown--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/teLGgNS2Les?feature=oembed" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></figure><!--kg-card-begin: markdown--><h1 id="closingkeynote">Closing Keynote</h1>
<p>Nitya Narasimhan (<a href="https://twitter.com/nitya">@nitya</a>) closed out the conference with <strong>Self Care for Developers: Defense Against The Dark Arts</strong> (<a href="https://bit.ly/2018-asdc-datda">slides</a>), a serious topic presented with analogies from Harry Potter. Due to technical difficulties and delays, she had to accelerate her already-rapid speech and compress her talk into 20 minutes!</p>
<p>She shared a report from the APA titled <a href="https://www.apa.org/news/press/releases/stress/index.aspx">Stress in America</a> which highlights that uncertainty about the future, and work, are major sources of stress for Americans.</p>
<p>The connection to Harry Potter was interesting: J. K. Rowling was diagnosed with depression, and her illness inspired <a href="http://harrypotter.wikia.com/wiki/Dementor">dementors</a>. Nitya contrasted various evil powers in the books, with their defenses, and made analogies with how we can protect ourselves against sources of stress.</p>
<p><img src="https://gnuf.dev/content/images/2018/08/IMG_20180816_175624.jpg" alt="Android Summit 2018 Recap" loading="lazy"></p>
<p>She outlined various strategies for combating stress. First, knowing the enemy is key: some acute stress makes you more resilient, but episodic or chronic stress wears you down, and the cumulative wear and tear on your brain reduces your ability to cope with future uncertainty.</p>
<p>Stress triggers come in many forms: environmental, physiological, physical, social. She suggested we remove time-based triggers by spending 30 days to set new habits, whether it be spending time to eat regularly, getting more sleep, or exercising more. For space-based triggers, declutter your environment (whether home, work, or online) by discarding, hiding, or blocking those sources.</p>
<p>By refocusing, we can cultivate habits to slow down. For Nitya, sketching and food preparation were tasks that helped her to calm down. Another good tip was to &quot;ally-up&quot;: finding people who know you, who can cheer for you, or to run interference for you.</p>
<p>One practical tip to help build self-appreciation was to practice saying things to reinforce your sense of self-worth, e.g. when congratulated, just say &quot;Thank you&quot;, without following up with any disclaimers or self-deprecating remarks.</p>
<p>Finally, she gave a constraints-based analogy to help us devise our own self-care strategy: think of a suitcase as holding all your mental baggage. If someone makes a stressful request of you, think about whether you have room to spare in your suitcase. If not, just say, &quot;I don&apos;t have space for your request&quot;.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Year in Review: 2017]]></title><description><![CDATA[A review of my accomplishments in 2017: expanding my Android knowledge through Kotlin, RxJava, and Firebase; taking courses in deep learning]]></description><link>https://gnuf.dev/year-in-review-2017/</link><guid isPermaLink="false">5a5d6fa9837c1708bc1adfd8</guid><category><![CDATA[review]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Mon, 22 Jan 2018 04:55:37 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2018/01/party-sparkler-with-shiny-decor_925x.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h1 id="softwaredevelopment">Software Development</h1>
<h2 id="newskills">New Skills</h2>
<img src="https://gnuf.dev/content/images/2018/01/party-sparkler-with-shiny-decor_925x.jpg" alt="Year in Review: 2017"><p>I&apos;m pleased that I was able to expand my knowledge of the Android development ecosystem this year:</p>
<ul>
<li><a href="https://github.com/googlesamples/android-architecture/tree/todo-mvp/">Model-View-Presenter</a>: the Logo Maker app gave me an opportunity to look into this architecture for the first time, and apply it to a real app.</li>
<li>Custom Views: the logo is rendered with a composite view that uses dynamic resizing of multiple elements. I&apos;ll write a blog post about it sometime.</li>
<li><a href="https://github.com/ReactiveX/RxJava/wiki">RxJava</a>: After many months of sitting on the sidelines, I finally got a chance to learn and apply reactive programming in an app.</li>
<li><a href="https://kotlinlang.org/">Kotlin</a>: The timing of Google&apos;s announcement for Kotlin support was right around the time we started on Hatchful.</li>
<li><a href="http://hannesdorfmann.com/android/mosby3-mvi-1">Model-View-Intent</a>: One of my colleagues was keen on exploring this architecture, which we used in Hatchful.</li>
<li>More <a href="https://firebase.google.com/">Firebase</a>: There&apos;s over a dozen products in this platform, but this year, I was able to use the <strong>realtime database</strong>, <strong>cloud storage</strong>, <strong>cloud functions</strong>, and <strong>performance monitoring</strong>, in a production app.</li>
<li><a href="https://developer.android.com/google/play/billing/index.html">In-App Billing</a>: This is the first time that I&apos;ve had to deal with IAP, and managing SKUs in the Developer Console.</li>
</ul>
<h2 id="apps">Apps</h2>
<p>At work, I contributed to two new Android apps at Shopify:</p>
<h3 id="imgsrc__ghost_url__contentimages201801logomakerpnginlinestyleheight75pxwidth75pxlogomakerspan"><img src="https://gnuf.dev/content/images/2018/01/logomaker.png#inline" style="height: 75px; width: 75px;" alt="Year in Review: 2017"> <a href="https://play.google.com/store/apps/details?id=com.shopify.logomaker.logogenerator">Logo Maker</a></h3>
<p>In April, we launched a basic logo maker, allowing you to customize text, icon, color, and basic placement.</p>
<h3 id="imgsrc__ghost_url__contentimages201801hatchfulpnginlinestyleheight75pxwidth75pxhatchful"><img src="https://gnuf.dev/content/images/2018/01/hatchful.png#inline" style="height: 75px; width: 75px;" alt="Year in Review: 2017"> <a href="https://play.google.com/store/apps/details?id=com.shopify.logomaker.hatchful">Hatchful</a></h3>
<p>In November, we launched a more polished brand asset app that launched simultaneously for iOS and Android.</p>
<h1 id="education">Education</h1>
<p>For the past year, I&apos;ve been thinking about earning a master&apos;s degree. I like the idea of more formalized education, and the discipline of learning in a structured environment with a cohort of peers.</p>
<p>I looked into Georgia Tech&apos;s <a href="https://www.omscs.gatech.edu/">OMSCS</a> (an online CS Master&apos;s), but wasn&apos;t sure whether I could sustain interest and apply myself in a completely online environment. I also looked into the University of Toronto&apos;s <a href="http://web.cs.toronto.edu/Graduate/prospective_gradwhy/mscac.htm">MScAC</a> program. It combines study and work, and is suited for practising professionals, like me, but the program has very little coursework. With so many free, online resources available, I also found the idea of a <a href="http://willwolf.io/en/2016/07/29/my-open-source-machine-learning-masters-in-casablanca-morocco/">self-directed, open-source masters</a> interesting.</p>
<p>As to what area I&apos;d like to concentrate in, hardly a day goes by without some mention of artificial intelligence or <strong>machine learning</strong>, even in non-technical news sources. I&apos;ve been paying more attention to these fields to see if they&apos;re areas I&apos;d like to someday pivot into. With the formation of the <a href="https://vectorinstitute.ai/">Vector Institute</a> in Toronto, and the Ontario government&apos;s <a href="https://news.ontario.ca/medg/en/2017/10/ontario-boosting-the-number-of-graduates-in-science-tech-engineering-mathematics-and-artificial-inte.html">announcement</a> of increased funding for applied master&apos;s students in AI, this is an exciting time to be involved.</p>
<h2 id="deeplearning">Deep Learning</h2>
<p>With these thoughts rattling around in my mind, after the announcement of <a href="https://www.deeplearning.ai/">deeplearning.ai</a> in August, I decided to jump (back) into studying machine learning. (A few years ago, I started, but didn&apos;t complete, Andrew Ng&apos;s seminal ML course on Coursera.)</p>
<p>After enrolling in the <a href="https://www.coursera.org/specializations/deep-learning">Deep Learning Specialization</a>, I&apos;m pleased to have successfully completed the first four courses this year:</p>
<ul>
<li><a href="https://www.coursera.org/learn/neural-networks-deep-learning">Neural Networks and Deep Learning</a></li>
<li><a href="https://www.coursera.org/learn/deep-neural-network">Improving Deep Neural Networks</a></li>
<li><a href="https://www.coursera.org/learn/machine-learning-projects">Structuring Machine Learning Projects</a></li>
<li><a href="https://www.coursera.org/learn/convolutional-neural-networks">Convolutional Neural Networks</a></li>
</ul>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Completed the fourth course (Convolutional Neural Networks) of the <a href="https://twitter.com/hashtag/deeplearningai?src=hash&amp;ref_src=twsrc%5Etfw">#deeplearningai</a> specialization by <a href="https://twitter.com/AndrewYNg?ref_src=twsrc%5Etfw">@AndrewYNg</a> on <a href="https://twitter.com/coursera?ref_src=twsrc%5Etfw">@Coursera</a>. Here are some neural style transfer images I created as part of an assignment. <a href="https://t.co/74uaTZWzK7">pic.twitter.com/74uaTZWzK7</a></p>&#x2014; Eric Fung (@gnufmuffin) <a href="https://twitter.com/gnufmuffin/status/950204961132089344?ref_src=twsrc%5Etfw">January 8, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The subjects have been really interesting, so I&apos;ll continue to take more online courses this year, after finishing the DL specialization.</p>
<h1 id="publicspeakingandwriting">Public Speaking and Writing</h1>
<p>In contrast to <a href="https://code.gnufmuffin.com/year-in-review-2016/#publicspeaking">last year</a>, I wasn&apos;t accepted to speak at any conferences this year. I&apos;m not sure how many CfPs I submitted, but it was less than five. My proposal ideas weren&apos;t that original, so I&apos;m going to re-think my talk ideas some more, and try again this year.</p>
<p>The <a href="https://www.meetup.com/ToAndroidDev/">Toronto Android Meetup</a> is still going strong. We ended the year with over 700 members, and 11 events (including two rounds of lightning talks). I gave one talk at our meetup this year:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/vbHDoiMBbmA?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<p>As you can confirm, I didn&apos;t publish anything in 2017. I&apos;d like to work on that more, perhaps using a productivity tool to encourage me to meet a goal for writing on a weekly basis.</p>
<h1 id="productivity">Productivity</h1>
<p>I&apos;ve been running <a href="https://www.rescuetime.com/">RescueTime</a> on my work laptop for a long time. My <a href="https://www.rescuetime.com/year-in-review/2017/B63iUfl3wKY4U3xCB71T9iPTGWXPsasVJOCFsVkY">year in review report</a> is an interesting look back at how I spend my work time. Notably, I spend only half my day doing &quot;software development&quot;. One-quarter of my day is on &quot;communication and scheduling&quot;, that is, <a href="https://m.signalvnoise.com/is-group-chat-making-you-sweat-744659addf7d">group chat using Slack</a>.</p>
<p><img src="https://gnuf.dev/content/images/2018/01/rescuetime-2017.png" alt="Year in Review: 2017" loading="lazy"></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Year in Review: 2016]]></title><description><![CDATA[A review of my accomplishments in 2016: starting a Meetup, giving talks at external events, launching an app, and starting this blog.]]></description><link>https://gnuf.dev/year-in-review-2016/</link><guid isPermaLink="false">59814fce4731ff4690afb990</guid><category><![CDATA[review]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Wed, 11 Jan 2017 11:00:00 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2017/02/2017-02-14-22.09.54.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gnuf.dev/content/images/2017/02/2017-02-14-22.09.54.jpg" alt="Year in Review: 2016"><p>Inspired by a <a href="http://blog.sqisland.com/2015/12/learn-to-celebrate.html">post</a> I read on Chiu-ki Chan&apos;s <a href="http://blog.sqisland.com/">blog</a> a while ago, here is a look back at what I accomplished during the year:</p>
<h2 id="startedameetup">Started a Meetup</h2>
<p>I was somewhat surprised to discover that an Android meetup for Toronto developers didn&apos;t exist at the beginning of the year (a previous group became inactive at the end of 2015). With the support of my employer, I decided to start one myself.</p>
<p>After getting in touch with the previous organizers, <a href="https://www.meetup.com/ToAndroidDev/">Toronto Android Developers Meetup</a> was  formed and began meeting in April. During the year, we held <strong>8 meetups</strong>, grew our membership to over <strong>400 people</strong>, and have received a lot of encouraging, positive feedback about the need for such a community.</p>
<h2 id="publicspeaking">Public Speaking</h2>
<p>One of my professional goals for the year was to speak at external events. I was inspired by Donn Felker&apos;s <a href="http://www.donnfelker.com/the-single-best-thing-you-can-do-for-your-career/"><em>The Single Best Thing You Can Do For Your Career</em></a>, and the excellent book <a href="http://femgineer.com/present-book/"><em>Present! A Techie&apos;s Guide to Public Speaking</em></a> by Poornima Vijayashanker and Karen Catlin.</p>
<p>I ended the year submitting to <strong>five</strong> CfPs. (The deadlines for several other conferences I wanted to submit to were in my calendar, but I didn&apos;t follow through. Something to work on in 2017&#x2026;)</p>
<p>I was accepted to <strong>two</strong> events and gave these talks:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=N31imAjtuAQ">Set Course for Notifications&#x2026; Engage!</a> at <a href="http://archive.devfestdc.org/">DevFest DC</a>
<ul>
<li>An introduction to Firebase Notifications, and how I used them in an Android app I developed for work</li>
</ul>
</li>
<li><a href="https://www.youtube.com/watch?v=EBVSbHiuyig">Playing with Fire(base)</a> at <a href="https://devfestflorida.org/">DevFest Florida</a>
<ul>
<li>A survey of several Firebase technologies I used to re-engage users for an Android app I built</li>
</ul>
</li>
</ul>
<p>In addition, I gave numerous presentations at work and at the Meetup.</p>
<h2 id="launchedanewapp">Launched a new app</h2>
<p>As the sole mobile developer on a team I joined in the middle of the year, I built and launched <a href="https://play.google.com/store/apps/details?id=com.shopify.blog&amp;referrer=utm_source%3Dgnufmuffin%26utm_medium%3Dblog">Entrepreneur&apos;s Digest</a>. The app gave me a chance to explore Firebase, and was fertile ground in finding material for talks.</p>
<h2 id="startedthisblog">Started this blog</h2>
<p>I don&apos;t consider myself a good author (and struggle with writer&apos;s block a lot). But, I decided a good way to practise was to start writing technical articles.</p>
<h2 id="thisyear">This year</h2>
<ul>
<li><strong>Read more</strong>: Make time to read more books of all kinds: software development classics, fiction, etc.</li>
<li><strong>Watch more</strong>: I learn best from reading words, but I&apos;m finding more and more useful content in the form of videos (conference talks) and screencasts (e.g. <a href="https://caster.io/">https://caster.io/</a> for Android developers)</li>
<li><strong>Increase productivity</strong>: As I get more experience, I shouldn&apos;t be making the same mistakes. I want to try and identify bad habits and find new efficiencies.</li>
<li><strong>Reduce distractions</strong>: I started using <a href="https://www.rescuetime.com/">RescueTime</a> to track where the time in my day goes.</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[DevFest Florida 2016 Recap]]></title><description><![CDATA[A report on DevFest Florida 2016, which took place November 5. Talks about Android, Firebase, software development, and career.]]></description><link>https://gnuf.dev/devfest-florida-recap/</link><guid isPermaLink="false">59814fce4731ff4690afb98f</guid><category><![CDATA[conference]]></category><category><![CDATA[DevFest]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Tue, 08 Nov 2016 17:00:00 GMT</pubDate><media:content url="https://gnuf.dev/content/images/2017/01/IMG_20170116_210800-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gnuf.dev/content/images/2017/01/IMG_20170116_210800-1.jpg" alt="DevFest Florida 2016 Recap"><p>Another conference I was accepted into this year, was <a href="https://devfestflorida.org/">DevFest Florida</a>. This was my second speaking engagement for the year, after my debut at <a href="http://code.gnufmuffin.com/devfest-dc-day-2/">DevFest DC</a>.</p>
<p>Although I was editing and rehearsing right until the last minute, I had the opportunity to attend several of the talks:</p>
<h1 id="innovatingisnotabouttechnologybutpsychology">Innovating Is Not About Technology, But Psychology</h1>
<p>Alyssa Nicoll (<a href="https://twitter.com/AlyssaNicoll">@alyssanicoll</a>) gave the keynote on the topic of motivation for developers, and how to get into the mindset to achieve personal growth.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">At <a href="https://twitter.com/hashtag/DevFestFL?src=hash">#DevFestFL</a> and <a href="https://twitter.com/AlyssaNicoll">@AlyssaNicoll</a> is giving the keynote! <a href="https://t.co/4SoB0xvvJ7">pic.twitter.com/4SoB0xvvJ7</a></p>&#x2014; Mike Traverso &#x1F384; (@traversoft) <a href="https://twitter.com/traversoft/status/794909898844803072">November 5, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
There were a lot of notable questions and suggestions I jotted down in the first part of her talk:
<ul>
<li>Many developers strive to be well-read, perhaps as a goal in itself</li>
<li>Why are we here? To be a life-long learner? To be around other developers?</li>
<li>When asked &quot;What do you struggle with?&quot;, responses typically included &quot;fear of learning something new&quot;, &quot;fear of getting stuck&quot;, &quot;fear of looking dumb&quot;</li>
<li>It&apos;s OK to be afraid, but don&apos;t let that fear crush you. You can&apos;t do everything alone.</li>
<li>Try to surround yourself with &quot;winners&quot;: passionate, not complacent, people. Not people who sit around, wait around, wish for something to happen.</li>
</ul>
<p>Alyssa gave three steps to help you get beyond sitting, waiting and wishing:</p>
<ul>
<li><strong>Ask <em>Why</em> More</strong>: This is an important mindset for developers. It helps you learn new things, challenge yourself, and grow.</li>
<li><strong>Teach</strong>: Influential developers are innately sharing with others, but also learn to be self-taught.</li>
<li><strong>Want More</strong>: Are you growing and being challenged? Do you recognize what you are worth and what your potential is?</li>
</ul>
<p>An essay that Alyssa shared was <a href="http://www.npr.org/sections/monkeysee/2011/04/21/135508305/the-sad-beautiful-fact-that-were-all-going-to-miss-almost-everything">The Sad, Beautiful Fact That We&apos;re All Going To Miss Almost Everything</a> by Linda Holmes. One memorable sentence from it is <em>&quot;You will die having missed almost everything.&quot;</em> The key observation is that with the number of books, TV shows, social media, and other sources of information, you will never see the vast majority of them in your lifetime. Culling, or being more selective, is not enough. Learning to <strong>surrender</strong> to this &quot;sad, beautiful fact&quot; allows us to be well-read via &quot;making a genuine effort to explore thoughtfully&quot;, rather than trying to keep up.</p>
<p>The keynote was quite short, maybe 20 minutes, but Alyssa engaged us in a group discussion for the remainder of the time. We talked about the <a href="https://medium.com/@aliciatweet/overcoming-impostor-syndrome-bdae04e46ec5#.xqocrqj5g">imposter syndrome diagram</a> and a few other things, and she took notes to use for future iterations of her talk.</p>
<p>Here&apos;s Alyssa giving an Ignite! version of this talk at another conference:</p>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/fG7qdkAlA-8?rel=0" frameborder="0" allowfullscreen></iframe>
<h1 id="stayingrelevantforallyouryears">Staying Relevant for All Your Years</h1>
<p><a href="https://docs.google.com/presentation/d/1P9OdO3VqtaNXeeB4RqwZI8Lga-z1_jxLFCS2AO9jp_Q/edit?usp=sharing">(Slides)</a></p>
<p>Christi Kapp (<a href="https://twitter.com/artinmotionllc">@artinmotionllc</a>) spoke about her personal journey of innovation and entrepreneurship.</p>
<p>She spoke about the importance of staying <strong>relevant</strong> throughout your career, and to keep up with the changing winds of technology. Like with animal or plant species, technology also goes extinct. Careers also have phases. Using her own trajectory as an example, Christi graphed her career along a time vs. satisfaction axes. It was interesting to note her satisfaction fluctuating when changing jobs, the correlation (both positive and negative) between satisfaction and money, and how she found retiring and travelling boring.</p>
<p>Her keys to successfully thriving were to:</p>
<ul>
<li>Be open to <strong>non-tech</strong> related career directions</li>
<li>Listen and pay attention to patterns in data (e.g. <strong>exponential curves</strong> signalling breakout technologies, money being invested in certain areas)</li>
<li>Being friendly and collaborative, making positive and productive <strong>connections</strong> with people</li>
<li>Surf the waves of technology and trends: be ready to <strong>jump</strong> to the next wave when the current one is about to crash</li>
</ul>
<h1 id="multiwindowandyourapp">Multi-Window and Your App</h1>
<p><a href="https://commonsware.com/presos/2016-11-MW-ChromeOS/#/">(Slides)</a></p>
<p>Mark Murphy (<a href="https://twitter.com/commonsguy">@commonsguy</a>) spoke about <a href="https://developer.android.com/guide/topics/ui/multi-window.html">multi-window support</a> in Android 7.0</p>
<p>This talk was right before mine, so I was feeling nervous and didn&apos;t take any notes at all.</p>
<h1 id="playingwithfirebase">Playing with Fire(base)</h1>
<p><a href="https://speakerdeck.com/efung/playing-with-fire-base-devfest-florida">(Slides)</a></p>
<p>Because I enjoy creating extra work for myself, I didn&apos;t repeat my <a href="https://speakerdeck.com/efung/set-course-for-notifications-dot-dot-dot-engage-devfest-dc">previous talk</a> about Firebase Notifications, but created a new talk to cover a few more Firebase technologies:</p>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/EBVSbHiuyig?rel=0" frameborder="0" allowfullscreen></iframe>
<h1 id="designingasadeveloper">Designing as a Developer</h1>
<p>Justin Mitchell <a href="https://twitter.com/itsthisjustin">(@itsthisjustin)</a> spoke about how developers can become better at design.</p>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/OsGj0VOzG_s?rel=0" frameborder="0" allowfullscreen></iframe>
<p>Right off the bat, Justin spoke about the products that developers create, whether they had a UI or not. He emphasized the importance of solving problems for users, not just fulfilling a set of requirements. Here are tools and ideas organized under four areas that Justin shared:</p>
<h2 id="usertesting">User Testing</h2>
<ul>
<li>Don&apos;t overlook putting your app/product/website in front of people who haven&apos;t seen it before</li>
<li>A service that provides audio/video feedback on your website or mobile app is <a href="https://usertesting.com">https://usertesting.com</a>. You select the target audience criteria, and they match you up from their panel of a million testers.</li>
<li>The subreddit <a href="https://www.reddit.com/r/beermoney/">/r/beermoney</a> can be used to solicit feedback on your upcoming product launch in exchange for a few bucks.</li>
<li><a href="https://lookback.io/">https://lookback.io/</a> is a user research tool for Mac desktop, iOS and Android that records users playing with your app, and their feedback. Not only is their face and voice captured, but what they saw and where they touched.</li>
<li><a href="https://usabilityhub.com/">https://usabilityhub.com/</a> helps you run design tests in the form of quick surveys (e.g. what do you recall after 5 seconds, do you prefer A or B, where would you click to do this)</li>
</ul>
<h2 id="prototyping">Prototyping</h2>
<ul>
<li>Don&apos;t discount quick sketches on paper or napkins</li>
<li>Two website and mobile app prototyping tools I hadn&apos;t heard of were <a href="https://marvelapp.com/">Marvel</a> and <a href="https://proto.io/">Proto.io</a></li>
<li>A very powerful and sophisticated UX prototyping tool (with poor UX itself!) is <a href="https://www.axure.com/">Axure</a></li>
<li>Someone in the audience mentioned his company&apos;s product, <a href="http://giveabrief.com/">Briefs</a>, which makes rich prototypes on Mac and iOS (unfortunately, not Android).</li>
</ul>
<h2 id="stealing">&quot;Stealing&quot;</h2>
<ul>
<li>There are a lot of free or low-cost, high quality icons out there, so developers have no excuse</li>
<li><a href="https://thenounproject.com/">The Noun Project</a> is a paid service ($10/mo) with a desktop app that lets you drag-and-drop from 500,000 vector icons. They also offer a free tier, with attribution.</li>
<li><a href="https://www.lingoapp.com/">Lingo</a> is a visual asset library that helps ensure consistency among teams</li>
<li>Over 9000 templates and assets are available from <a href="https://elements.envato.com/">Envato Elements</a>. A yearly subscription gives you unlimited downloads.</li>
</ul>
<h2 id="utilities">Utilities</h2>
<ul>
<li><a href="http://www.eternalstorms.at/yoink/">Yoink</a> ($10) is a Mac drag-and-drop utility that acts like a virtual shelf to simplify moving things around. It provides a section of invisible screen that acts like a cut-and-paste buffer.</li>
<li>If you&apos;ve ever been annoyed by your menubar icons being hidden by your application menu, you need <a href="https://www.macbartender.com/">Bartender 2</a> ($20). It organizes your menu bar apps, and lets you navigate by keyboard and search them too.</li>
<li>A free colour picker tool called <a href="http://sipapp.io/">Sip</a> makes it dead easy to sample the colour of any on-screen pixel, and then output it in a variety of IDE-ready formats (e.g. if you specify Android ARGB it would copy <code>Color.argb(255, 55, 111, 203)</code> to the clipboard)</li>
<li>For file sharing, particularly for visual assets like screenshots or screen recordings, <a href="https://jumpshare.com/">Jumpshare</a> offers commenting, file previews, and more.</li>
<li>If you want to outsource user testing, feedback and research, <a href="https://www.applause.com/">Applause</a> can handle everything from handling NDAs to signing apps, etc.</li>
</ul>
<h1 id="writingtestableapps">Writing Testable Apps</h1>
<p>Matt Dupree (<a href="https://twitter.com/philosohacker">@philosohacker</a>) gave an elegant talk about how to make Android code more testable.</p>
<p>He began with the obvious statement that &quot;tested apps are better apps&quot;, and shared a few quotes from prominent developers like <a href="https://dannorth.net/">Dan North</a>. I particularly enjoyed Matt&apos;s skewering of the <a href="https://github.com/google/iosched">Google I/O app for Android</a>. But instead of just criticizing, he demonstrated how to refactor parts of the app to make it more testable.</p>
<p>Matt kept coming back to the concept of <a href="http://www.informit.com/articles/article.aspx?p=359417">seams</a>, which is from <a href="https://michaelfeathers.silvrback.com/">Michael Feathers</a>&apos; book,<br>
<a href="https://www.amazon.ca/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052">Working Effectively with Legacy Code</a>. A seam is a place to alter behaviour, without editing in that place. In the Android programming world, think of build variants (link seams), or dependency injection of mocks (object seams). Link seams can be used to swap out factory classes, so that you can use object seams. For example, for testing, you can swap in a factory class that returns a mocked version of a network service.</p>
<p>There were a lot of terms and concepts that I need to study more thoroughly, and patterns that I need to explore. Fortunately, Matt has been writing a lot about Android testing in his blog, <a href="http://www.philosophicalhacker.com/">Philosophical Hacker</a>, which I&apos;ve added to my reading list.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[AndroidTO 2016 Recap]]></title><description><![CDATA[A report on AndroidTO 2016, a GDG DevFest event about Android, that took place on November 2. ]]></description><link>https://gnuf.dev/android-to-2016-recap/</link><guid isPermaLink="false">59814fce4731ff4690afb98e</guid><category><![CDATA[conference]]></category><category><![CDATA[AndroidTO]]></category><dc:creator><![CDATA[Eric Fung]]></dc:creator><pubDate>Fri, 04 Nov 2016 22:20:00 GMT</pubDate><media:content url="https://code.gnufmuffin.com/content/images/2016/12/IMG_20161102_090107.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://code.gnufmuffin.com/content/images/2016/12/IMG_20161102_090107.jpg" alt="AndroidTO 2016 Recap"><p>Now in its 7th year, the <a href="http://androidto.com/">AndroidTO</a> conference was held at the beginning of November. Despite being an Android-focused event, there were a few, general mobile talks that were nevertheless quite interesting. Here&apos;s my rundown of the sessions I attended:</p>
<h1 id="3waystoaddmachinelearningtoyourapp">3 Ways to Add Machine Learning to Your App</h1>
<p>Yufeng Guo (<a href="https://twitter.com/yufengg/">@yufengg</a>), a Google Developer Advocate for the Cloud Platform, spoke about Google&apos;s machine learning initiatives.</p>
<p>This <a href="https://developers.google.com/events/devfest/">DevFest</a> season, Google has really been spreading the word about ML and TensorFlow. I heard two different talks at <a href="https://gnuf.dev/devfest-dc-day-1/">DevFestDC</a> on the subjects already, so was curious to hear another Googler&apos;s perspectives.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">First talk of the day at <a href="https://twitter.com/androidTO">@androidTO</a> by <a href="https://twitter.com/yufengg">@yufengg</a> on Google&apos;s machine learning APIs, both mobile and cloud. Awesome live demos! <a href="https://t.co/9Q5DSvfXdT">pic.twitter.com/9Q5DSvfXdT</a></p>&#x2014; Eric Fung (@gnufmuffin) <a href="https://twitter.com/gnufmuffin/status/793810055946575872">November 2, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Yufeng spoke about the challenges of <strong>training</strong> and <strong>prediction</strong> in a mobile context: a lot more computational horsepower is required for the former, so even today&apos;s smartphone processors are no match for hundreds of networked CPUs. For each combination of dimension (pre-trained vs. custom, and, local vs. cloud prediction), Google offers a way to use ML in your apps:</p>
<ul>
<li>The <a href="https://developers.google.com/vision/">Mobile Vision API</a> gives you a pre-trained model with processing done on mobile. It offers fast, face detection (<em>not</em> recognition), returning image coordinates of facial features, and expression probabilities (e.g. smiling, joyfulness).</li>
<li>The <a href="https://cloud.google.com/vision/">Cloud Vision API</a> is another pre-trained model, but offers much more powerful image analysis. It can take arbitrary photos and label them with descriptions across a variety of domains, including logos and landmarks. In addition, In addition, there is a <a href="https://cloud.google.com/speech/">Speech API</a> for converting audio to text, and a <a href="https://cloud.google.com/natural-language/">Natural Language API</a> for analyzing the structure and meaning of text.</li>
<li>For all other custom applications, there&apos;s <a href="https://www.tensorflow.org/">TensorFlow</a>, whether it&apos;s used on mobile or cloud.</li>
</ul>
<p>Yufeng even did a few live demos involving the audience:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Thanks for giving me the thumbs up <a href="https://twitter.com/androidTO">@androidTO</a> <a href="https://t.co/XYwBJ7k3Qq">pic.twitter.com/XYwBJ7k3Qq</a></p>&#x2014; Yufeng G (@yufengg) <a href="https://twitter.com/yufengg/status/793813270528299008">November 2, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="superchargingyourandroidreleasewithfastlane">Supercharging Your Android Release with Fastlane</h1>
<p>Andrea Falcone (<a href="https://twitter.com/asfalcone">@asfalcone</a>) gave the <a href="https://gnuf.dev/droidcon-nyc-day-1/#superchargingyourandroidreleasewithfastlane">same talk</a> I attended at <a href="https://gnuf.dev/droidcon-nyc-day-1/">Droidcon NYC</a>.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Excited (again) about how <a href="https://twitter.com/FastlaneTools">@FastlaneTools</a> can speedup app screenshots and publishing. Listening to <a href="https://twitter.com/asfalcone">@asfalcone</a> this morning at <a href="https://twitter.com/androidTO">@androidTO</a> <a href="https://t.co/O1THvDmmUB">pic.twitter.com/O1THvDmmUB</a></p>&#x2014; Eric Fung (@gnufmuffin) <a href="https://twitter.com/gnufmuffin/status/793819494757961728">November 2, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="animationstoguideusall">Animations to Guide Us All</h1>
<p>(<a href="https://speakerdeck.com/marcospaulo/animation-to-guide-us-all-2nd-edition">Slides</a>)</p>
<p>Marcos Paulo Damasceno (<a href="https://twitter.com/marcospaulosd">@marcospaulosd</a>) walked us through a series of progressively fancier activity transitions in a sample app he built.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Fancy Activity Transition implementation details being explained by <a href="https://twitter.com/marcospaulosd">@marcospaulosd</a> at <a href="https://twitter.com/androidTO">@androidTO</a> <a href="https://t.co/scNhzmnPXx">pic.twitter.com/scNhzmnPXx</a></p>&#x2014; Eric Fung (@gnufmuffin) <a href="https://twitter.com/gnufmuffin/status/793830811652939776">November 2, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Some things I learned about activity transitions:</p>
<ul>
<li>Get started by calling <a href="https://developer.android.com/reference/android/view/Window.html#setEnterTransition(android.transition.Transition)"><code>getWindow().setEnterTransition()</code></a>, with one of the predefined transitions in the <code>android.transition</code> package</li>
<li>After a shared element transition, to have other views in the target activity slide in, create a <a href="https://developer.android.com/reference/android/transition/Slide.html"><code>Slide</code></a> transition, then add views to it that should be animated. Group these transitions into a <a href="https://developer.android.com/reference/android/transition/TransitionSet.html"><code>TransitionSet</code></a> and set them on the window.</li>
<li>Call <a href="https://developer.android.com/reference/android/support/v4/view/ViewGroupCompat.html#setTransitionGroup%28android.view.ViewGroup,%20boolean%29"><code>setTransitionGroup(false)</code></a> to tell Android that a <code>ViewGroup</code> should <em>not</em> be treated as a unit, and that its children should be animated individually.</li>
<li>After an <code>INVISIBLE</code> view moves to its final position, use a shared element callback to add a <a href="https://developer.android.com/training/material/animations.html#Reveal">reveal effect</a>, making the view <code>VISIBLE</code> and then starting the animation.</li>
</ul>
<p>All of the above ideas are demonstrated in a slick demo app that Marcos wrote: <a href="https://github.com/marcospaulo/cinema_example">https://github.com/marcospaulo/cinema_example</a></p>
<h1 id="machinelearningtools">Machine Learning Tools</h1>
<p>Vihan Jain (<a href="https://www.linkedin.com/in/vihanjain">LinkedIn</a>), a Google employee, gave (yet) another overview of the company&apos;s machine learning tools.</p>
<p>He described <a href="https://www.tensorflow.org/">TensorFlow</a> as a way to perform computations defined as a <a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph">DAG</a> to optimize an objective function. These computations are defined in a high-level language, compiled, and optimized in the cloud. A lower-level CPU, e.g. mobile or GPU, can execute part or all of the graph, and push data through it. He suggested we look at a code lab called <a href="https://codelabs.developers.google.com/codelabs/tensorflow-for-poets/#0">TensorFlow for Poets</a> which introduces you to the idea of <strong>transfer learning</strong>, where an existing trained model can be re-trained on a similar problem.</p>
<p>To build something from scratch, Google uses the phrase <a href="https://research.googleblog.com/2016/06/wide-deep-learning-better-together-with.html">Wide and Deep Learning</a> to describe their techniques for building recommender systems, search and ranking systems.</p>
<h1 id="giveyourappaboostinqualitywithfirebase">Give Your App a Boost in Quality with Firebase</h1>
<p>Doug Stevenson (<a href="https://twitter.com/codingdoug">@codingdoug</a>), a familiar face from all of his Firebase advocacy in videos and articles, gave a lightning talk about how to use Firebase to improve app quality.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Sharing about the powerful and feature rich <a href="https://twitter.com/Firebase">@Firebase</a> platform - lightning talk by <a href="https://twitter.com/CodingDoug">@CodingDoug</a> at <a href="https://twitter.com/androidTO">@androidTO</a> <a href="https://t.co/85toDs7Exe">pic.twitter.com/85toDs7Exe</a></p>&#x2014; Eric Fung (@gnufmuffin) <a href="https://twitter.com/gnufmuffin/status/793863239935201280">November 2, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I jotted down a few notes on my phone:</p>
<ul>
<li><a href="https://firebase.google.com/docs/remote-config/">Remote Config</a> is like a fancy <code>if-else</code></li>
<li>Per-user authentication can be done for the real-time database, using Firebase <a href="https://firebase.google.com/docs/auth/">Authentication</a></li>
<li>Coming soon: mobile <a href="https://firebase.google.com/docs/crash/">crash reporting</a> view</li>
<li>Use <a href="https://firebase.google.com/docs/notifications/">notifications</a> to inform users of known crashes</li>
</ul>
<h1 id="dealingwithlegacycode">Dealing with Legacy Code</h1>
<p>Nathalie Simons (<a href="https://ca.linkedin.com/in/nathalie-simons-7926b146">LinkedIn</a>) from <a href="http://www.tribalscale.com/">TribalScale</a> spoke about how to deal with old, inherited codebases. This was a generally applicable talk, not dealing directly with Android.</p>
<p>Legacy code often comes with the baggage of bugs, unknown behaviour and/or unknown design decisions. People are forgetful and don&apos;t remember the what and why of code they wrote.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Nathalie Simons from <a href="https://twitter.com/TribalScale">@TribalScale</a> giving tips on dealing with legacy code - lightning talk at <a href="https://twitter.com/androidTO">@androidTO</a> <a href="https://t.co/hAUS9ksHDm">pic.twitter.com/hAUS9ksHDm</a></p>&#x2014; Eric Fung (@gnufmuffin) <a href="https://twitter.com/gnufmuffin/status/793875007331979264">November 2, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Nathalie outlined three approaches if you find yourself working with another company&apos;s code:</p>
<ul>
<li><strong>Work with what you have</strong>: study existing code to figure out how it is currently working, without trying to clean it up. Make use of logging and breakpoints with the goal of understanding.</li>
<li><strong>Start from scratch</strong>: in addition to studying the existing code, rewrite the code. Put yourself in the previous developers&apos; shoes: they had reasons for what they did. Figure out why, before judging. This technique may be risky, because you don&apos;t know what the original business requirements were.</li>
<li><strong>Take what you need</strong>: after studying the legacy code, cherry-pick what you need onto a clean slate. Introduce portions of old code incrementally, so you know where to place blame when something breaks.</li>
</ul>
<h1 id="buildingintegratedmedicalapps">Building Integrated Medical Apps</h1>
<p><a href="http://goo.gl/4DXL8P">(Slides)</a></p>
<p>James Agnew (<a href="https://twitter.com/jamesagnew">@jamesagnew</a>) from the <a href="http://ehealthinnovation.org/">Centre for eHealth Global Innovation</a> spoke about his team&apos;s challenges in developing healthcare apps.</p>
<p>To start, James presented an example of rethinking healthcare from a <a href="https://www.wired.com/2010/11/ff_bloodwork/">2010 article in WIRED</a> about redesigning blood test results. The designs were turned into a <a href="https://gallery.smarthealthit.org/boston-childrens-hospital/cardiac-risk">proof-of-concept</a> by the Boston Children&apos;s Hospital.</p>
<p>The Centre&apos;s mandate is to develop apps for patients to manage chronic conditions, so they can be active participants in their own health. Such apps help patients follow their care plan (measuring weight or blood sugar, reminders to follow a morning routine), help doctors manage patients&apos; health (uploading data to a doctor), make sense of collected data, and accumulate data for research.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Learning about apps to manage chronic conditions, modern healthcare standards, and research at scale from <a href="https://twitter.com/jamesagnew">@jamesagnew</a> at <a href="https://twitter.com/androidTO">@androidTO</a> <a href="https://t.co/UljKldtNFj">pic.twitter.com/UljKldtNFj</a></p>&#x2014; Eric Fung (@gnufmuffin) <a href="https://twitter.com/gnufmuffin/status/793895279497846788">November 2, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>As one can imagine, healthcare apps have to deal with a number of standards.</p>
<ul>
<li><strong>Bluetooth</strong> often suffers from flaky connections, and the lack of standard profiles for medical applications (like a thermometer or blood pressure monitor).</li>
<li>The underlying premise of most <strong>electronic health record</strong> systems is replicating data everywhere, which doesn&apos;t match the intermittent connectivity and limited storage of smartphones.</li>
<li><a href="https://en.wikipedia.org/wiki/Fast_Healthcare_Interoperability_Resources">FHIR</a> is a modern initiative to develop a data format and API for exchanging health records. There is an open-source reference implementation in Java available at <a href="http://hapifhir.io/">http://hapifhir.io/</a> including an Android library. And, at a higher level, the <a href="http://smarthealthit.org/">SMART Health IT</a> platform defines standards for profile and authorization for healthcare apps, and an app gallery.</li>
</ul>
<p>Looking to the future, James mentioned a number of ongoing initiatives:</p>
<ul>
<li>the idea of seeing your health records curated like a Facebook news feed</li>
<li>using smartphones to perform research at scale (<em>Ed.</em>: I immediately thought of Apple&apos;s <a href="https://www.apple.com/researchkit/">ResearchKit</a>).  <a href="http://syncfor.science/">Sync For Science</a> is a US collaboration which allows patients to share their electronic health data with research apps</li>
<li>an artificial pancreas that uses a smartphone as a controller, e.g. <a href="http://typezero.com/">TypeZero</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>