Android: RecyclerView (Kotlin)

XML Layout
Create a new layout file with a LinearLayout at the top level if you plan to display multiple Views for each item. If you will only have one view, i.e. a TextView, you can make that your top level View instead. Give it an id and set the layout_width and layout_height to “wrap_content”.

MyAdapter extends RecyclerView.Adapter<MyViewHolder>
Once you have created the Adapter class, go ahead and create the inner ViewHolder class so the implemented methods will have the correct type. Then implement the Adapter classes:
onCreateViewHolder – Inflate the xml layout, and use it to create and return an instance of your ViewHolder.

val adapterLayout = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false)

onBindViewHolder – Get the item from the cached list of items, and use it to set the content of the View inside the ViewHolder (be sure to handle a null list of items).
getItemCount – Return the size of the cached list of items – handle null case.

MyViewHolder extends RecyclerView.ViewHolder. This can be an inner class of the Adapter. The constructor takes a View as a parameter. This is the layout that gets inflated by the adapter. Get any Views from the parent and store them as a member variables. You can call findViewById on the parent View to get its children.

onCreate – Set up the RecyclerView

// Kotlin
// Initialize data.
val myDataset = Datasource().loadAffirmations()
val recyclerView = findViewById<RecyclerView>(
recyclerView.adapter = ItemAdapter(this, myDataset)

// Java
RecyclerView recyclerView = findViewById(;
recyclerView.setAdapter(new MyAdapter(this));
// a layout manager must be provided for the RecyclerView to function
recyclerView.setLayoutManager(new LinearLayoutManager(this));

How To: View Binding in Android

In your app build.gradle file, add the following lines to the “android” section:

buildFeatures {
    viewBinding = true

In your main class, create a member variable called, “binding”, where the name of the binding class is the name of the layout converted to camelCase:

 lateinit var binding: ActivityMainBinding

In onCreate(), initialize the view binding:

binding = ActivityMainBinding.inflate(layoutInflater)

Call setContentView with “binding.root” instead of the main layout file:


Now you can access views using the binding object like this, where the name of the view is its id converted to camelCase.:


Options Trading Rundown

The following info was gathered from E*TRADE’s Options Trading for Beginners Series.


This is a strategy to use if you expect the price to go up.

Buying a call is like buying a coupon. Maybe I don’t want to buy pizza today even though it’s only $8, but I think the price is going up so I can buy a coupon for $10 to use if/when the price goes above $10 (factoring in the cost I paid for the coupon). Same with stocks – I buy the choice, “option”, to buy it at a certain price even if the price goes above that. If it doesn’t hit that certain price I do not have to buy it. This is great if I can use a $10 coupon on a $20 pizza!

You earn/save money if the price goes up beyond the value of the coupon plus what you paid for that coupon. If you pay $1 for the option to buy at $10, you won’t make a profit selling the stock until the price is at least $11+.

You lose money with this strategy if you’ve paid for a coupon, “option”, that you never get to use when the price doesn’t go up as expected. i.e. You paid $1 for the option to buy at $10, but the price doesn’t rise beyond $10 before the option expires.


This is a strategy to use if you expect the price to drop.

Buying a put is like buying insurance. I already own my car, but I expect the value to drop. Buying a put allows me to sell my $8000 car (or stock) for $7000 even if the actual value drops below that.

You earn/save money if the value drops below the value of the option. If I buy a stock for $10 but I’m worried the price will fall, I can buy a put for $9 and if the price drops to $5, I can still sell for $9. If I paid $1 for the option to do this, I essentially get rid of this stock for $8 instead of only $5. So this is a strategy to use if you expect the price to drop.

You lose the amount you paid for the option if the stock doesn’t drop.

Covered Call

This is a strategy to use if you expect the price to drop.

This is when I’m on the other side of a call, selling someone else a coupon for a stock I own. If I’m willing to sell a stock I paid $10 for once it reaches $12, I make not only the $2 profit, but the amount I was paid to sell it (which I get up front).

You earn money if the stock doesn’t go up and the “coupon” is never used.

You lose money if the stock rises more than anticipated and you have to sell your stock that’s worth $20 for only $12.

Cash Secured Put

This is a strategy to use if you expect the price to go up.

This is when I’m selling someone else insurance (agreeing to buy their stock if it drops). If a stock costs more than I’m willing to pay for it, someone pays me to buy it from them if/when it drops. I get the premium even if it doesn’t drop, but if it does drop I get the stock at the price I wanted it for.

You gain money if the stock doesn’t drop (because you keep the premium) or if it drops to the price you’ve agreed to buy it for, but not lower.

You lose money if the stock drops below what you’ve agreed to pay for it. i.e. A stock is currently worth $100, I agree to buy if/when it drops to $95, but it drops all the way to $90.

How To: Using Jersey Client to Upload in Java

Ajax and especially jQuery make HTTP requests very simple when you’re writing javascript. But what if you need to make a request in Java? Jersey is great for writing the backend of your jax-rs service and it turns out it makes writing a client simple too. Here are a few common requests and how to make them:

Multipart Form Data

final Client client = ClientBuilder.newBuilder().register(MultiPartFeature.class).build(); WebTarget target ="http://localhost:8080/resource/mydoc"); final FileDataBodyPart filePart = new FileDataBodyPart("file", new File("C:/mydoc.pdf")); FormDataMultiPart formDataMultiPart = new FormDataMultiPart(); final FormDataMultiPart multipart = (FormDataMultiPart) formDataMultiPart.field("foo", "bar").bodyPart(filePart); final Response response = target.request().post(Entity.entity(multipart, multipart.getMediaType())); // do something with the response System.out.println(response); formDataMultiPart.close(); multipart.close();

File Upload

final Client client = ClientBuilder.newBuilder().register(MultiPartFeature.class).build(); WebTarget target ="http://localhost:8080/resource/mydoc"); final File fileToUpload = new File("C:/mydoc.pdf"); final Response response = target.request().put(Entity.entity(new FileInputStream(fileToUpload), new MediaType("application", "pdf"))); // do something with the response System.out.println(response);

POST data

final Client client = ClientBuilder.newBuilder().register(MultiPartFeature.class).build(); WebTarget target ="http://localhost:8080/resource/mydoc"); Response response = target.request().post(Entity.json(myJson)); // do something with the response System.out.println(response);

Basic Authentication

HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("username", "password"); final Client client = ClientBuilder.newBuilder().register(MultiPartFeature.class).build(); client.register(feature);

Java Plugin Architecture

Sometimes you need to design an application in such a way that users (devs) can drop in their own implementation of a service. Generally this is done via interfaces. But how do we communicate to our application that a new implementation has been created and how do we know which implementation of the service to use? This is where the plugin architecture comes in. In this blog I’ll be summarizing the Java Tutorial for Creating Extensible Applications. Their example uses a Dictionary that allows to you look up the definition of a word. Let’s take a look at classes and what each one of them does.

Sample Code Explained

DictionaryDemo.class is the main class that uses the Dictionary to look up words. It’s simply the caller of the service.

public class DictionaryDemo { public static void main(String[] args) { DictionaryService dictionary = DictionaryService.getInstance(); String definition = dictionary.getDefinition("book"); } }

Dictionary.class is the service provider interface (SPI). This is what the service knows about and what the service providers must implement.

public interface Dictionary { public String getDefinition(String word); }

DictionaryService.class is a singleton that uses the ServiceLoader to get the implementations of Dictionary.class and does the work of interacting with the actual implementation.

public class DictionaryService { private static DictionaryService service; private ServiceLoader loader; private DictionaryService() { loader = ServiceLoader.load(Dictionary.class); } public static synchronized DictionaryService getInstance() { if (service == null) { service = new DictionaryService(); } return service; } public String getDefinition(String word) { String definition = null; try { Iterator dictionaries = loader.iterator(); while (definition == null && dictionaries.hasNext()) { Dictionary d =; definition = d.getDefinition(word); } } catch (ServiceConfigurationError serviceError) { definition = null; serviceError.printStackTrace(); } return definition; } }

Then you have GeneralDictionary.class and ExtendedDictionary.class which are two actual implementations of Dictionary. They are the service providers. I’m not going to show the code here because the whole point is that the exact implementation doesn’t matter!


So let’s say you are the dev who wants to provide the implementation of Dictionary, how do you actually communicate to the application that you have created the implementation? You do this by registering your service provider. Do this by creating a configuration file that contains the fully qualified class name of your service provider implementation. The name of the file must match the fully qualified class name of the service provider interface. Then, the configuration file should be stored in the META-INF/services directory of the service provider’s JAR file.

For example, if you have created the GeneralDictionary implementation of Dictionary, then the config file name is dictionary.spi.Dictionary (since Dictionary lives in the dictionary.spi package). The file contains one line, dictionary.GeneralDictionary (since GeneralDictionary lives in the dictionary package). Finally, the implementation and config file need to be packaged in a jar that should be placed in the application’s classpath.

Jeff’s Favorite Chili


1 Onion (diced)
1 Green Pepper (diced)
2 cloves Garlic (minced)
1 Jalapeno (diced)
28oz Can crushed or diced tomatoes
15oz Can tomato sauce
1 can original rotel
1-1.5 cans of water (15oz)
1 can black beans
1 can dark red kidney beans
1 can pinto beans

2tsp salt
1tsp black pepper
2tsp Oregano
2Tbsp Chili Powder
1Tbsp Cumin

Optional Garnish & Serving suggestion:
Green onions
Sliced black olives
Tortilla chips


Add all ingredients EXCEPT the beans to a large dutch oven and simmer for 1 hour.
Add the beans (with their juices) and cook for another hour.
Check on it and stir periodically as you may need to add more water during cooking.

We like to garnish it with green onions and sliced black olives. Scooping it with tortilla chips is also tasty!

Spinach Lasagna


8oz Lasagna noodles (look for the no boil kind)
12oz package frozen chopped spinach (thawed)
4 cups thick spaghetti sauce (I used Kroger’s Chunky Mushroom & Onion)
1/2 cup water
1 tsp salt
1 tsp Italian Seasoning
2 cups Ricotta cheese
3 cups shredded Mozzarella cheese
1/2 cup grated Parmesan cheese


Mix spaghetti sauce, spinach, water, salt, and seasoning in a bowl.
Spray 9 x13 pan with cooking spray and put 1/3 of the sauce in the bottom of the pan.
Place 1/2 of the uncooked noodles on top of the sauce, then add a layer of 1 cup of Ricotta cheese and a layer of 1 cup of Mozzarella cheese.
Repeat layers of sauce, noodles, Ricotta, and Mozzarella.
Top with remaining sauce, then remaining cup of Mozzarella, and then the Parmesan cheese.
Bake at 350 for 55-60 minutes. (You may need to cover it with foil near the end if the cheese gets too brown).
Let stand 10 minutes before cutting and serving.

The Protein Project: Spreading Awareness that Plants have Protein

“If I didn’t eat meat I don’t know where I’d get my protein.”

Has anyone ever said this to you? Or have you wondered this yourself? Or maybe you’re a vegan and have heard that “as long as you eat a variety of foods you’ll meet your daily protein needs”, but have your doubts… I have generally accepted that the vegan community knew what they were talking about and that I could trust people who have been living this lifestyle far longer than I have. BUT, instead of just repeating what I’ve heard or trusting others that I am eating a healthy diet, I prefer to do my own research and back up what I’m saying with hard facts. So that’s what I did.

So how much protein does a person *really* need to consume? According to Harvard Health, for each pound of body weight you should consume .36 grams of protein per day. This means a 140 pound woman needs to get around 50g of protein per day from her 2000 calorie diet. And a 200 pound man needs 72g of protein per day from a 2500 calorie diet. To simplify this into smaller units let’s look at how much protein these people need on average from every 100 calories they eat. 50 grams of protein from 2000 calories translates to 2.8g of protein from 100 calories. 72 grams of protein from 2500 calories translates to 2.88g of protein from 100 calories. This means for every 100 calories of food you eat, you should be getting just under 3 grams of protein. Of course, this is on average! Fruit tends to fall under that number, but many veggies pack close to 3 times that amount! Take a look at the list below of common foods and their corresponding grams of protein per 100 calories of that item. I’m including lists of animal products for comparison, but you’ll find that there are MANY plant sources that contain above and beyond the ~3g/100cal average. So if you’re a vegan or are interested in a plant based diet, you can rest easy that as long as you incorporate a variety of foods each day you will meet or exceed your daily protein requirements. If you have any other foods you’d like to see included on this list please let me know in the comments below!

Source: NutritionIX Database
*All values are grams of protein per 100 calories


Tuna 22.5
Salmon 10.7
Pork 10.9
Chicken 10.7
Beef 8.9
Bacon 7.5

Dairy & Eggs

Parmesan Cheese 9
Eggs 8.8
Yogurt 8.3
Mozzarella Cheese 7.4
Milk 6.8
Cheddar Cheese 5.7


Tofu 12
Tempeh 10.2
Soymilk 7


Almonds 4
Peanuts 3.4
Cashews 2.2
Walnuts 2.3
Macadamia Nuts 1.1
Pecans 1

Beans & Legumes

Lentils 8
Black Beans 6.7
Peas 6.4
Pinto Beans 6.1
Chick Peas 5.6
Green Beans 4.5


Spinach 12.9
Romaine Lettuce 7.3
Kale 6.9
Cabbage 5.3


Portabella Mushroom 11.3
Button Mushroom 8.8
Shiitake Mushroom 6.2


Asparagus 10.6
Cauliflower 8.3
Zucchini 7.6
Broccoli 6.7
Tomato 5
Cucumber 4.3
Corn 3.5
Onions 3.2
Green Pepper 3.1
Carrots 1.9


Blackberries 3.6
Canteloupe 2.5
Strawberries 2
Watermelon 2
Kiwi 2
Oranges 1.9
Pineapple 1.2
Banana 1.2
Grapes 1.2
Avocado 1
Apple .5


Gold Potatoes 2.7
Red potato 2.6
Sweet Potatoes 1.8


Wheat Bread 4
Quinoa 3.7
Oatmeal 3.6
White Bread 3.4
White Rice 2.1
Brown Rice 2.1

Things to Know for Technical Interviews

Google style interviews are becoming commonplace. If you are in tech and are in the job market you may need to brush up on your skills or revisit some of those things you learned in college 12 years ago (space and time complexity I’m looking at you). The following list was compiled based on this video from Google about how to prepare for one of their interviews:

  • Data Structures: Hash tables, Stacks, Arrays, Linked Lists, Trees (construction and manipulation, traversal)
  • Algorithms: Dijkstra, A*, etc
  • Space and Time Complexity
  • System Design
  • Object Oriented Programming
  • Testing: Unit testing, interesting inputs, corner and edge cases, integration, load and performance, security
  • NP Complete Problems: Traveling Salesman and the knapsack for example
  • Math: Basic discrete math: Counting, Probability theory, Combinatorics, n choose k
  • Recursion
  • Operating Systems: Processes, threads, concurrency issues (semaphores, mutexes, locks), resources allocation, context switching, scheduling
  • System design: feature sets, interfaces, class hierarchies, distributed systems
  • Internet: Routers, DNS, load balancers, firewalls, search

How To: Build a Trello Power-Up for YouTube

In this “How To” I’m going to show you how to build a Trello Power-Up that will allow users to search for and embed YouTube videos in their cards. We’ll be using Trello, GitHub, and YouTube. When we’re done, you’ll be able to create cards that look like this:

Trello card with YouTube Power-Up enabled and an embedded video

I’ll walk you through the process step by step, but you can also see the full code for the project on GitHub. I also recommend you refer to the Power-Up documentation before you begin.

GitHub Set Up

Let’s get started with GitHub. If you don’t have an account, you can create one at Once your account is created and you are logged in you will also need to set up your GitHub Pages website since that is where we’ll be hosting our Power-Up. Follow the instructions at to set up GitHub Pages. Now when you create a new GitHub repository it will be hosted at Pretty cool, right?

Fork the Power-Up Template

Now that GitHub is set up, let’s get started with the Power-Up! The easiest way to start building a new Power-Up is to fork Trello’s template project which is a full featured sample Power-Up. Go to the power-up-template repository and click the “Fork” button in the upper right corner of the page. I gave my fork a new name (youtube-power-up) and description. Then clone the repository to get a local copy. I like working with GitHub Desktop.

Update the Manifest

Next we’re going to edit the manifest to reflect that this is a YouTube Power-Up. Open up manifest.json in your favorite editor. We’ll edit the name, description, details, icon, author, and capabilities. Notice that I changed the url of the icon, that’s because we’re going to use the official YouTube icon instead of the icons that came with the template. The other section to pay attention to is the “capabilities”. I recommend reading more about capabilities here, but for now you need to know that we’ll be using attachment-sections, card-buttons, and callbacks. My finished manifest looks like this:

    "name": "YouTube",
    "description": "A Power-Up for integrating YouTube content in your cards",
    "details": "This Power-Up is used for integrating YouTube content in your cards",
    "icon": {
        "url": "./images/yt_icon_rgb.png"
    "author": "Amber Ream",
    "capabilities": [
    "connectors": {
        "iframe": {
            "url": "./index.html"

Be sure to add the YouTube icon to your images folder. When you’re done with your edits, push your changes to the manifest and the new icon to GitHub.

Create a new Power-Up in Trello

Now we’re ready to create the new Power-Up in Trello. If you don’t already have a Trello account, sign up for one at Since Power-Ups are associated with teams, you’ll also need to create a new team. You can do this by clicking the “Create a new team…” link underneath your boards.

Click the

Now you’re ready to create a new Power-Up from the Power-Ups Administration Page. Under “Teams” click on the team you’d like to add the Power-Up to, then choose “Create new Power-Up”. Give your Power-Up a name (I chose YouTube Power-Up) and then enter the path to manifest.json. If you’re using GitHub Pages, the url will look something like this:

Click save and your new Power-Up will show up in the list of Power-Ups.

Add the Power-Up to a Board

Now that our Power-Up has been created, let’s go to our boards so we can add our Power-Up and see it in action! At this point it will have the template functionality, we’ll implement the YouTube code later. To add the Power-Up to a board, go back to and if you haven’t already created a board for your team go ahead and do that now. Then click on your board to open it. To add the Power-Up, go to your team’s board and choose “Power-Ups” from the menu.

Open the menu on the right hand side of the page and select the

Scroll down and you should find the new “YouTube” Power-Up in the list (it is sorted alphabetically). Note that the name, details, and icon you see in the Power-Up list are all coming from manifest.json. Click “enable” to enable this Power-Up (if you are using the free version of Trello only one Power-Up is allowed per board so you may need to disable another Power-Up first). I’ve noticed it can take anywhere from a few minutes to a few hours for the Power-Up’s icon & description to show up in the list, so don’t worry if all you see is the name at this point! Likewise there is some lag before the Power-Up is available in your cards. In the meantime, we can get started on other parts of our project.

YouTube API

We will be using the YouTube Data API to search for videos. Before we can connect to the API, we’ll have to set up credentials and grant access to the YouTube Data API. Make a note of your API key because you’ll be using it soon, and please remember to restrict your API key! For example, I chose to only allow YouTube requests coming from my GitHub Pages site.

YouTube credentials accepting referrers from my github pages site

Implement client.js

Now it’s time to make our Power-Up do something! But first, a little set up. Since I like to use jQuery for ajax requests, I’m going to import it in index.html.

<script src=””integrity=”sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=” crossorigin=”anonymous”></script>

Next, since I have a custom icon that I would like to display for the YouTube Power-Up on the back of the card, let’s drop the gray play button icon in the images folder.

You may remember defining card-buttons, attachment-sections, and callback in manifest.json. Let’s take a look at how those are used in client.js. We don’t have to define anything specifically for “callback” here, but you can read more about why we need the “callback” defined in the manifest here.


We’ll start by adding a button to the back of our card. In client.js, find the ‘card-buttons‘ function inside TrelloPowerUp.initialize. We will need to define a button that will trigger the YouTube Power-Up. We’ll give it an icon, text, and a callback function that will be called when the button is clicked. This button will be using the gray play button icon, so you’ll need to update the GRAY_ICON constant at the top of the file to refer to the gray icon you just added to the images folder.

var GRAY_ICON = './images/yt_icon_gray.png';

We can also remove the template code since we won’t be using it, so when we’re finished, our card-buttons function will look like this:

'card-buttons': function(t, options) {
    return [{
      // YouTube!
      icon: GRAY_ICON,
      text: 'YouTube',
      callback: youTubeButtonCallback

Now we need to define the youTubeButtonCallback. This is where we’ll display a popup so the user can search for a video on YouTube. We’ll make requests to the YouTube Data API as the user types and then return a list of results that will get displayed in our popup. In the code below we’re using jQuery to make a request to the YouTube Data API to get a list of results based on what the user has typed into the search box. Each result contains “text” and “callback” properties. The text is what gets displayed in the list, and the callback is the function that gets called when an item in the list is clicked. In this case, we want to attach the YouTube video to the card. Remember to update the YouTube key to your own key which you created above or searching will fail!

var youTubeButtonCallback = function (t) {

    return t.popup({
        title: 'YouTube',
        items: function (t, options) {

            // use which is the search text entered so far
            // return a Promise that resolves to an array of items
            // similar to the items you provided in the client side version above
            var response = $.ajax({
                url: "",
                data: {
                    maxResults: '25',
                    type: 'video',
                    part: 'snippet',
                    key: 'AIzaSyCawso6-SQJS2JAw7FCXQD-sNeLtzDPxE0'
                success: function (data) {
                    //                console.log(data);
                error: function (jqXHR, textStatus, errorThrown) {

            // when the response is finished, then return a list of items
            return response.then(function (data) {
                //                console.log(data);
                var ret = new Array();
                var items = data.items;
                for (var i = 0; i < items.length; i++) {
                        text: items[i].snippet.title,
                        callback: (function (item) {
                            return function (t, opts) {
                                return t.attach({
                                    name: item.snippet.title, // optional
                                    url: "" + // required
                return ret;
        search: {
            // optional # of ms to debounce search to
            // defaults to 300, override must be larger than 300
            debounce: 300,
            placeholder: 'Search',
            empty: 'No results',
            searching: 'Searching'



On to "attachment-sections"! This is what defines how the YouTube url we are attaching in the YouTube callback will get displayed on the card. There are three files that need to be updated, let's start with section.html. In this file we're simply defining a container to hold our YouTube videos:

    <div id="content">
        <div id="videos"></div>
    <script src="./js/section.js"></script>

Notice the section.js import. This is the next file we need to edit. Let's look at the render function:

t.render(function () {
    // make sure your rendering logic lives here, since we will
    // recall this method as the user adds and removes attachments
    // from your section
        .filter(function (attachment) {
            return attachment.url.indexOf('') == 0;
        .then(function (youtubeAttachments) {
            var urls = (a) {
                return '<div class="video"><iframe src="' + a.url + '?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></div>';
            document.getElementById('videos').innerHTML = urls.join(' ');
        .then(function () {
            return t.sizeTo('#content');

Since we are interested in embedding YouTube videos, we're filtering for urls that begin with "". For each video we find, we're adding a new iframe and adding it to the videos div that we defined in section.html. Finally, we need to update the "attachment-sections" function in client.js.

    'attachment-sections': function (t, options) {
        // options.entries is a list of the attachments for this card
        // you can look through them and 'claim' any that you want to
        // include in your section.

        // we will just claim urls for YouTube
        var claimed = options.entries.filter(function (attachment) {
            // claim youtube urls
            return attachment.url.indexOf('') === 0;

        // you can have more than one attachment section on a card
        // you can group items together into one section, have a section
        // per attachment, or anything in between.
        if (claimed && claimed.length > 0) {
            // if the title for your section requires a network call or other
            // potentially length operation you can provide a function for the title
            // that returns the section title. If you do so, provide a unique id for
            // your section
            return [{
                id: 'YouTube', // optional if you aren't using a function for the title
                claimed: claimed,
                icon: YOUTUBE_ICON,
                title: 'YouTube Videos',
                content: {
                    type: 'iframe',
                    url: t.signUrl('./section.html', {
                        arg: 'you can pass your section args here'
                    height: 230
        } else {
            return [];

As in section.js we need a filter for urls that begin with "" because these are the only urls we want to include in our YouTube attachment section. The "id" will be the name of the attachment section, and the "icon" will show up next to the name. We are using the standard YouTube icon here, so we'll need to create a constant at the top of our file that points to the red YouTube icon.

var YOUTUBE_ICON = './images/yt_icon_rgb.png';

Code Cleanup

You're done with the hard part! Since we cloned this project from the sample codebase there is a lot of code we don't need, so let's delete it. Starting with client.js, there are several hooks defined that we're not using. You can delete everything except "card-buttons" and "attachment-sections". In client.js we can also get rid of the callback functions we're not using (just be sure to keep the youTubeButtonCallback).

All done!

That's it! Go back to, open up your board, and if you don't already have a card you should create one. Then when you edit your card you will see the YouTube button on the right side of the card under "Power-Ups". When you click the button, the search popup will come up and you can start searching for a YouTube video. Click on the name of the video you want to embed, and Voila, it will appear near the top of your card under the YouTube heading.

Congratulations on completing your YouTube Power-Up!