Implementing Web Push Notifications

Long gone are the days of push notifications being an exclusive feature of native mobile applications. With the advent of the Push API, web browsers now have the ability to receive notifications as well. Users can even receive notifications when they don’t have your website open. We have recently implemented support on meetme.com.

What is the current browser support?

Web push works on both desktop and mobile; however, not all browsers support the standard yet.

  • Chome 50+ (both desktop and mobile Android)
  • Chrome 44 introduced partial support but notification payloads were not supported
  • Firefox 44+ desktop, Firefox 48+ mobile Android
  • Safari does NOT support web push (although the desktop version supports Apple’s own proprietary protocol for push)
  • Coming soon to Microsoft Edge

High level overview

From the perspective of a user, web push notifications work largely the same as on native mobile. However, before a user can start receiving push notifications they must accept permissions for your website. When a user arrives at a website that supports web push (and they are using a browser that supports it), the website can then prompt the user to enable notifications:

Chrome Desktop
Chrome desktop notifications prompt

From this point forward, whenever the website has a notification it will make a REST call to the push provider (more on this below), which will in turn generate the notification on the user’s device. Note that the user does not need to have the website open in order to receive notifications.

Notification
Chrome desktop notification

Behind the scenes the website needs to implement a service worker which will handle receiving the push event and generating a notification UI element.

Components of Web Push

Service Workers

A service worker runs in the background separately from your web application and is required in order to receive push notifications. It has no direct access to the DOM and any communication with your web application must occur using postMessage() to send and receive messages. Some other aspects of service workers include:

  • Can be stopped/restarted by the browser as necessary (you cannot depend on global state)
  • Has access to the IndexedDB API
  • Does NOT have access to LocalStorage or cookies
  • Receives the actual push notification message
  • Generates the visible notification message and handles the click

Use of Promises

The API for service workers relies heavily on promises, so you will need to have some familiarity with them. In a nutshell, promises are designed to make working with asynchronous code easier by wrapping success and error callbacks in chainable functions to allow for greater readability. Although this article is not going to give a tutorial on promises, some good documentation can be found here. One thing that should be noted, however, is that if you are accustomed to using promises in jQuery or Q, the interface for JavaScript ES6 Promises is slightly different. For example, here is how you might use a promise in jQuery:

The preceding code will wait for 5 seconds and then print “Wait Complete!” to the JavaScript console. Let’s look at the equivalent using ES6 Promises:

As you can see, the concept is the same but the syntax is slightly different. It should also be noted that, unlike jQuery, an ES6 Promise does not have a done() method.

How is a service worker used?

A service worker needs to be registered; however, we must first check for browser support.

Once our service worker is registered we will need to subscribe via the PushManager in order to start receiving push notifications.

For users who have not previously enabled notifications a prompt will appear.

Chrome android prompt
Chrome Android notifications prompt

Note that if you want to check for the existence of a push subscription prior to invoking the prompt, you can do so by calling getSubscription() on the PushManager instance instead. If the Promise it returns resolves to a null value then there is no existing subscription. Calling getSubscription() first can be useful if you want to display your own custom message to better inform the user of the enable notifications prompt they are about to see.

It’s possible to determine if the user has pressed cancel on the “enable notifications” prompt if you call subscribe(); the error thrown will have a “name” property of “PermissionDeniedError”.

You may have noticed that when we called subscribe() a “userVisibleOnly” property was specified as true. This is currently required in Chrome. The option indicates that for every push notification received we will show a notification. If you were to not generate a visible notification when a push message was received then the browser will generate a default notification with the text “This site has been updated in the background.” This can also occur if you attempt to perform an asynchronous operation prior to showing the notification without returning a promise.

In our handlePushSubscription() function, we will receive a single argument of type PushSubscription. This contains the data that is required in order for our backend to generate push notifications. The data that PushSubscription contains is a bit different from what you may be used to if you’ve worked with native mobile push notifications (which generate a token hash string). Here is an example of how it is formatted if we stringify the PushSubscription object:

The “keys” portion is what is used to encrypt the payload of the push message. There are libraries that can handle this portion for you (we’ll show an example later on).

The “endpoint” property is unique and is used to identify your browser. Endpoints take the following form (where “unique-key” is a key that identifies the user’s specific browser):

Chrome
https://android.googleapis.com/gcm/send/unique-key

FireFox
https://updates.push.services.mozilla.com/wpush/v1/unique-key

Now let’s define our “handlePushSubscription” function. Its job is going to be informing our backend of the push subscription (which can then be used to generate notifications to the browser itself).

The URL endpoint used above will need to store our web push subscription data (and associate it with the current user).

Generating Push Notifications on our backend

When we have a notification that we would like to send to our user, we will need to encrypt the notification’s payload and make a POST request to the endpoint that we received. To accomplish this, we’re going to use Node.js and the excellent web-push package.

Chrome requires some additional setup steps such as defining an application manifest with a “gcm_sender_id” (for more details see here).

Handling Push Notifications in our Service Worker

We’re now going to setup the “serviceWorker.js” that we referenced earlier. This script is going to run in the background listening for notifications.

There are other useful events that we can listen for in a service worker (see a more thorough guide here). The “push” event is fired when a push notification is received by the browser. You do not need to have a tab open (of the web application) to receive a notification. Let’s define our event handlers:

Note that the notification click handler above will open a new browser tab regardless of whether or not one is already open (there are ways around this by finding an existing client tab using clients.matchAll(), sending it a message via postMessage(), and then handling the message in the web application).

Conclusion

Browser adoption of the push standard has taken an important step towards moving the web platform forward. Although the discussion of service workers in this article was limited to push notifications, service workers can be used for other purposes such as adding offline functionality. A full example of the code used here has been posted on Github.

How to Win Friends and Influence Global Viral Catastrophe

MeetMe is always offering its employees opportunities to better themselves and to have fun. From having a game room to providing everyone with a subscription to Lynda.com, it is quite apparent that skill development and enjoyment are key to the company. Other than the retreats and team building activities that upper management and HR organize, there are also clubs that some employees have taken upon themselves to form. As a female with some social anxiety working at a tech company, it can be hard to come out of your shell. But having activities and clubs which are supported by the company can really make a big impact. I recently joined in on a couple of these and found the positive results to be quite substantial.

Book Club

The first one is the book club. This had been tried in the past, but fizzled out due to the chosen books only being relevant to a small percentage of the company. This time around it was decided to focus more on general skills and self-improvement. The first book chosen was How to Win Friends and Influence People in the Digital Age, an updated version of the book originally written by Dale Carnegie. We met for one hour per week to discuss what we had read in the specified chapters. Since this book was a more general topic, there were people from all different parts of the company interested in it. We had participation from the Engineering, Advertising, Management, and Member Services teams.

 LindsayA lot of the discussions that take place are people sharing personal stories. Whether you want to or not, you’re going to get to know these people better, and depending on how much you share, they’re going to get to know you too. This definitely helps to make it less awkward when passing in the halls or waiting for your coffee to brew in the kitchen, especially when it comes to people you don’t work with on a regular basis (or at all). At one meeting, there was even a diagram drawn of the appropriate distance to say “Hi” to someone when walking towards each other down a long hallway. It was nice to hear that other people tend to overthink these situations the same way I do.

Since the books we’ve been reading are generally about self-improvement, it seemed like a great time to work on bettering myself. I tend to be rather quiet, especially when it comes to talking in groups of people. While I definitely felt reserved at the first meeting, I soon realized I had relevant things to say and started contributing to the conversation. It was a small group of people all with the same interest of discussing a book we were reading, which made it seem less intimidating.

After already discussing one book with generally the same crowd, my comfort level was slightly elevated. We had a meeting before starting our second book, Talent is Overrated by Geoff Colvin, and when asked if anyone wanted to lead a discussion, I volunteered. While the thought terrified me, the obligation would force me to face those fears.

BCblog-3

I have always enjoyed reading, but don’t do it as often as I would like to. While being a part of the book club forces me to read more in general, there was definitely once or twice that I hadn’t completed the required reading before the weekly discussion. Whether it was an extra busy week, or I just couldn’t pull myself away from binge watching Daredevil on Netflix, the item that usually got the boot was the reading. However, knowing that I was leading the discussion disciplined me to read even when I didn’t want to. And not only read it quickly to get it done, but take the time to understand it. I have a hard time concentrating when I read a book and my mind tends to wander, so this was forcing me into the practice I needed. I took notes as I read, which really made me pay attention and absorb what I was learning. I found it to be quite amusing that I was reading a book about deliberate practice, while deliberately practicing.

The discussions themselves are always interesting. Everyone has their own point of view, so it’s compelling to see where they intersect and where they diverge. Everyone has something engaging to contribute, whether it’s a deeper thought about a topic, or an exemplary anecdote from their own life. After much sweating and nervousness, I made it through leading the discussion and I felt proud for putting myself out there. All my effort had paid off and I had successfully accomplished doing something I never would have imagined doing even a year ago. I have since continued to lead discussions for the book club and it has absolutely helped me to feel more comfortable when it comes to speaking in groups of people and sharing my thoughts.

BCblog-2

Board Games

The other group that I joined was the group of people who play board games during lunch. Prior to this, my knowledge of board games had been along the lines of Monopoly and Scrabble. My mind was blown that there was a whole other world out there to explore. I knew there were people who did this, but I always assumed the games were long and confusing and difficult for a newbie to learn.

BGblog-2

Then one day I ended up talking to a couple coworkers who were about to play a game during lunch and they invited me to join. I accepted the invitation, and it was the start of something big. We played Dominion that day, which is played with cards in which you buy skills and try to earn the most points. Not only was it easy to pick up, I thoroughly enjoyed playing it. In the following days I would play it again, purchase it, and continue playing other games including Splendor (which I now have the app for on my phone), and Pandemic (which I also now own).

It all started out as just a fun way to spend time during lunch, but I soon started seeing there were other benefits. Much like the book club, I was interacting with people that I normally wouldn’t get the chance to connect with. Playing board games with people helps take the pressure off of having a conversation. You have something to focus on, and silence is understood as people plan their strategy. You don’t have to desperately be trying to think of things to keep the conversation going. For someone like me whose brain immediately goes blank at the prospect of having to come up with small talk, this is perfect. Usually discussions start up, or stories get told, but nothing feels forced. Conversation is not the focus.

pandemic2

Sure, they have the word “game” in the name, but board games can also be quite a practical tool. You’re not only having fun, but you’re working on problem solving skills, team building, and thinking outside the box. Pandemic is one of my favorites, because everyone playing is on the same team. It’s the people vs the game. You have to work together and come up with the best way to save the world against four viruses that threaten to annihilate the global population. Sometimes you win, sometimes you lose, but either way, you do it as a team. You celebrate together, and you commiserate together. It’s also great for people who are apprehensive about learning a new game. There isn’t any pressure on them to make the right move, because you’re basically all deciding what to do together. The first few games I played, I was more of a spectator than a participant, but once I started understanding the rules and picking up some strategies, I was able to start suggesting tactics along with everyone else.

catan2Sometimes you’re stuck in a rut trying to solve a work problem and the best thing for you to do is walk away and clear your head. Playing a board game gives you something completely different to focus on. Instead of rehashing the same thing over and over, your brain is exercising itself in an alternate way. Many times that break is all you need to figure out where the problem in your code is. Maybe you even win the game, and your defeated mood has now turned into a much more positive one, and it was the boost that you needed to get over the hump. Board games can provide a distraction just when you need it the most.

One of the biggest things that I have really enjoyed is being able to share this newfound love of board games with other people. I have either bought or borrowed many games to play at home with my husband. He has enjoyed them just as much as I have and it has given us something completely new to do together. While we both enjoy watching reruns of The Office, it’s always fun to do something new. Just as it helps team building with coworkers, it also helps team building with significant others. Also, having friends over for game nights allows us to still be social while not having to go out to a crowded bar where we drink overpriced beer and can’t hear each other talk (yes, we’re getting old).

BGblog-4

It’s great when you can combine having fun with building skills, and these two groups are excellent at doing both. Working at MeetMe has provided me with these great opportunities to learn new things, challenge myself, and get to know coworkers better. From providing the books for the book club and giving us company time for the discussions to designating a room specifically for board games, MeetMe will do what they can to bring to life your interest or idea that will benefit employees.

I wanted to share my experiences, because I was pleasantly surprised at how two things that seemed rather inconsequential ended up having such a positive influence on me. Sometimes you have to take the initiative to get the most out of it, but it’s well worth it in the end. I hope other people will try them out or find something else that has a similar effect for them.

pandemic

How Mark Summers helped our office become better friends while fighting aardvarks for charity

A few times each year, our office holds a hackathon (cleverly titled HACK’D) and we’re able to work on whatever we want for two days. Last HACK’D someone had organized a scavenger hunt around the office which went over really well. Rather than work on a new app, I wanted to take the concept of a company-wide game further.

Our team of four was fresh off an online game called Neptune’s Pride and I was interested in seeing if we could incorporate some of the elements of the game to the whole office. Here are some of those elements:

  • Takes a month to play
  • Lots of players
  • You get to know your coworkers more deeply
  • You end up hating your coworkers

We brainstormed ideas on how we could take an existing competitive game like Neptune’s Pride or Diplomacy and rework it using our office as the map. We knew we wanted everyone in the office a part of the game by default, whether they liked it or not. One of the team members had an idea: make it a cooperative game rather than having people or teams compete against each other. This not only sounded more fun but solved that pesky “hating your coworkers” problem.

Having decided on creating a cooperative game, we sketched out our main goals:

Get people from different departments to know each other
While most people in the company knew each others’ names, the tendency exists to mainly talk to people you either work on projects with, play a sport with, or those who joined the company around the same time. There are lots of people working here who don’t fit into any of these categories but you might still get along with. Even worse, there are some entire departments that are so physically far from the bulk of the team that interaction is minimal. As a result, members of far away teams can become a mysterious unknown island.

Experiment with an office wide game
This was the goal that resonated with me the most. I love games and have fun creating things, so I was excited to see what we can learn from this experiment and how far we could push it. Could this game be something that we do more than once? Does this have bigger potential than just being played at our company?

Liven the office up
I’ve heard more than once that our office is very quiet, which seems to clash with the aesthetically fun environment here. Tech types aren’t known for being particularly outgoing, but while introverts abound I don’t believe that the people here want to be left alone. Many want to be involved, talk to more people, and have more fun at work but just need that little push. Hopefully this game could be that push while also giving the office a warmer, higher energy feel.

An early obstacle was figuring out how to encourage our officemates into the game. The aforementioned scavenger hunt was prize-based, but since this would be a co-op game it would be pretty difficult to get good prizes for 100 people based on our $500 budget. We realized that having the prize be a donation to charity would work because it will motivate some while guilting others into action. Hey, whatever works.

To give a sense of pulling the office together, our game would revolve around being attacked from outside forces. A game needs a name, and thankfully some of my more over the top names were vetoed (“They’ll Kill Us All”) in favor of “We’re Under Attack!” which satisfied everyone. The game would involve multiple attacks per day for about a week and our coworkers would need to gather in the assaulted section of the office to “defend” it.

I usually try to crowbar in create a comedic video for any project I’m working on and our team devoted a work day to filming a video introducing the concept and tone of the game:

Yup. We got Marc Summers of Double Dare fame to send us a video clip promoting our game. Don’t ask me how it happened, we just wanted it enough and somehow got it. How could our coworkers who mostly grew up in the 80s and 90s resist a game that involves Marc Summers?

The core loop of our game was to have MeetMe employees gather into different zones of the office that were themed and mapped out:

Gameplay map that mirrors our office.
Gameplay map that mirrors our office.

Each time our office successfully defended an attack, we donated $50 to a local Boys & Girls Club. To defend an area, there needed to be a certain count of bodies there. To make things more interesting and also to learn more about each other, we sometimes required people from certain departments and other times made people worth more if they had certain attributes. For instance our first attack was in the QA area. Here is the email that went out:

first-emailDid you put together that BUGS were attacking the QA testers, whose jobs it is to find and eliminate bugs? So clever.

We had no idea how many people would show up. Some on our team argued that we should require less people so that we didn’t start off with a loss. A measly three people could have came and it wouldn’t have surprised us. Do we have a moral imperative to try to get these missions to pass, for the sake of the charity? No, we decided. The game must have integrity, else why bother playing it? As it so happens there were 17 present for the first attack so I threw together a comically bad photoshop showing the participants and the defeated bugs.

1-bug-attack

The photoshops ended up being a really fun thing to do. It gave character to a game that could have ended up being very generic (“Meet here, take a picture in silence, go back to your desk”). I got advice to play some music while people were gathering because it can be awkward while people are milling about in wait. The attackers started to get more and more ridiculous as well, such as the Aristocratic Aardvarks:

aristocratic-aardvardks

We also started adding more of the bonus qualifiers and we would ask people about them. In one of the challenges a Giant Meteor was going to hit an area of the building. Anyone who had been to space camp counted as 10 people. While asking people about their bonuses, we found out that one of our coworker’s father is an actual astronaut. We found out all sorts of things about each other, such as who owns a horse, who has been to an Aerosmith concert (and is willing to admit it), and who cried in the Never Ending Story when Artex sunk into the Swamp of Sadness.

neverending-story

Pretty sure people lied about not crying.

The amount of people showing up to defend each attack grew steadily. No one was forced to participate, it was just good old fashioned peer pressure. You didn’t want to be the one NOT in the picture of a Sharknado attacking office. Unsurprisingly, the more fun and whimsical we made things the greater the participation was. Our team had spent so much time on the technical aspects before the start, such as debating the best notification system and working on an accurate map of the office, that we initially overlooked the fun factor.

We had two attacks per day over the course of 5 days. As the game neared the end, I thought the game was going a little *too* well. Is this not challenging enough? Being the most sadistic member of the team, I wanted to really stretch these people. They had to earn it! Our final challenge was the most epic yet – The Nothing from the Never Ending Story was threatening our office’s very existence.

It would take 100 people to defend this greatest of foes, a tricky feat considering there are only maybe 90 people working here on a good day. Our group of course had to rely on the bonuses, which included things like acting in a movie, publishing a novel, and of course crying when Artex sank. Not only did the team reach the 100 total, they blew it away. I was surprised at how much so many of our team has accomplished in their lives. Besides being nice getting to know your coworkers more deeply, it’s pretty inspiring to see what kind of people we have working here. 10 out of 10 attacks successfully defended, all $500 went to the charity.

10-neverending-story

We sent out a survey after it was all said and done to see what people liked and didn’t like. Beforehand we were very worried about interrupting people’s work (we were after all gathering 50 people around a random person’s desk while blasting “The Final Countdown” on my laptop). 23% of respondents were “a little annoyed” and everyone else wasn’t annoyed at all (with no one selecting “very annoyed”). Almost everyone enjoyed taking the pictures and receiving the emails describing the attacks in all their horribly photoshopped glory.

Sadly, only about half the people found that they learned useful things about their coworkers that they would be able to use to start a conversation. If we do another game like this we will have to come up with different mechanics to get people to know each other better.

I was curious if people were participating because it was fun or because we were donating to charity. To my chagrin, only 13% said they would have participated even without the charity, while 30% said they would not have participated without the charity. The majority, 57%, said that the charity motivated them some. I like to believe that most of that 57% would have played along anyway.

Most of the respondents (63%) said they’d like to play “We’re Under Attack!” quarterly, with the rest split between monthly and yearly. This does seem like the kind of game that you need a break from for a while before you start to miss it. In that sense, it is very similar to Neptune’s Pride and Diplomacy. When you’re in it, it consumes most of your attention. When you finish it, you definitely want some space.

My biggest takeaway from the experience is a new appreciation for playtesting and the iterative process. We also learned what are the most important things to focus our time on and which aspects people didn’t even notice. I would venture to guess that my team’s experimenting with live social games isn’t over and hopefully we can come up with a game that is effective at making a workplace more fun and social while also being something that any other office could implement.

Now if I could just get Marc Summers on board for the next one…

PaletteBar – A color picker for Android

Try it out

As a developer, I’m often skimming tech blogs in search of a solution, so if you’re here to find a color picker for your Android app, here’s the Github link: https://github.com/MeetMe/PaletteBar

paletteBarScreenshot

PaletteBar is a minimal color picker, implemented as a custom Android View, done entirely in code in one class file. The border shows the selected color – in this case, light green. Here’s a grainy gif of it in action:

paletteBarDemo

From a development perspective, it’s super easy to add to a project. Just copy the single PaletteBar.java file, no extra resources or xml needed. It’ll automatically scale that color spectrum to any height and width, and the border size can be changed or removed entirely.

From the UI side, this layout allows any color to be chosen by moving horizontally, and any hue by moving vertically. Grays and browns have been tacked on to the left. The notable downside is that it’s nigh impossible to choose the exact same color repeatedly.

Here’s PaletteBar in our MeetMe app on Android, used to edit Chat photos:

selfiePaletteBar

Color pickers are tricky

The trouble with mobile devices is that screens are small and fingers get in the way, and the trouble with mobile developers is that we haven’t had many years to establish conventions for UI elements.

In helping build MeetMe’s drawing feature, and in my personal quest to find useable apps for my toddlers, I’ve tried a ton of apps with palettes, and most of them fail terribly. If a drawing app is just for entertainment, then you don’t need to specify RGB values or cram in a “recent favorite colors” section for your palette. We recently downloaded a [beloved copyrighted character] coloring book app that was particularly awful. It took me and my young kids a couple days to realize that the palette only appeared to be limited to six colors because it could scroll horizontally.

Ultimately, there are just two UI problems for a color picker to solve:

  1. Show the user all the available colors
  2. Allow the user to choose a color

Some apps use a ROYGBIV spectrum, which is great for making rainbows, but you can’t draw people and robots without browns and grays. Check out how many palettes make that faux pas in this Google images search for “color picker”: https://www.google.com/search?q=color+picker&tbm=isch  For apps that only need a dozen or so colors, like a simple text editor with font colors, the easy solution is to have a button for each color. It’s when the palette grows that the challenge arises for both displaying and accessing colors. Wikipedia has a small article under “Color tool” that shows some complex, but common, solutions for art applications: https://en.wikipedia.org/wiki/Color_tool  Even in a sophisticated app, these desktop solutions would be unwieldy.

ColorPalette is an attempt to provide pretty much all the colors in limited real estate, with quick interaction, at the cost of being able to specify a color beyond eyeballing. For an entertaining scenario like MeetMe’s Chat, it’s a great fit . I also integrated ColorPalette in an extremely simple drawing app for my kids, and can confirm that even a 2-year-old can use it. And it’s rife with opportunities for feature creep or even legitimate enhancements, so have at it.

simpleDrawUse2

HACKD 11 – Illegal amounts of fun

Every quarter, MeetMe holds a 48-hour hackathon called HACKD. Though it is heavily focused on coding, we are proud to say that we differ by allowing all departments to participate and to “hack” fixes/improvements into their daily lives at work. Projects in the past have gone from cleaning up documentation to building a ping pong table to building a completely anonymous chat platform.

We just finished our 11th HACKD and I’m happy to say that it’s one of the best we’ve had.

Before we even get started hacking, one of the things we do is come up with a theme for HACKD to make it a little fun. They have all revolved around movies, and this one was no different:

hackd11

(inspiration)

Once November 12 hit, the teams were free to start hacking! Here’s a look at what MeetMe built at HACKD 11.

Meet Cast

Similar to the apps Periscope/Meerkat, the team of David, Jason, and Bryan tinkered with a platform where people can livestream their own video with a group chat component built in. We were really impressed by the work they did and what they learned in a short order of time!

We’re Under Attack!!

We have a fairly large company and the thought from Corky, Louise, and Bobby here was to hack office relationships, building a game that acts as an icebreaker for people in different departments to come together. Why not do it as a fun game by saving the MeetMe offices from a school of sabertooth sharks?

Office Overhaulers

There were a few improvements that could have been made around the office so Kate and Jamie took it upon themselves to do so. They revamped our kitchen area to organize all of our coffee and build a bar for more people to sit at.

New kitchen bar!
That organization and extra work space…
Looks great, Kate!
Looks great, Kate!

MeetMe has a good amount of people interested in photography, so one of the more personal touches that they will soon implement is to have some MeetMe employees’ photos be on display around the office.

Browser Push Notifications

One of the major ways that web is lagging behind mobile is in the ability to send push notifications. Google Chrome now enables browser notifications, so Bill and Sandeep took it upon themselves to test out how they work. We’re encouraged by what they showed and are impressed that they were able to get it to work on a production environment in two short days!

Amateur Hour

Our VP of Product, Jeremy, worked to build optimizations to our project flows. Over time, our code changes have become less and less legible, so he reformatted all of these emails to make them much easier to read. In addition, he also took our integration of JIRA and built out a completely customizable project pipeline page that makes our lives much easier in order to find out the status of all projects and to be able to change project priorities on the fly.

i18n Automation

We have automated scripts that regression test our apps but currently we’re limited to English only. Joe and Lindsay worked together to automate some of the key actions in other languages on Android.

Your HACKD project earns you a thumbs up!

This is just a sample of what teams put together for HACKD 11. I’m so proud of what everyone accomplished and can’t wait to see what everyone comes up with for HACKD 12!

(If you have any ideas for a movie theme for HACKD 12, please leave a suggestion in the comments…we’re going to eventually run out of movies here.)