<?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"><channel><title><![CDATA[IJHDev Blog]]></title><description><![CDATA[The blog of a tech lead, equal parts tutorial and archive for things I have worked on.]]></description><link>https://github.com/seperot/sepe-devblog</link><generator>RSS for Node</generator><lastBuildDate>Thu, 25 May 2023 12:59:01 GMT</lastBuildDate><item><title><![CDATA[More Android Gradle tips]]></title><description><![CDATA[Going over some lesser known configuring tools for Gradle on an Android project
]]></description><link>https://github.com/seperot/sepe-devblog/gradle-two/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/gradle-two/</guid><pubDate>Fri, 26 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Going over some lesser known configuring tools for Gradle on an Android project&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;&lt;a href=&quot;https://ijh.dev/gradle-one&quot;&gt;In part one&lt;/a&gt; we talked about how Android Gradle is a powerful build tool that allows developers to easily manage dependencies, build multiple variants of an app, and customise build settings. However, many developers may not be aware of some of the lesser-known features and options that Android Gradle provides. In this blog post, we’ll take a look at some tips and tricks for using Android Gradle that may not be as well-known, but can greatly improve your Android development process.&lt;/p&gt;
&lt;h3&gt;1. Using the lintGradle task&lt;/h3&gt;
&lt;p&gt;The lintGradle task is a built-in task that is used to run lint checks on your project’s Gradle files. This can be extremely useful for detecting and fixing issues with your build files, such as unresolved dependencies or misconfigured tasks. To run the lintGradle task, simply navigate to the root of your project in the command line and run the command &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;./gradlew lintGradle.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;2. Using the dependencyInsight task&lt;/h3&gt;
&lt;p&gt;The dependencyInsight task is a built-in task that is used to show detailed information about a specific dependency in your project. This can be useful for understanding the transitive dependencies of a specific library and how they are being used in your project. To run the dependencyInsight task, simply navigate to the root of your project in the command line and run the command &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;./gradlew dependencyInsight --configuration &amp;lt;configuration&amp;gt; --dependency &amp;lt;dependency&amp;gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;3. Using the dependencyUpdates task&lt;/h3&gt;
&lt;p&gt;The dependencyUpdates task is a built-in task that is used to check for updates to the dependencies in your project. This can be useful for keeping your project up-to-date with the latest versions of libraries, and for identifying potential security vulnerabilities. To run the dependencyUpdates task, simply navigate to the root of your project in the command line and run the command &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;./gradlew dependencyUpdates.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;4. Using the dependencyCheck plugin&lt;/h3&gt;
&lt;p&gt;The dependencyCheck plugin is a third-party plugin that can be used to check for known vulnerabilities in the dependencies of your project. This can be a useful tool for identifying and addressing potential security issues in your project. To use the dependencyCheck plugin, simply add the following to your build.gradle file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;dependencies {
    dependencyCheck gradlePlugin
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;5. Using the maven-publish plugin&lt;/h3&gt;
&lt;p&gt;The maven-publish plugin is a built-in plugin that allows you to publish your project to a Maven repository. This can be useful for sharing your project with others or for distributing your project as a library. To use the maven-publish plugin, simply add the following to your build.gradle file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apply plugin: &amp;#39;maven-publish&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;6. Using the signing plugin&lt;/h3&gt;
&lt;p&gt;The signing plugin is a built-in plugin that allows you to sign your apk files with a private key. This is a necessary step for publishing your app to the Google Play Store, and for creating a release version of your app. To use the signing plugin, simply add the following to your build.gradle file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apply plugin: &amp;#39;signing&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;7. Using the com.jfrog.bintray plugin&lt;/h3&gt;
&lt;p&gt;The com.jfrog.bintray plugin is a third-party plugin that allows you to easily publish your project to the Bintray package repository. This can be useful for distributing your project as a library, and for publishing to the JCenter repository, which is used by default in many Android projects. To use the com.jfrog.bintray plugin, you need to add the following to your build.gradle file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apply plugin: &amp;#39;com.jfrog.bintray&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and then configure the bintray credentials and package details in the build.gradle file.&lt;/p&gt;
&lt;h3&gt;8. Using the com.github.dcendents.android-maven plugin&lt;/h3&gt;
&lt;p&gt;The com.github.dcendents.android-maven plugin is a third-party plugin that allows you to easily publish your android library to Maven Central Repository. This can be useful for distributing your android library widely and make it easier for other developers to use it. To use the com.github.dcendents.android-maven plugin, you need to add the following to your build.gradle file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apply plugin: &amp;#39;com.github.dcendents.android-maven&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and then configure the Maven Central Repository details in the build.gradle file.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Android Gradle tips]]></title><description><![CDATA[Going over the basics for configuring Gradle on an Android project
]]></description><link>https://github.com/seperot/sepe-devblog/gradle-one/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/gradle-one/</guid><pubDate>Fri, 14 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Going over the basics for configuring Gradle on an Android project&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;Android Gradle is a powerful build tool that allows developers to easily manage dependencies, build multiple variants of an app, and customise build settings. In this blog post, we’ll take a look at some tips and tricks for using Android Gradle to streamline your Android development process.&lt;/p&gt;
&lt;h3&gt;1. Using the compile vs implementation configuration&lt;/h3&gt;
&lt;p&gt;When adding dependencies to your build.gradle file, you may have noticed two different configuration options: compile and implementation. Both options serve the same purpose, but implementation is the recommended configuration for new projects. The difference between the two is that compile also includes transitive dependencies, while implementation only includes the specified dependency. This can lead to version conflicts and unexpected behaviour if not managed properly.&lt;/p&gt;
&lt;h3&gt;2. Managing dependencies with the dependencies block&lt;/h3&gt;
&lt;p&gt;The dependencies block in the build.gradle file is where you specify all of the dependencies for your project. When adding a new dependency, you can use the compile or implementation configuration and specify the library name, version, and any additional configuration options.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;dependencies {
    implementation &amp;#39;com.android.support:appcompat-v7:28.0.0&amp;#39;
    implementation &amp;#39;com.google.firebase:firebase-core:16.0.8&amp;#39;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;3. Using the exclude configuration&lt;/h3&gt;
&lt;p&gt;If you find yourself in a situation where a transitive dependency is causing conflicts or unexpected behavior, you can use the exclude configuration to remove it. The exclude configuration allows you to specify specific dependencies to be excluded from a transitive dependency.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;dependencies {
    implementation &amp;#39;com.android.support:appcompat-v7:28.0.0&amp;#39;
    implementation (&amp;#39;com.google.firebase:firebase-core:16.0.8&amp;#39;) {
        exclude group: &amp;#39;com.android.support&amp;#39;, module: &amp;#39;support-v4&amp;#39;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;4. Using the configurations block&lt;/h3&gt;
&lt;p&gt;The configurations block allows you to configure various aspects of your build, such as the Java version, packaging options, and signing settings. For example, you can use the configurations block to specify the minimum and target SDK version for your app.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;android {
    compileSdkVersion 28
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 28
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;5. Using the buildTypes block&lt;/h3&gt;
&lt;p&gt;The buildTypes block allows you to configure different build types for your project, such as debug and release. This is useful for building different versions of your app with different settings, such as signing and obfuscation.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;android {
    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            proguardFiles getDefaultProguardFile(&amp;#39;proguard-android-optimize.txt&amp;#39;), &amp;#39;proguard-rules.pro&amp;#39;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;6. Using the productFlavors block&lt;/h3&gt;
&lt;p&gt;The productFlavors block allows you to configure different product flavors for your project, such as free and paid. This is useful for building different versions of your app with different features, such as in-app purchases or ads.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;android {
    productFlavors {
        free {
            applicationId &amp;quot;com.example.app.free&amp;quot;
        }
        paid {
            applicationId &amp;quot;com.example.app.paid&amp;quot;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;7. Using the com.android.application plugin&lt;/h3&gt;
&lt;p&gt;The com.android.application plugin is the main plugin for building Android applications. It provides a number of tasks for building, testing, and deploying your app. It also provides access to the Android SDK and other dependencies needed for building your app.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apply plugin: &amp;#39;com.android.application&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;8. Using the com.android.library plugin&lt;/h3&gt;
&lt;p&gt;The com.android.library plugin is used for building Android libraries. It provides a number of tasks for building, testing, and deploying your library. It also provides access to the Android SDK and other dependencies needed for building your library.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apply plugin: &amp;#39;com.android.library&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;9. Using the com.android.test plugin&lt;/h3&gt;
&lt;p&gt;The com.android.test plugin is used for building and running Android instrumentation tests. It provides a number of tasks for building and running tests, as well as configuring test options such as test runner and test instrumentation.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apply plugin: &amp;#39;com.android.test&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;10. Using the com.android.support library&lt;/h3&gt;
&lt;p&gt;The com.android.support library is a set of libraries that provide backwards compatibility for older versions of Android. It includes support for various UI components, as well as other features such as appcompat, design, and cardview.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;dependencies {
    implementation &amp;#39;com.android.support:appcompat-v7:28.0.0&amp;#39;
    implementation &amp;#39;com.android.support:design:28.0.0&amp;#39;
    implementation &amp;#39;com.android.support:cardview-v7:28.0.0&amp;#39;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In conclusion, Android Gradle is a powerful build tool that provides a number of features and options for building, testing, and deploying Android apps and libraries. By using the tips and tricks outlined in this post, you can streamline your Android development process and make the most out of the powerful capabilities of Android Gradle.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[OKRs]]></title><description><![CDATA[Objectives and Key Results (OKRs) are a goal-setting framework that has been adopted by companies like Google to drive growth and focus.
]]></description><link>https://github.com/seperot/sepe-devblog/okrs/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/okrs/</guid><pubDate>Fri, 24 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Objectives and Key Results (OKRs) are a goal-setting framework that has been adopted by companies like Google to drive growth and focus.&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;The idea behind OKRs is simple: set clear, measurable objectives and track progress through key results. But OKRs are more than just a set of goals – they are a way of thinking and working that can transform an organization. In this article, we’ll explore what OKRs are, the advantages they offer over other goal-setting processes, and some examples of how they have been used in practice.&lt;/p&gt;
&lt;h3&gt;What are OKRs?&lt;/h3&gt;
&lt;p&gt;OKRs were first developed by Andy Grove, the former CEO of Intel, in the 1970s. The basic idea behind OKRs is to set clear, measurable objectives that are aligned with the organization’s overall goals and track progress through key results. OKRs are typically set on a quarterly or annual basis and involve all levels of the organization, from the CEO to individual team members.&lt;/p&gt;
&lt;p&gt;Here’s an example of how OKRs might work in practice:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Objective:&lt;/strong&gt; Increase customer satisfaction&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key results:&lt;/strong&gt; &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Increase Net Promoter Score (NPS) from 50 to 60&lt;/li&gt;
&lt;li&gt;Reduce customer churn rate by 10%&lt;/li&gt;
&lt;li&gt;Increase customer satisfaction survey scores by 5%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this example, the objective is to increase customer satisfaction, and the key results are specific, measurable targets that will help the organization achieve this goal. By tracking progress towards these key results, the organization can see how well they are doing in terms of meeting their objective.&lt;/p&gt;
&lt;h3&gt;Advantages of OKRs&lt;/h3&gt;
&lt;p&gt;OKRs offer several advantages over other goal-setting processes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Focus and alignment:&lt;/strong&gt; OKRs help organizations stay focused on their most important goals by setting clear objectives and key results. This ensures that everyone in the organization is working towards the same priorities and that resources are being directed towards high-impact projects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Transparency and accountability:&lt;/strong&gt; OKRs are typically shared with the entire organization, which promotes transparency and accountability. By making progress visible to everyone, OKRs encourage team members to work towards their goals and celebrate their achievements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flexibility:&lt;/strong&gt; OKRs are designed to be flexible and adaptable, which allows organizations to pivot and respond to changing circumstances. This can be especially important in fast-moving industries where the landscape is constantly shifting.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Motivation:&lt;/strong&gt; OKRs can be a powerful motivator for employees because they provide a sense of purpose and direction. When team members understand how their work fits into the bigger picture, they are more likely to be engaged and motivated.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Examples of OKRs in action&lt;/h3&gt;
&lt;p&gt;OKRs have been used by a wide range of organizations, from startups to Fortune 500 companies. Here are a few examples of how OKRs have been used in practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Google:&lt;/strong&gt; OKRs have been central to Google’s growth and success since the company’s early days. Google sets OKRs on a quarterly basis, with each team responsible for setting their own objectives and key results. This decentralized approach allows teams to focus on their own priorities while still being aligned with the overall goals of the organization.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LinkedIn:&lt;/strong&gt; LinkedIn has used OKRs to drive innovation and growth since the company’s early days. In 2012, LinkedIn CEO Jeff Weiner implemented a company-wide OKR program, which has helped the company stay focused on its most important goals and drive growth.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Asana:&lt;/strong&gt; Asana, a project management software company, has used OKRs to drive growth and focus since the company’s founding in 2008. Asana sets OKRs on a quarterly basis and has found that the process helps the company stay focused and aligned while also allowing for flexibility and adaptability.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Challenges of OKRs&lt;/h3&gt;
&lt;p&gt;While OKRs offer many benefits, they are not without their challenges. Here are a few potential drawbacks to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Time and effort:&lt;/strong&gt; Implementing OKRs requires time and effort, particularly in the beginning as teams get used to the process. There is a learning curve involved in setting clear objectives and key results, and it can take time to get everyone on board.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Risk of over-optimization:&lt;/strong&gt; By focusing on a few key objectives, organizations risk neglecting other important areas. It’s important to strike a balance and ensure that OKRs are not overly narrow or short-sighted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Culture fit:&lt;/strong&gt; OKRs work best in organizations with a culture of transparency, collaboration, and continuous improvement. If your company doesn’t have these values, it may be difficult to implement OKRs successfully.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Resistance to change:&lt;/strong&gt; Like any new process, OKRs can be met with resistance, especially if employees are used to working in a different way. It’s important to communicate the benefits of OKRs and get buy-in from team members before implementing them.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;OKRs are a powerful goal-setting framework that can help organizations stay focused and drive growth. While there are potential challenges to implementing OKRs, the benefits often outweigh the cons. If your company values transparency, collaboration, and continuous improvement, OKRs may be worth considering. &lt;/p&gt;
&lt;p&gt;To make the most of OKRs, it’s important to set clear objectives and key results, track progress regularly, and be open to adapting and adjusting as needed. With the right approach, OKRs can help your organization stay focused, aligned, and motivated as you work towards your most important goals.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Android Jetpack Compose]]></title><description><![CDATA[Jetpack Compose is a modern toolkit for building native Android UI
]]></description><link>https://github.com/seperot/sepe-devblog/jetpack-compose/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/jetpack-compose/</guid><pubDate>Fri, 10 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Jetpack Compose is a modern toolkit for building native Android UI&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;It was created by Google as a response to the increasing complexity of designing user interfaces for Android apps. With Jetpack Compose, developers can use a declarative and reactive approach to building UI, which makes it easier to create visually stunning and intuitive layouts.&lt;/p&gt;
&lt;p&gt;In this blog post, we will cover the following topics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What is Jetpack Compose?&lt;/li&gt;
&lt;li&gt;Why was Jetpack Compose created?&lt;/li&gt;
&lt;li&gt;How to set up Jetpack Compose in your Android project&lt;/li&gt;
&lt;li&gt;Coding examples with Jetpack Compose&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What is Jetpack Compose?&lt;/h3&gt;
&lt;p&gt;Jetpack Compose is a declarative and reactive UI toolkit for Android. It allows developers to create UI elements using Kotlin code, rather than XML layout files. This means that developers can write UI code that is more concise, easier to read, and easier to understand.&lt;/p&gt;
&lt;p&gt;One of the key benefits of Jetpack Compose is that it follows a declarative programming model. This means that developers specify what they want their UI to look like, rather than specifying how to build it. This makes it easier to create and modify UI elements, as developers only need to worry about the end result, rather than the individual steps needed to achieve it.&lt;/p&gt;
&lt;p&gt;Jetpack Compose also follows a reactive programming model, which means that the UI is automatically updated whenever the underlying data changes. This makes it easier to build dynamic and interactive UI, as developers don’t need to manually update the UI when data changes.&lt;/p&gt;
&lt;h3&gt;Why was Jetpack Compose created?&lt;/h3&gt;
&lt;p&gt;Jetpack Compose was created as a response to the increasing complexity of designing user interfaces for Android apps. In the past, Android developers had to use a combination of XML layout files and Java/Kotlin code to create their UI. This approach was prone to errors, and it made it difficult to create complex UI layouts.&lt;/p&gt;
&lt;p&gt;With Jetpack Compose, Google aims to simplify the process of building UI for Android apps. By using a declarative and reactive programming model, Jetpack Compose makes it easier for developers to create visually stunning and intuitive layouts. It also allows developers to write UI code that is more concise and easier to understand, which makes it easier to maintain and update the UI over time.&lt;/p&gt;
&lt;h3&gt;How to set up Jetpack Compose in your Android project&lt;/h3&gt;
&lt;p&gt;To use Jetpack Compose in your Android project, you will need to install the latest version of Android Studio (4.1 or higher). Once you have Android Studio installed, you can follow these steps to set up Jetpack Compose in your project:&lt;/p&gt;
&lt;p&gt;Open your Android project in Android Studio.
In the build.gradle file for your project, add the following line to the dependencies block:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;implementation &amp;#39;androidx.ui:ui-tooling:0.1.0-dev02&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the build.gradle file for your app module, add the following lines to the android block:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the AndroidManifest.xml file for your app, add the following attribute to the application element:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;android:usesCleartextTraffic=&amp;quot;true&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In your app’s main activity, import the following packages:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import androidx.compose.ui.platform.setContent
import androidx.ui.core.Text
import androidx.ui.core.setContent
import androidx.ui.graphics.Color
import androidx.ui.layout.Column
import androidx.ui.layout.Spacing
import androidx.ui.material.MaterialTheme
import androidx.ui.tooling.preview.Preview&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the onCreate method of your main activity, set the content view to a Jetpack Compose UI component:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;setContent {
    MaterialTheme {
        // Your Jetpack Compose UI goes here
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This sets the content view of your activity to a Jetpack Compose UI, which will be rendered in the app when it is run.&lt;/p&gt;
&lt;h3&gt;Coding examples with Jetpack Compose&lt;/h3&gt;
&lt;p&gt;Now that you have Jetpack Compose set up in your Android project, let’s look at some coding examples to see how it works in practice.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example 1:&lt;/em&gt; Creating a simple text view
The following code creates a simple text view using Jetpack Compose:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Text(text = &amp;quot;Hello, world!&amp;quot;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will create a text view with the text “Hello, world!” displayed on the screen.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example 2:&lt;/em&gt; Creating a column layout
The following code creates a column layout with three text views:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Column {
    Text(text = &amp;quot;Text view 1&amp;quot;)
    Text(text = &amp;quot;Text view 2&amp;quot;)
    Text(text = &amp;quot;Text view 3&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will create a column layout with three text views, one below the other.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example 3:&lt;/em&gt; Adding styling to a text view
The following code creates a text view with custom styling:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Text(
    text = &amp;quot;Hello, world!&amp;quot;,
    color = Color.Red,
    style = TextStyle(fontSize = 24.sp)
)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will create a text view with the text “Hello, world!” displayed in red and with a font size of 24sp.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example 4:&lt;/em&gt; Using a preview function
Jetpack Compose includes a preview function that allows you to see a live preview of your UI while you are coding. To use the preview function, add the following code to your file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;@Preview
@Composable
fun Preview() {
    // Your Jetpack Compose UI goes here
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will create a preview function that you can use to see a live preview of your UI while you are coding.&lt;/p&gt;
&lt;p&gt;I hope this has helped you understand what Jetpack Compose is and how to use it to build native Android UI. With Jetpack Compose, you can use a declarative and reactive approach to building UI, which makes it easier to create visually stunning and intuitive layouts.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Jumping into a new team as a manager]]></title><description><![CDATA[Pointers on the daunting task of becoming the manager of an existing team.
]]></description><link>https://github.com/seperot/sepe-devblog/ways-of-working/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/ways-of-working/</guid><pubDate>Thu, 05 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Pointers on the daunting task of becoming the manager of an existing team.&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;Starting a new job as a manager can be exciting, but it can also be overwhelming, especially if you are joining a new team. Whether you are moving to a new company or a new department within your organization, there are a few key steps you can take to make the transition smoother and get up to speed more quickly. In this article, we’ll break down what you can do in the first 30, 60, and 90 days on the job to determine and improve your team’s ways of working.&lt;/p&gt;
&lt;h3&gt;30 days:&lt;/h3&gt;
&lt;p&gt;Get to know your team: Your first priority should be to get to know your new team members. Take the time to introduce yourself, learn about their roles and responsibilities, and find out how they prefer to work. This will help you build rapport and understand how your team operates.&lt;/p&gt;
&lt;p&gt;Observe and ask questions: Pay attention to how your team works and ask questions to get a better understanding of their processes and tools. Ask about the team’s goals, challenges, and successes, and try to get a sense of their work culture.&lt;/p&gt;
&lt;p&gt;Find out who the key players are: Determine who the key players are on your team and try to get to know them. These may be subject matter experts, team leads, or other influential team members. Building relationships with these individuals will help you get up to speed more quickly and navigate your new team more effectively.&lt;/p&gt;
&lt;p&gt;Communicate your vision and goals: As a manager, it’s important to communicate your vision and goals for the team. Share your expectations and make sure that your team understands how their work fits into the bigger picture.&lt;/p&gt;
&lt;h3&gt;60 days:&lt;/h3&gt;
&lt;p&gt;Start contributing: After you have had a chance to observe and learn, it’s time to start contributing to your team. This might mean taking on small tasks or projects, or offering to help out with ongoing work. The goal is to demonstrate your value and start building your reputation within the team.&lt;/p&gt;
&lt;p&gt;Identify areas for improvement: As you get more comfortable with your team and their ways of working, start looking for areas where you can add value. This might be by suggesting new tools or processes, or by finding ways to streamline existing workflows.&lt;/p&gt;
&lt;p&gt;Seek feedback: Don’t be afraid to ask for feedback from your team members. This can help you understand how you are perceived and where you can improve. Use this feedback to adjust your approach and better understand your team’s needs and expectations.&lt;/p&gt;
&lt;p&gt;Define roles and responsibilities: Make sure that your team understands their roles and responsibilities clearly. This will help them know what is expected of them and ensure that everyone is working towards the same goals.&lt;/p&gt;
&lt;h3&gt;90 days:&lt;/h3&gt;
&lt;p&gt;Take on more responsibility: As you gain more experience and build your reputation within the team, start taking on more responsibility. This might involve leading small projects or taking on additional tasks and responsibilities.&lt;/p&gt;
&lt;p&gt;Make your mark: By now, you should have a good understanding of your team’s goals and ways of working. Use this knowledge to make a positive impact and contribute to your team’s success.&lt;/p&gt;
&lt;p&gt;Build relationships outside of your team: In addition to building relationships within your team, start networking and building relationships with other teams and departments. This can help you better understand the broader organization and find opportunities for collaboration.&lt;/p&gt;
&lt;p&gt;Set performance goals: As a manager, it’s important to set performance goals for your team and track progress towards these goals. This will help you measure the team’s performance and identify areas for improvement.&lt;/p&gt;
&lt;h3&gt;Beyond 90 days:&lt;/h3&gt;
&lt;p&gt;Continue learning: No matter how long you have been with your team, there is always more to learn. Keep an open mind and be willing to learn from your team members and other colleagues.&lt;/p&gt;
&lt;p&gt;Foster a culture of continuous improvement: Encourage your team to regularly review and assess their ways of working and look for opportunities to improve. This might involve experimenting with new tools or processes, or seeking feedback from other teams.&lt;/p&gt;
&lt;p&gt;Develop your team: As a manager, it’s your responsibility to help your team grow and develop. This might involve providing training and development opportunities, setting performance goals, or offering feedback and support.&lt;/p&gt;
&lt;p&gt;Lead by example: As a manager, your team looks to you for guidance and direction. Make sure that you are setting a good example by being reliable, respectful, and proactive.&lt;/p&gt;
&lt;p&gt;In conclusion, moving into a new team as a manager can be challenging, but with the right approach, you can quickly get up to speed and make a positive impact. By getting to know your team, observing and asking questions, and communicating your vision and goals, you can build relationships and understand your team’s ways of working. As you gain more experience and build your reputation, you can take on more responsibility, make your mark, and foster a culture of continuous improvement. With the right approach, you can be a successful leader and help your team succeed.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Android Fastlane CICD Image]]></title><description><![CDATA[
Easy to use CI/CD Image for Android builds

]]></description><link>https://github.com/seperot/sepe-devblog/android-cicd-image/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/android-cicd-image/</guid><pubDate>Mon, 01 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Easy to use CI/CD Image for Android builds&lt;/p&gt;
&lt;!-- end --&gt;
&lt;h1&gt;What is it&lt;/h1&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAQD/8QAFgEBAQEAAAAAAAAAAAAAAAAABAEC/9oADAMBAAIQAxAAAAHGscSMbn//xAAWEAEBAQAAAAAAAAAAAAAAAAAAAzH/2gAIAQEAAQUCXx//xAAWEQADAAAAAAAAAAAAAAAAAAABEDH/2gAIAQMBAT8BEX//xAAWEQEBAQAAAAAAAAAAAAAAAAABEDL/2gAIAQIBAT8BdE//xAAWEAADAAAAAAAAAAAAAAAAAAAAAhD/2gAIAQEABj8CFn//xAAaEAAABwAAAAAAAAAAAAAAAAAAARAhMZHh/9oACAEBAAE/ISkWHxP/2gAMAwEAAgADAAAAEPvP/8QAFhEAAwAAAAAAAAAAAAAAAAAAEDGB/9oACAEDAQE/EGQf/8QAFhEAAwAAAAAAAAAAAAAAAAAAARCh/9oACAECAQE/EBov/8QAGhABAAIDAQAAAAAAAAAAAAAAAREhABAxYf/aAAgBAQABPxBQFJvmKtBQhAIHxr//2Q==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;What is it&quot;
        title=&quot;&quot;
        src=&quot;/static/androidCISmall-2a653c29bf3049054c7650642809b02b-177e4.jpg&quot;
        srcset=&quot;/static/androidCISmall-2a653c29bf3049054c7650642809b02b-6c2d5.jpg 175w,
/static/androidCISmall-2a653c29bf3049054c7650642809b02b-3d17c.jpg 350w,
/static/androidCISmall-2a653c29bf3049054c7650642809b02b-177e4.jpg 700w,
/static/androidCISmall-2a653c29bf3049054c7650642809b02b-40bb7.jpg 1050w,
/static/androidCISmall-2a653c29bf3049054c7650642809b02b-562d0.jpg 1400w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;p&gt;A maintained Android Image for CI pipelines. Can be used for any CICD that uses Docker hub, works with standard Android or Fastlane. Use the image as is or pull the code yourself and make it work for your specific needs.&lt;/p&gt;
&lt;h2&gt;Latest version&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;image: ijhdev/gitlab-ci-fastlane-android&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;How to use it&lt;/h1&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAgED/9oADAMBAAIQAxAAAAHYkLKgi//EABUQAQEAAAAAAAAAAAAAAAAAAABC/9oACAEBAAEFAlv/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPwFZ/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8BJ//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABkQAAMBAQEAAAAAAAAAAAAAAAABESExQf/aAAgBAQABPyGQasvcOes//9oADAMBAAIAAwAAABD73//EABYRAQEBAAAAAAAAAAAAAAAAAAEAIf/aAAgBAwEBPxBByNL/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPxACT//EABsQAAICAwEAAAAAAAAAAAAAAAERADEhYXHh/9oACAEBAAE/EMib2bgEqF0ewj0VP//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;how to use the Android Fastlane Image&quot;
        title=&quot;&quot;
        src=&quot;/static/howitworks-b10a32e5ad0a1f80b8e26f26bbce262e-177e4.jpg&quot;
        srcset=&quot;/static/howitworks-b10a32e5ad0a1f80b8e26f26bbce262e-6c2d5.jpg 175w,
/static/howitworks-b10a32e5ad0a1f80b8e26f26bbce262e-3d17c.jpg 350w,
/static/howitworks-b10a32e5ad0a1f80b8e26f26bbce262e-177e4.jpg 700w,
/static/howitworks-b10a32e5ad0a1f80b8e26f26bbce262e-40bb7.jpg 1050w,
/static/howitworks-b10a32e5ad0a1f80b8e26f26bbce262e-562d0.jpg 1400w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;h2&gt;Example Gitlab .Yaml&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;image: ijhdev/gitlab-ci-fastlane-android

variables:
 ANDROID_COMPILE_SDK: &amp;quot;31&amp;quot;
 ANDROID_BUILD_TOOLS: &amp;quot;30.0.3&amp;quot;
 ANDROID_SDK_TOOLS:   &amp;quot;7583922&amp;quot;
 LC_ALL: &amp;quot;en_US.UTF-8&amp;quot;
 LANG: &amp;quot;en_US.UTF-8&amp;quot;
 GIT_STRATEGY: clone

before_script:
 - export GRADLE_USER_HOME=$(pwd)/.gradle
 - chmod +x ./gradlew

cache:
 key: ${CI_PROJECT_ID}
 paths:
   - .gradle/

stages:
 - unit_test
 - debug_build

unit_test:
 tags:
   - your_build_runner
 dependencies: []
 stage: unit_test
 artifacts:
   paths:
     - fastlane/screenshots
     - fastlane/logs
   expire_in: 1 hour
 script:
   - fastlane tests

debug_build:
 tags:
   - your_build_runner
 dependencies: []
 stage: debug_build
 artifacts:
   paths:
     - app/build/outputs/
   expire_in: 1 week
 script:
   - bash ./version_updater.sh
   - fastlane yourDebug&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Example Bitrise&lt;/h2&gt;
&lt;h3&gt;Open up bitrise and the project you want to change the image for&lt;/h3&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 56.22641509433962%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHuptDS/wD/xAAWEAEBAQAAAAAAAAAAAAAAAAAQAjH/2gAIAQEAAQUCZw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAVEAEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAQAGPwJj/8QAGBAAAwEBAAAAAAAAAAAAAAAAARAxIYH/2gAIAQEAAT8h6wE4Mf8A/9oADAMBAAIAAwAAABB3D//EABYRAQEBAAAAAAAAAAAAAAAAAAEREP/aAAgBAwEBPxAaXP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAAIDAQEAAAAAAAAAAAAAAAERACExURD/2gAIAQEAAT8QHQzy9lpAQErkxyiwyvBk/9k=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Bitrise page one&quot;
        title=&quot;&quot;
        src=&quot;/static/bitrise1-49800e9d071673e174c0ad1e3fa796fa-177e4.jpg&quot;
        srcset=&quot;/static/bitrise1-49800e9d071673e174c0ad1e3fa796fa-6c2d5.jpg 175w,
/static/bitrise1-49800e9d071673e174c0ad1e3fa796fa-3d17c.jpg 350w,
/static/bitrise1-49800e9d071673e174c0ad1e3fa796fa-177e4.jpg 700w,
/static/bitrise1-49800e9d071673e174c0ad1e3fa796fa-3e98a.jpg 795w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;h3&gt;From here click on Workflow&lt;/h3&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 47.66269477543538%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHcGyShP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAMBAQAAAAAAAAAAAAAAAAABIRBR/9oACAEBAAE/IUoXjKLf/9oADAMBAAIAAwAAABAgH//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAAIDAQAAAAAAAAAAAAAAAAERACExEP/aAAgBAQABPxBAAOhrszUDemJk9//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Bitrise page two&quot;
        title=&quot;&quot;
        src=&quot;/static/bitrise2-4b033aa40f91950cbd45b93a8dc5a598-177e4.jpg&quot;
        srcset=&quot;/static/bitrise2-4b033aa40f91950cbd45b93a8dc5a598-6c2d5.jpg 175w,
/static/bitrise2-4b033aa40f91950cbd45b93a8dc5a598-3d17c.jpg 350w,
/static/bitrise2-4b033aa40f91950cbd45b93a8dc5a598-177e4.jpg 700w,
/static/bitrise2-4b033aa40f91950cbd45b93a8dc5a598-40bb7.jpg 1050w,
/static/bitrise2-4b033aa40f91950cbd45b93a8dc5a598-44f46.jpg 1091w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;h3&gt;Once in Workflow navigate to Stack&lt;/h3&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 45.85308056872038%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdymCKD/xAAWEAEBAQAAAAAAAAAAAAAAAAAAESD/2gAIAQEAAQUCRMf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAADAQEAAAAAAAAAAAAAAAAAARFRIP/aAAgBAQABPyGYhaI4/9oADAMBAAIAAwAAABAzD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAEDBQAAAAAAAAAAAAAAAAABEVEQMUFhof/aAAgBAQABPxC9UcR5IBo6Zr//2Q==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Bitrise page three&quot;
        title=&quot;&quot;
        src=&quot;/static/bitrise3-1980f301e06cbf1075dcd5436ea9a7ba-177e4.jpg&quot;
        srcset=&quot;/static/bitrise3-1980f301e06cbf1075dcd5436ea9a7ba-6c2d5.jpg 175w,
/static/bitrise3-1980f301e06cbf1075dcd5436ea9a7ba-3d17c.jpg 350w,
/static/bitrise3-1980f301e06cbf1075dcd5436ea9a7ba-177e4.jpg 700w,
/static/bitrise3-1980f301e06cbf1075dcd5436ea9a7ba-4ebb4.jpg 844w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;h3&gt;Here you will see default Stack and Workflow Specific Stasks. If you want to change the build image for all steps then use default but if there is just one area failing for you then use the Workflow specific stacks&lt;/h3&gt;
&lt;h1&gt;But wait&lt;/h1&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAQF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAED/9oADAMBAAIQAxAAAAGmMNAZ3//EABoQAAEFAQAAAAAAAAAAAAAAAAIAAQQREiH/2gAIAQEAAQUCkdLKB7D/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPwGn/8QAGBAAAgMAAAAAAAAAAAAAAAAAAAEQEVH/2gAIAQEABj8CrIR//8QAGRABAAMBAQAAAAAAAAAAAAAAAQARMSFx/9oACAEBAAE/IbHbgz2F6FvmxEupP//aAAwDAQACAAMAAAAQ/D//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxBn/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8QET//xAAaEAEBAAIDAAAAAAAAAAAAAAABEQAhUWGR/9oACAEBAAE/EAtEqItUlfMJL0EjZNAdYzd2HnP/2Q==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;theres more!&quot;
        title=&quot;&quot;
        src=&quot;/static/butwait-e4f13cb027d950225a6a2f116e33aa6a-177e4.jpg&quot;
        srcset=&quot;/static/butwait-e4f13cb027d950225a6a2f116e33aa6a-6c2d5.jpg 175w,
/static/butwait-e4f13cb027d950225a6a2f116e33aa6a-3d17c.jpg 350w,
/static/butwait-e4f13cb027d950225a6a2f116e33aa6a-177e4.jpg 700w,
/static/butwait-e4f13cb027d950225a6a2f116e33aa6a-40bb7.jpg 1050w,
/static/butwait-e4f13cb027d950225a6a2f116e33aa6a-562d0.jpg 1400w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;p&gt;Also included in the Github codebase is an Android auto version updater. The version updater takes your Google Play store listing and checks it against your Gradle version. If your Gradle version number is higher than the app store it will stick with it, if it’s equal or lower that the store version then it will take that, +1 to the end, then change the Gradle version for that one.&lt;/p&gt;
&lt;p&gt;All you need to do is take the version_updater.sh file from the Github and put it in the root folder of your app and edit this line to have your bundle ID&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;curl -O playstorepage https://play.google.com/store/apps/details\?id\=&amp;lt;YourBundleID&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;Check out the Image and Code here&lt;/h1&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAQF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAID/9oADAMBAAIQAxAAAAGHTI0gB//EABgQAQEAAwAAAAAAAAAAAAAAAAEAAhMh/9oACAEBAAEFAgsA09v/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPwFn/8QAFxAAAwEAAAAAAAAAAAAAAAAAAAExEf/aAAgBAQAGPwKiWFP/xAAZEAADAAMAAAAAAAAAAAAAAAAAAREhQWH/2gAIAQEAAT8hYlmiy9F0H//aAAwDAQACAAMAAAAQgC//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxAJ/8QAFREBAQAAAAAAAAAAAAAAAAAAAAH/2gAIAQIBAT8QpH//xAAaEAEBAAMBAQAAAAAAAAAAAAABEQAhQTFR/9oACAEBAAE/EL6q0dife40AjU3duIzQb9L3P//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;links&quot;
        title=&quot;&quot;
        src=&quot;/static/library-07a3f1bf3197bcb5d1eb7fc4b3cc084b-177e4.jpeg&quot;
        srcset=&quot;/static/library-07a3f1bf3197bcb5d1eb7fc4b3cc084b-6c2d5.jpeg 175w,
/static/library-07a3f1bf3197bcb5d1eb7fc4b3cc084b-3d17c.jpeg 350w,
/static/library-07a3f1bf3197bcb5d1eb7fc4b3cc084b-177e4.jpeg 700w,
/static/library-07a3f1bf3197bcb5d1eb7fc4b3cc084b-40bb7.jpeg 1050w,
/static/library-07a3f1bf3197bcb5d1eb7fc4b3cc084b-562d0.jpeg 1400w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hub.docker.com/repository/docker/ijhdev/gitlab-ci-fastlane-android/general&quot;&gt;https://hub.docker.com/repository/docker/ijhdev/gitlab-ci-fastlane-android/general&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/seperot/Android_Fastlane_CICD_Image&quot;&gt;https://github.com/seperot/Android&lt;em&gt;Fastlane&lt;/em&gt;CICD_Image&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Simple Reusable Dialog for Android]]></title><description><![CDATA[A small native Android/Kotlin library that helps build dialogs with however many buttons in however many styles you want.
]]></description><link>https://github.com/seperot/sepe-devblog/simple-reusable-dialog/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/simple-reusable-dialog/</guid><pubDate>Wed, 08 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A small native Android/Kotlin library that helps build dialogs with however many buttons in however many styles you want.&lt;/p&gt;
&lt;!-- end --&gt;
&lt;h1&gt;What is it&lt;/h1&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAUE/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAamoSaF//8QAFxAAAwEAAAAAAAAAAAAAAAAAAQMQMf/aAAgBAQABBQJMO//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAQEBAAAAAAAAAAAAAAAAAAEAEP/aAAgBAQAGPwJxv//EABgQAAIDAAAAAAAAAAAAAAAAAAARAaHB/9oACAEBAAE/IcxqS0f/2gAMAwEAAgADAAAAEAQP/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QZ//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAAICAwEAAAAAAAAAAAAAAAABESFBobHw/9oACAEBAAE/EFTcvLLCtxGTcdP/2Q==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Dialog&quot;
        title=&quot;&quot;
        src=&quot;/static/dialogsmol-40da271c284577ba7f7c782e97d8ddfe-177e4.jpeg&quot;
        srcset=&quot;/static/dialogsmol-40da271c284577ba7f7c782e97d8ddfe-6c2d5.jpeg 175w,
/static/dialogsmol-40da271c284577ba7f7c782e97d8ddfe-3d17c.jpeg 350w,
/static/dialogsmol-40da271c284577ba7f7c782e97d8ddfe-177e4.jpeg 700w,
/static/dialogsmol-40da271c284577ba7f7c782e97d8ddfe-40bb7.jpeg 1050w,
/static/dialogsmol-40da271c284577ba7f7c782e97d8ddfe-562d0.jpeg 1400w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;p&gt;Android dialogs can be a bit of a faffy affair, this small lib gives you a range of customizability while staying native so a lower risk of compatibility issues down the road.&lt;/p&gt;
&lt;h2&gt;Latest version for gradle&lt;/h2&gt;
&lt;p&gt;implementation ‘com.github.seperot:SimpleReusableDialog:1.5’&lt;/p&gt;
&lt;h1&gt;How to use it&lt;/h1&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAIFBv/EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAGVQFa8U//EABkQAAMAAwAAAAAAAAAAAAAAAAECAwQREv/aAAgBAQABBQLJZS1X7eQ1L//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABkQAAIDAQAAAAAAAAAAAAAAAAARAQJhIv/aAAgBAQAGPwLqjWjKxh//xAAaEAEAAwADAAAAAAAAAAAAAAABABEhMWGx/9oACAEBAAE/IXvPUOYyMrAnQR8n/9oADAMBAAIAAwAAABAL7//EABYRAQEBAAAAAAAAAAAAAAAAAAEQEf/aAAgBAwEBPxBMn//EABYRAQEBAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPxAdn//EABoQAQEAAgMAAAAAAAAAAAAAAAERADEhQaH/2gAIAQEAAT8QPLHInHnrNBBAbAIYBWg+M//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;How it works&quot;
        title=&quot;&quot;
        src=&quot;/static/howitworks-e3b80309636a8a749512543036036df2-177e4.jpeg&quot;
        srcset=&quot;/static/howitworks-e3b80309636a8a749512543036036df2-6c2d5.jpeg 175w,
/static/howitworks-e3b80309636a8a749512543036036df2-3d17c.jpeg 350w,
/static/howitworks-e3b80309636a8a749512543036036df2-177e4.jpeg 700w,
/static/howitworks-e3b80309636a8a749512543036036df2-40bb7.jpeg 1050w,
/static/howitworks-e3b80309636a8a749512543036036df2-562d0.jpeg 1400w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;h2&gt;Example button&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;btndemo.setonclicklistener &quot;&gt;&lt;pre class=&quot;language-btndemo.setonclicklistener &quot;&gt;&lt;code class=&quot;language-btndemo.setonclicklistener &quot;&gt;       registerButton(
           getString(
               R.string.dialog_one_button_one),
               R.color.colorPrimary,
               android.R.color.white,
               object : ReusableDialogListener {
                   override fun onDialogButtonClick
                   dialogFragment: DialogFragment, index: Int) {
                       dialogFragment.dismiss()
                   }})

           ReusableDialog.dialogCancelable(false)

           ReusableDialog.createDialogInstance(
               getString(R.string.dialog_one), 
               getString(R.string.dialog_one_text), 
               null).show(
               supportFragmentManager, null
           )
       }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Working parts&lt;/h2&gt;
&lt;h3&gt;registerButton&lt;/h3&gt;
&lt;p&gt;RegisterButton adds a button to the next time you call createDialogInstance it has four parts&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;buttonTitle: The text the button shows&lt;/li&gt;
&lt;li&gt;buttonBackground: The button background color, things like R.color or android.R.color work&lt;/li&gt;
&lt;li&gt;textColor: The button text color, again things like R.color or android.R.color work&lt;/li&gt;
&lt;li&gt;dialogButtonListener: The button onClickListener, this has to be called in the following format currently until I can figure out how to make it look nicer&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;dialogCancelable&lt;/h3&gt;
&lt;p&gt;A simple boolean that turns on or off the users ability to cancel out of the dialog without choosing an option. This also needs to be done before you call createDialogInstance and will reset after use true = they can cancel, false = they cannot cancel This also works for the back button&lt;/p&gt;
&lt;h3&gt;createDialogInstance&lt;/h3&gt;
&lt;p&gt;Calls the dialog and shows it to the user, grabs any registered buttons and checks if the dialog is cancelable or not then displays. Has three fields&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;title: The dialog title&lt;/li&gt;
&lt;li&gt;infoText: The dialog content&lt;/li&gt;
&lt;li&gt;topIcon: The icon at the top of the Dialog, set as null to remove from Dialog&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally be sure to add “.show(supportFragmentManager, null)” At the end to display the dialog&lt;/p&gt;
&lt;p&gt;As you can see in this second example, you can expand this to as many buttons as you like&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;btndemo2.setonclicklistener &quot;&gt;&lt;pre class=&quot;language-btndemo2.setonclicklistener &quot;&gt;&lt;code class=&quot;language-btndemo2.setonclicklistener &quot;&gt;            registerButton(
                getString(R.string.dialog_two_button_one),
                R.color.colorPrimary,
                android.R.color.black,
                object: ReusableDialogListener {
                    override fun onDialogButtonClick(
                        dialogFragment: DialogFragment, 
                        index: Int) {
                    dialogFragment.dismiss()
                    Toast.makeText(
                        applicationContext, 
                        getString(R.string.toast_one), 
                        Toast.LENGTH_LONG).show()
                }})

            registerButton(
                getString(R.string.dialog_two_button_two),
                R.color.colorPrimaryDark,
                android.R.color.white,
               object: ReusableDialogListener {
                   override fun onDialogButtonClick(
                       dialogFragment: DialogFragment, 
                       index: Int) {
                       dialogFragment.dismiss()
                       Toast.makeText(
                           applicationContext,
                           getString(R.string.toast_two),
                           Toast.LENGTH_LONG
                       ).show()
                   }})

            ReusableDialog.createDialogInstance(
                getString(R.string.dialog_two),
                getString(R.string.dialog_two_text),
                R.drawable.pupcat
            ).show(
                supportFragmentManager, null
            )
        }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;Planned features&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;./images/pla.jpeg&quot; alt=&quot;Future Plans&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a setting for custom button margin&lt;/li&gt;
&lt;li&gt;Add a setting for custom button padding&lt;/li&gt;
&lt;li&gt;Add set dialog size and scroll&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Check out the library here&lt;/h1&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAQF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAID/9oADAMBAAIQAxAAAAGHTI0gB//EABgQAQEAAwAAAAAAAAAAAAAAAAEAAhMh/9oACAEBAAEFAgsA09v/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPwFn/8QAFxAAAwEAAAAAAAAAAAAAAAAAAAExEf/aAAgBAQAGPwKiWFP/xAAZEAADAQEBAAAAAAAAAAAAAAAAARFhIUH/2gAIAQEAAT8hYl2tLLovgf/aAAwDAQACAAMAAAAQ8C//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxAJ/8QAFREBAQAAAAAAAAAAAAAAAAAAAAH/2gAIAQIBAT8QpH//xAAaEAEBAAMBAQAAAAAAAAAAAAABEQAhMUFR/9oACAEBAAE/EO+VpsJ99x4BGpu7cRmg30vuf//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Check it out&quot;
        title=&quot;&quot;
        src=&quot;/static/library-8c5eda631c09c0985c7804e9d4cede0e-177e4.jpeg&quot;
        srcset=&quot;/static/library-8c5eda631c09c0985c7804e9d4cede0e-6c2d5.jpeg 175w,
/static/library-8c5eda631c09c0985c7804e9d4cede0e-3d17c.jpeg 350w,
/static/library-8c5eda631c09c0985c7804e9d4cede0e-177e4.jpeg 700w,
/static/library-8c5eda631c09c0985c7804e9d4cede0e-40bb7.jpeg 1050w,
/static/library-8c5eda631c09c0985c7804e9d4cede0e-562d0.jpeg 1400w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/seperot/SimpleReusableDialog&quot;&gt;https://github.com/seperot/SimpleReusableDialog&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Setting up a Golden Path Document]]></title><description><![CDATA[A golden path document is the best way for new starters to come in strong and help those that come after them
]]></description><link>https://github.com/seperot/sepe-devblog/golden-path-document/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/golden-path-document/</guid><pubDate>Tue, 04 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A golden path document is the best way for new starters to come in strong and help those that come after them&lt;/p&gt;
&lt;!-- end --&gt;
&lt;h3&gt;What is a golden path document&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/goldenpath.jpeg&quot; alt=&quot;Golden Path&quot;&gt;&lt;/p&gt;
&lt;p&gt;A Golden path, when talking about software is the ideal route for a user to take. In the case of a shopping app, the golden path would see the user log in, find the item they are looking for easily, go to checkout, and pay all without any issue. A golden path document in this instance is showing the ideal path for a new member of the team to get up and running with the project quickly and easily. The document shouldn’t assume much as you don’t know where someone’s starting point or gaps in their knowledge will be. For someone, it may be they have never used the CI software your team uses but for someone else it could be they have no idea what to install as their previous company had strict installation rules.&lt;/p&gt;
&lt;h3&gt;How to structure it&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/structure.jpeg&quot; alt=&quot;Structure&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Start with the basics&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first thing is for them to check they have all the hardware they will need. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is their workstation the minimum spec? &lt;/li&gt;
&lt;li&gt;Do they need peripherals or cables? &lt;/li&gt;
&lt;li&gt;Do they need access to devices they need for debugging? &lt;/li&gt;
&lt;li&gt;Are there any accessability items they don’t know they can ask for?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next is software, same deal as hardware but this time what they need to get the job done. This also included pulling down the codebase they will be working on.&lt;/p&gt;
&lt;p&gt;Finally make sure all their access and permissions needed are sorted as well, any cloud platforms or tools like Jira they need logins for are ready for them to use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Code rules and opinions&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With the basics out the way, the next thing to do is write as much as you can down on the coding standards your team uses.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Camel case, Delimiter space, Hungarian notation or whatever naming style the team uses&lt;/li&gt;
&lt;li&gt;If you use multiple languages then anything unique to each of them&lt;/li&gt;
&lt;li&gt;Where you set strings, colours, and sizes&lt;/li&gt;
&lt;li&gt;2 spaces, 4 spaces or tabs&lt;/li&gt;
&lt;li&gt;Class and folder structure&lt;/li&gt;
&lt;li&gt;If you do TDD, or what the test coverage percentage needs to be&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;How the work process… works&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So now they have the tools to do the job and know what the standards look like, it’s time for them to take on a task. Breaking down this section based on them working through the workflow.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where to go to get work&lt;/li&gt;
&lt;li&gt;What system does your teamwork with like Kanban or Scrum.&lt;/li&gt;
&lt;li&gt;How they let everyone know they are taking on the task and when it’s ready&lt;/li&gt;
&lt;li&gt;Is there a QA process they need to take the work through&lt;/li&gt;
&lt;li&gt;What CI/CD pipeline process are working in the background at the various stages.&lt;/li&gt;
&lt;li&gt;Do the git branches have a lifespan before being deleted?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You also need to make clear when their work is ready for review&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When do they open a pull request&lt;/li&gt;
&lt;li&gt;Is there a limit to the size of the pull request&lt;/li&gt;
&lt;li&gt;How commenting on the PR works and when to take it to a conversation&lt;/li&gt;
&lt;li&gt;How to write good descriptions on a PR&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Getting their work live&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Finally once they have done all the work and been through the process, they will no doubt want to see their work live. Here you just need to explain the release process and if they need to make release notes. &lt;/p&gt;
&lt;h3&gt;Make it living&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/living.jpeg&quot; alt=&quot;Living&quot;&gt;&lt;/p&gt;
&lt;p&gt;An important part of a golden path document being effective is to keep it as a living document. As the new member of the team works through the document encourage them to edit anything they find as a pain point, something outdated that you had to explain to them or just something they feel like needs more detail. This helps keep the document up to date and makes the new starter have a hand making the process better for the next new starter. Keep the document in an area where you can rollback if something is changed incorrectly but everyone in the team has access, Confluence being a good example. &lt;/p&gt;</content:encoded></item><item><title><![CDATA[Golang automated pipeline]]></title><description><![CDATA[A guide to building a simple pipeline from PR to deploy with a scalable solution. (TurtleWare 2.0 pt3)
]]></description><link>https://github.com/seperot/sepe-devblog/open-source-pipeline/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/open-source-pipeline/</guid><pubDate>Mon, 09 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A guide to building a simple pipeline from PR to deploy with a scalable solution. (TurtleWare 2.0 pt3)&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;This is part 3 of a 3 part blog post about making v2 of turtle wear watch face. &lt;a href=&quot;https://ijh.dev/middlewear-go&quot;&gt;You can check out part 1 here&lt;/a&gt; and &lt;a href=&quot;https://ijh.dev/making-wear-os&quot;&gt;you can check out part 2 here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This post will also be covering everything from PR to releasing on a Kubernetes cluster. We will be skipping some of the basics of CI/CD pipelines and what they are, if you are not sure what that is &lt;a href=&quot;https://ijh.dev/mobile-ci-cd/&quot;&gt;check out this blog post on a mobile CI/CD here&lt;/a&gt;. &lt;/p&gt;
&lt;h3&gt;Git, pull requests and pipeline starting point&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/github.jpeg&quot; alt=&quot;GitHub&quot;&gt;&lt;/p&gt;
&lt;p&gt;A theme with this guide will be getting free stuff wherever able, the cool thing about open source projects is there is a lot of options. For a good solid Git repo with a good pull request system and plugins to use all these free tools, &lt;a href=&quot;https://github.com&quot;&gt;you can’t go wrong with GitHub&lt;/a&gt;. &lt;a href=&quot;https://trunkbaseddevelopment.com&quot;&gt;Trunk based development&lt;/a&gt; for git is still the preferred method for having something easy to admin but fast to release. For setting that kind of environment up on GitHub as well as protecting your open source project, make sure you have the following settings:&lt;/p&gt;
&lt;h4&gt;&lt;em&gt;In options:&lt;/em&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;In Data services, turn on “Security Alerts” to find out if any third-party libraries have a vulnerability and need you to update&lt;/li&gt;
&lt;li&gt;In Merge button, turn “Allow squash merging” and “Automatically delete head branches” on and the others off. This will keep your Trunk/Master branch readable on what went out and when. This goes hand in hand with good pull request descriptions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;&lt;em&gt;In branches:&lt;/em&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Set your default branch to your Trunk/Master branch&lt;/li&gt;
&lt;li&gt;Create a new branch protection rule for the Trunk/Master&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;&lt;em&gt;Inside the branch protection rule:&lt;/em&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Turn on “Require pull request reviews before merging” with at least 1 approval needed. This will stop anyone from pushing code right into the master branch and instead need a code review before merging.&lt;/li&gt;
&lt;li&gt;With “Require pull request reviews before merging” the option to turn on “Dismiss stale pull request approval when new commits are pushed” appears. This will help if someone has already had their code reviewed but then adds the dreaded “just one more quick fix” to their PR from being able to merge the code without at least 1 reviewer re-checking the PR. &lt;/li&gt;
&lt;li&gt;Turn on “Require status checks to pass before merging”. This won’t have much of an effect yet but as we get further down we can come back to this rule and set up plugins to prevent PR’s from being merged before checks are complete.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are other things you can set up here for to depending on what you need such as Wikis and Social Preview images but you can play around there for whatever suits your project best. &lt;/p&gt;
&lt;h3&gt;SonarCloud&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/sonarcloud.jpeg&quot; alt=&quot;SonarCloud&quot;&gt;&lt;/p&gt;
&lt;p&gt;One important thing to add to your pipeline is static code analysis. This doesn’t replace the need for other engineers reviewing code, rather it acts as an extra reviewer who is obsessed with code rules. The tool we are going to use in this setup is &lt;a href=&quot;https://sonarcloud.io/&quot;&gt;SonarCloud&lt;/a&gt; as it’s free for open-source code and easily plugs into GitHub. You can sign up for a SonarCloud account by using your GitHub auth, this way you can point it to your project so it can analyze it in just a couple of steps. You can set the code analyser to fire every time you trigger a build and it will comment on the pull request. It will cover a range of rules from code duplication, vulnerabilities including &lt;a href=&quot;https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project&quot;&gt;OWASP Top 10&lt;/a&gt; bugs, and Unit test coverage. Going back to GitHub as well, we can now set the “Require status checks to pass before merging” to require SonarCloud to pass before allowing merging.&lt;/p&gt;
&lt;h3&gt;DockerHub&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/docker.jpeg&quot; alt=&quot;DockerHub&quot;&gt;&lt;/p&gt;
&lt;p&gt;So while there are more feature-rich automated building tools available, if your plan is to containerise your program &lt;a href=&quot;https://www.docker.com/&quot;&gt;using Docker&lt;/a&gt; then there is a simple automated builder in Docker &lt;a href=&quot;https://hub.docker.com/&quot;&gt;called DockerHub&lt;/a&gt;. Before you sign up for that it’s better if you set up a working docker file for your Go program.&lt;/p&gt;
&lt;p&gt;So for Turtle Wear the docker image is quite simple and looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;FROM golang&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;alpine AS builder

RUN apk update &amp;amp;&amp;amp; apk add &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;no&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cache git ca&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;certificates 
&amp;amp;&amp;amp; update&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ca&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;certificates
RUN adduser &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;D &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;g &apos;&apos; appuser

WORKDIR $GOPATH/src/turtle&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;wear&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;api

COPY . .

RUN go mod download &amp;amp;&amp;amp; go mod verify
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go test &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cover ./&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 
go build &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;o /go/bin/turtle&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;wear&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;api ./cmd/turtle&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;functions/main.go

FROM scratch
COPY &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;from=builder /etc/ssl/certs /etc/ssl/certs
COPY &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;from=builder /etc/passwd /etc/passwd
COPY &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;from=builder /go/bin/turtle&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;wear&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;api /go/bin/turtle&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;wear&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;api
USER appuser
ENTRYPOINT &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/go/bin/turtle-wear-api&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Credit to &lt;a href=&quot;https://github.com/ndane&quot;&gt;Nathan Dane&lt;/a&gt; for showing me his implementation and working with me on modifying it for the project needs.&lt;/p&gt;
&lt;p&gt;With Golang you can get your unit tests running right inside the docker image as well. The line “RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go test -cover ./…” is what runs all the unit tests in the app and will fail the build if one doesn’t pass. Also the line “RUN apk update &amp;#x26;&amp;#x26; apk add —no-cache git ca-certificates &amp;#x26;&amp;#x26; update-ca-certificates” was needed otherwise the container couldn’t make external requests.&lt;/p&gt;
&lt;p&gt;Once you have a docker file setup and working, &lt;a href=&quot;https://hub.docker.com/&quot;&gt;Login to DockerHub&lt;/a&gt; and click “Create repository”. Add a name and a description, select public and then connect up your GitHub account and point it to your Golang build with the docker file. From here DockerHub will take the first build and if it’s all working put the image up ready from use. In the same way we did with SonarCloud we can go back to GitHub and set the “Require status checks to pass before merging” to require DockerHub to pass before allowing merging.&lt;/p&gt;
&lt;h3&gt;Kubernetes in Google Cloud Platform&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/kubernetes.jpeg&quot; alt=&quot;kubernetes&quot;&gt;&lt;/p&gt;
&lt;p&gt;“Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications. It groups containers that make up an application into logical units for easy management and discovery.” Is the business talk way of explaining what they are. In very layman’s terms, it’s a way to deploy containers that are self-sustaining, update automatically, and can grow and shrink as demand requires.&lt;/p&gt;
&lt;p&gt;There are a few different services to get Kubernetes up and running easily enough, AWS has EKS, Azure has AKS, IBM has… IBM Kubernetes, and Google have GKE. GKE is what we will be using for this guide as it’s the right balance between giving you control and doing things for you. Plus they give you $300 free credit to use in your first year so you can learn the system safely.&lt;/p&gt;
&lt;p&gt;To start with, create a new project on the Google Cloud Platform. To do this, first go to the sidebar, click “IAM &amp;#x26; admin” then “Manage resources”. On this page click Create Project, then in the new project window that appears, enter a project name and other applicable details, then click create.&lt;/p&gt;
&lt;p&gt;With that setup, you can now navigate to the Kubernetes Engine in the same left sidebar and click on clusters. Click on create cluster and you should be brought to a new screen with a lot of options. First pay attention to the very left-hand side, here are some templates with descriptions to make choosing the right cluster easier. For the purpose of this guide, we will be using “Your first cluster” as it’s the cheapest and what we are looking for. If you are building something that has higher requirements then feel free to select a different template.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/templates.jpeg&quot; alt=&quot;templates&quot;&gt;&lt;/p&gt;
&lt;p&gt;After selecting your template you can move over to the next section to the right. Starting from the top we have Name which is simple enough. deally, remember to name it with “cluster” at the end as you will be naming a few things when setting this up and it’s good to keep a naming convention for clarity later on. Next is Location type, choose zonal unless you really need always-on coverage assurance. Zone or Region just allows you to select where your cluster is hosted. Everything else here is fine as standard for whatever template you’ve chosen, click create when you’re ready. Once that’s done you will be back on the Clusters screen, give your new cluster a few minutes to finish setting up before continuing on.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/clusternotready.jpeg&quot; alt=&quot;cluster not ready&quot;&gt;&lt;/p&gt;
&lt;h5&gt;&lt;em&gt;Not ready yet&lt;/em&gt;&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;./images/clusterready.jpeg&quot; alt=&quot;cluster ready&quot;&gt;&lt;/p&gt;
&lt;h5&gt;&lt;em&gt;Green tick is good to go&lt;/em&gt;&lt;/h5&gt;
&lt;p&gt;Next, we need to deploy our container, so click Deploy at the top of the clusters screen. The screen shows Container first, we can take the image path from our DockerHub build, press done then continue. It then moves on to Configuration, here you can name the Application and Namespace, again name them “app” and “namespace” at the end to keep that clarity. At the bottom, it allows you to select your newly created cluster then click Deploy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/deployingwait.jpeg&quot; alt=&quot;deploying wait times&quot;&gt;&lt;/p&gt;
&lt;h5&gt;&lt;em&gt;More loading times&lt;/em&gt;&lt;/h5&gt;
&lt;p&gt;This will bring you inside the deployment details page, the next thing to do here is on a prompt near the top of the page which mentions exposing the service. If you click that you will go to another screen where you can map the port for inside the container and the outside world. I highly recommend keeping the port to 80 but using whatever port you have set up inside your code for the target port. Click Expose here and it will start opening up the port.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/exposeip.jpeg&quot; alt=&quot;Expose IP&quot;&gt;
With that all done you now have an External endpoint IP address. Use this to run tests on your code to make sure in the container is working correctly before moving on.&lt;/p&gt;
&lt;h3&gt;DNS Setup&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/dns.jpeg&quot; alt=&quot;DNS setup&quot;&gt;&lt;/p&gt;
&lt;p&gt;With the IP address from GKE all setup the pipeline is complete, but there is some quality of life things we can do before we call it a full success. Firstly having a raw IP address isn’t the nicest looking way to connect up an endpoint, you can very easily use a web domain you have to be your new endpoint name. If you go to your DNS records for that domain and do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Record type&lt;/em&gt; : A&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Name&lt;/em&gt; : Your domain name but with “api. before it&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Value&lt;/em&gt; : This is your GKE ip address&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you give it 5-10 mins for the DNS to propagate then give the same tests you were running on the IP address to the name in your DNS record you will find that everything works just the same but looks a lot nicer now.&lt;/p&gt;
&lt;h3&gt;Swagger docs&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/swagger.jpeg&quot; alt=&quot;Swagger&quot;&gt;&lt;/p&gt;
&lt;p&gt;Allowing anyone to understand how your endpoints work and allow them to test them out is a great way to help people on board and save you a lot of time explaining how it works. &lt;a href=&quot;https://app.swaggerhub.com/&quot;&gt;Swagger Hub&lt;/a&gt; offers a pretty good free hosted setup so long as again it’s open source. The site has an editor and a coding mode so you can set up the page, I would recommend looking at one of their demo pages to understand the layout properly. &lt;a href=&quot;https://app.swaggerhub.com/apis-docs/ijhdev/turtle-wear-api/1.0.0&quot;&gt;You can check out the one I made for Turtle Wear API here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Wrapping up&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/wrapup.jpeg&quot; alt=&quot;The End&quot;&gt;&lt;/p&gt;
&lt;p&gt;There you go, you now have a pipeline so when you deploy new code to your Golang project it will check for code issues, run the unit tests, build into a container and then deploy to a scalable solution. This also wraps up the Turtle Wear trilogy of blog posts about my journey of doing a v2 of the app, hopefully it’s of some help to you!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/seperot/turtle-wear-api&quot;&gt;Turtle Wear API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/seperot/turtle-wear&quot;&gt;Turtle WearOS App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://app.swaggerhub.com/apis-docs/ijhdev/turtle-wear-api/1.0.0&quot;&gt;Swagger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/ijhdev/turtle-wear-api&quot;&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Building a WearOS Watch face]]></title><description><![CDATA[Making a wearOS watch face and avoiding some of the faff. (TurtleWare 2.0 pt2)
]]></description><link>https://github.com/seperot/sepe-devblog/making-wear-os/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/making-wear-os/</guid><pubDate>Mon, 18 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Making a wearOS watch face and avoiding some of the faff. (TurtleWare 2.0 pt2)&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;This is part 2 of a 3 part blog post about making v2 of turtle wear watch face. &lt;a href=&quot;https://ijh.dev/middlewear-go&quot;&gt;You can check out part 1 here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Starting the project off&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/trtlwear.jpeg&quot; alt=&quot;Watch&quot;&gt;&lt;/p&gt;
&lt;p&gt;Starting off, WearOS is the same as Android for 99% of things. Meaning you can write the app Kotlin (or Java if you love some boilerplate) and use all the same Android libraries. So for folder structures and initializing the project can be done in Android Studio just like you would a phone app. For Turtle Wear 2.0, there are two important entry points. The CanvasWatchFaceService and the MainActivity.&lt;/p&gt;
&lt;h4&gt;CanvasWatchFaceService&lt;/h4&gt;
&lt;p&gt;The CanvasWatchFaceService is the place where you run all the watch face visuals. You set an inner class that contains a CanvasWatchFaceService.Engine() which deals with refresh speeds, ambient and active visuals, and custom tap commands. The CanvasWatchFaceService is pretty standard and should look something like the below:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TurtleFace&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CanvasWatchFaceService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

  var specW&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
  var specH&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; val displaySize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  companion object &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; val INTERACTIVE_UPDATE_RATE_MS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; val MSG_UPDATE_TIME &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  override fun &lt;span class=&quot;token function&quot;&gt;onCreateEngine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Engine &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EngineHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reference&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Engine&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; val mWeakReference&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WeakReference&lt;span class=&quot;token generics function&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Engine&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;WeakReference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reference&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    override fun &lt;span class=&quot;token function&quot;&gt;handleMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      val engine &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mWeakReference&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;engine &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; null&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        when &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;what&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          MSG_UPDATE_TIME &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; engine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handleUpdateTimeMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The inner class Engine has a lot more moving parts, but some bits to consider are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Standard Android lifecycle events&lt;/strong&gt; - So you can deal with the watch face starting, going into the background and being stopped when changed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;onAmbientModeChanged&lt;/strong&gt; - This is where you can control what you show and hide when the user is actively looking at the watch or not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;onTapCommand&lt;/strong&gt; - This controls any custom action you want your watch face to perform, like a double tap to refresh the view&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;onDraw&lt;/strong&gt; - The main visuals for your view go here rather than OnCreate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;onApplyWindowInsets&lt;/strong&gt; - This helps with different screen sizes (Round, flat tire, square) so your watch face fits all of them&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;MainActivity&lt;/h4&gt;
&lt;p&gt;This should be familiar to anyone who has made an Android app, this is where your android.intent.action.MAIN and android.intent.category.LAUNCHER is set to on the manifest. In this case, it will be the launch point for the settings app.&lt;/p&gt;
&lt;h3&gt;Setting up the simulator&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/simulator.jpeg&quot; alt=&quot;Simulator&quot;&gt;&lt;/p&gt;
&lt;p&gt;There are a few catches with working on wearOS apps when it comes to debugging, especially with a watch face. The first being the standard configuration. This will always finish building with the settings app open, which if you are working on watch face changes is a bit annoying. It’s better to have this as the configuration settings.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/wearconfig.jpeg&quot; alt=&quot;configSettings&quot;&gt;&lt;/p&gt;
&lt;p&gt;This way the app will build but nothing will change on the device or simulator. Next is setting up devices to debug the build on. There are two options here:&lt;/p&gt;
&lt;h4&gt;Simulator&lt;/h4&gt;
&lt;p&gt;Open up the Android Virtual Device Manager for this and select “create a new virtual device” at the bottom. Choose WearOS as your hardware, Select Pie as the system image but don’t select the Chinese version unless you specifically want that, everything else is fine on default so you can click finish.&lt;/p&gt;
&lt;h4&gt;Device&lt;/h4&gt;
&lt;p&gt;For this method, you should have an Android phone or tablet connected to your workstation already. Make sure it’s got developer settings enabled and has allowed your workstation access. You also need the Wear OS app installed on that device and Bluetooth connected to the wearable.&lt;/p&gt;
&lt;p&gt;Open the WearOS app on the Android device and scroll to the bottom where advanced settings are, at the bottom here is “Debugging over Bluetooth”. Turn that on and you should get “Host: Disconnected Target: Connected”. Once that’s done go into terminal and enter the following commands:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;adb forward tcp:4444 localabstract:/adb-hub
adb connect 127.0.0.1:4444&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now your wearable should be fully connected and ready to debug&lt;/p&gt;
&lt;h3&gt;Manifest settings&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/manifest.jpeg&quot; alt=&quot;Manifest&quot;&gt;&lt;/p&gt;
&lt;p&gt;When making a wearable app and specifically a watch face, there are some permissions you need to have set in the manifest. They are:&lt;/p&gt;
&lt;h5&gt;Required for any of this to work on a wearable.&lt;/h5&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;uses-feature&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.hardware.type.watch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h5&gt;Required to act as a custom watch face.&lt;/h5&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;uses-permission&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.permission.WAKE_LOCK&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h5&gt;Required for complications to receive complication data and open the provider chooser.&lt;/h5&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;uses-permission&lt;/span&gt; 
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Another set most apps will likely need is internet access, you will likely also need Bluetooth access as part of this if you want to communicate with the connected device.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;uses-permission&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.permission.INTERNET&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;uses-permission&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.permission.BLUETOOTH&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;uses-permission&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.permission.ACCESS_NETWORK_STATE&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;uses-permission&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.permission.FOREGROUND_SERVICE&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Slightly less important is location services, Turtle wear needs this information to get weather data for your location.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;uses-permission&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.permission.ACCESS_COARSE_LOCATION&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;uses-permission&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.permission.ACCESS_FINE_LOCATION&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the CanvasWatchFaceService, it need to be declared specifically as a watch face. So we start by flagging it as a service and giving it the Bind Wallpaper permission.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;service&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.watchface&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@string/my_name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;permission&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.permission.BIND_WALLPAPER&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next, we set up the metadata for the watch face and what the watch face example screen looks like on a square and circular screen type. The examples are used when the user selects the watch face from the selection screen.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta-data&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.service.wallpaper&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;resource&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@xml/watch_face&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta-data&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;com.google.android.wearable.watchface.preview&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;resource&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@drawable/preview_digital&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta-data&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;com.google.android.wearable.watchface.preview_circular&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;resource&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@drawable/preview_digital_circular&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The last part is identifying it as a watch face.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;intent-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;action&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.service.wallpaper.WallpaperService&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;category&lt;/span&gt; 
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;com.google.android.wearable.watchface.category.WATCH_FACE&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;intent-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The main activity manifest setup is thankfully very default android.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;activity&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@string/app_name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.MainActivity&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;intent-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;action&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.intent.action.MAIN&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;action&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.intent.action.VIEW&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;category&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;android.intent.category.LAUNCHER&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;intent-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;activity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Important&lt;/h2&gt;
&lt;p&gt;Without this inside the application area of the manifest, you will not be able to make any API calls out from the phone on newer devices. The error messaging on this is vague so it took ages for me to find this.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;android:usesCleartextTraffic=&quot;true”&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Drawing the watch face&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/watchface.jpeg&quot; alt=&quot;Watch&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you check most places for how to draw the watch face, you will generally see very large OnDraw functions that require canvas drawing. I instead prefer to use xml layouts in the same way you would with an android application. This is a little less efficient but a lot more readable code in my opinion. To set this up first have a lateinit View outside the engine. Then on the OnCreate you inflate it like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;val inflater&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LayoutInflater &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; 
&lt;span class=&quot;token function&quot;&gt;getSystemService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LAYOUT_INFLATER_SERVICE&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; as LayoutInflater
watchLayout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; inflater&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;inflate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;R&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;watchface&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; null&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With the view now inflated, you can set it up with the screen dimensions on the OnDraw like below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;watchLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;specW&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; specH&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
watchLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; watchLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;measuredWidth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; watchLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;measuredHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mXOffset&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mYOffset &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
watchLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;canvas&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With that done, you now call any element you need to update wherever you need. For example, this is called on the OnDraw elsewhere for showing the current time.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; fun &lt;span class=&quot;token function&quot;&gt;setTimeandDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      val now &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      mCalendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timeInMillis &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; now
      val date&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TextView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; watchLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findViewById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;R&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date_number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%02d/%02d&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
      mCalendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Calendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DAY_OF_MONTH&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
      mCalendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Calendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MONTH&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      val hour&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TextView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; watchLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findViewById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;R&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hourtime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      hour&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%02d&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mCalendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Calendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;HOUR_OF_DAY&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      val min&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TextView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; watchLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findViewById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;R&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mintime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      min&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%02d&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mCalendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Calendar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MINUTE&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Finishing it up and launching&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/launch.jpeg&quot; alt=&quot;Launch&quot;&gt;&lt;/p&gt;
&lt;p&gt;That’s mostly in terms of the tricky bits of getting the Turtle wear v2 up and running. The Google play store takes submissions to it just the same as it would an Android app.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/seperot/turtle-wear&quot;&gt;You can check the github repo here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=uk.co.ijhdev.trtlware&amp;#x26;hl=en&quot;&gt;You can download the watch face here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If your interested in taking your build further with a CI/CD pipeline, &lt;a href=&quot;https://ijh.dev/mobile-ci-cd&quot;&gt;I’ve got a blog post all about how to set one up here&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Making a Smart Mirror]]></title><description><![CDATA[A smart mirror build with spotify added in
]]></description><link>https://github.com/seperot/sepe-devblog/making-magic-mirror/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/making-magic-mirror/</guid><pubDate>Thu, 10 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A smart mirror build with spotify added in&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;So for a challenge, I wanted to try and make a Smart Mirror. I wanted to make it functional as a mirror but also cool and with handy extras like USB charge ports and music.&lt;/p&gt;
&lt;h3&gt;Things needed&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/todolist.jpeg&quot; alt=&quot;To Do List&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;A screen:&lt;/em&gt;&lt;/strong&gt; I pulled the one I am working with from an old Samsung laptop. You can find controller units on Amazon or Ebay that turn it into standard monitor with ports and controls. Or you can take out the screen from any monitor really.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEDAgT/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAABsp4TqGM//8QAGBABAQEBAQAAAAAAAAAAAAAAAgEAERL/2gAIAQEAAQUCMm7zeSsqokUiJ5H/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAEBAQADAAAAAAAAAAAAAAABAAIQETH/2gAIAQEABj8C47nOZ8Fgv//EABwQAQACAgMBAAAAAAAAAAAAAAEAESExQVFhgf/aAAgBAQABPyFg2XmBoP16lTRvkg4GHLKQNhPMyf/aAAwDAQACAAMAAAAQ+/8A/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qh//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EBn/xAAbEAEBAQEBAAMAAAAAAAAAAAABEQAhQTFRgf/aAAgBAQABPxB0oGd1fEKM8Ae4GKI4lHVYlUndHYYL8/uWIcxn37v/2Q==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Screen&quot;
        title=&quot;&quot;
        src=&quot;/static/screen-f100016eb00fa8b98e3be0aa62043dd6-177e4.jpg&quot;
        srcset=&quot;/static/screen-f100016eb00fa8b98e3be0aa62043dd6-6c2d5.jpg 175w,
/static/screen-f100016eb00fa8b98e3be0aa62043dd6-3d17c.jpg 350w,
/static/screen-f100016eb00fa8b98e3be0aa62043dd6-177e4.jpg 700w,
/static/screen-f100016eb00fa8b98e3be0aa62043dd6-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;A raspberry Pi:&lt;/em&gt;&lt;/strong&gt; Pretty standard, the one in this build is a Raspberry Pi 3 Model B+ but I think you can get away with older models, just don’t select a Raspberry Pi Zero as it’s not got enough power to run everything.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAACBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAFQOsOpVak//8QAGhAAAgIDAAAAAAAAAAAAAAAAAQIAAwQREv/aAAgBAQABBQK7YrS0hoy9AYqAz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABgQAAMBAQAAAAAAAAAAAAAAAAABERAC/9oACAEBAAY/Am0K5C3rP//EABkQAQADAQEAAAAAAAAAAAAAAAEAETEQQf/aAAgBAQABPyFawTeItSoSr2HLFN7z/9oADAMBAAIAAwAAABBE3//EABYRAQEBAAAAAAAAAAAAAAAAAAEQIf/aAAgBAwEBPxAcn//EABcRAQEBAQAAAAAAAAAAAAAAAAEAESH/2gAIAQIBAT8QRXll/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARIZExYf/aAAgBAQABPxDNILOxYSYw+tR1ECQNWVnZa6ARzZvlRC5//9k=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;raspberry pi&quot;
        title=&quot;&quot;
        src=&quot;/static/raspberrypi-187e760485f3d8ad4d2a67f95a24d343-177e4.jpg&quot;
        srcset=&quot;/static/raspberrypi-187e760485f3d8ad4d2a67f95a24d343-6c2d5.jpg 175w,
/static/raspberrypi-187e760485f3d8ad4d2a67f95a24d343-3d17c.jpg 350w,
/static/raspberrypi-187e760485f3d8ad4d2a67f95a24d343-177e4.jpg 700w,
/static/raspberrypi-187e760485f3d8ad4d2a67f95a24d343-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;A Speaker:&lt;/em&gt;&lt;/strong&gt; If your going to play music from it you need to have at least a passable speaker connected.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFA//EABUBAQEAAAAAAAAAAAAAAAAAAAAC/9oADAMBAAIQAxAAAAFqTcmVLpmH/8QAGxAAAgIDAQAAAAAAAAAAAAAAAQMAAgQREiH/2gAIAQEAAQUCPisd1u63BBECNM43P//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABsQAAIBBQAAAAAAAAAAAAAAAAABEBEhIjGB/9oACAEBAAY/AuGTs5ojR//EABoQAQADAQEBAAAAAAAAAAAAAAEAEUEhcYH/2gAIAQEAAT8hNgZBCcJyWJQ+zhU4iUbF5HrP/9oADAMBAAIAAwAAABDX7//EABYRAQEBAAAAAAAAAAAAAAAAAAABIf/aAAgBAwEBPxCVj//EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAgEBPxBLG//EAB0QAQADAAEFAAAAAAAAAAAAAAEAESExQVFhgZH/2gAIAQEAAT8QYpr16IDhtOxCyow0+D0Q24xEYOwGN0y6+VsausWcy744J//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Speaker&quot;
        title=&quot;&quot;
        src=&quot;/static/speaker-c290aa500f2286c3451ab147937dc1bc-177e4.jpg&quot;
        srcset=&quot;/static/speaker-c290aa500f2286c3451ab147937dc1bc-6c2d5.jpg 175w,
/static/speaker-c290aa500f2286c3451ab147937dc1bc-3d17c.jpg 350w,
/static/speaker-c290aa500f2286c3451ab147937dc1bc-177e4.jpg 700w,
/static/speaker-c290aa500f2286c3451ab147937dc1bc-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;A USB Hub:&lt;/em&gt;&lt;/strong&gt; This helps power your Pi and Speakers so you won’t need a million cables coming out the back of the mirror.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 133.29999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAEEBQP/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAL/2gAMAwEAAhADEAAAAbmXxJprDWaMac0ST//EAB4QAAICAQUBAAAAAAAAAAAAAAECAwQSABEUICEi/9oACAEBAAEFAmbYctshLGwtH7qeyhFGpq2bxVSknT//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPwEp/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8BIf/EAB4QAAIBAwUAAAAAAAAAAAAAAAABEQIhMRASIEFR/9oACAEBAAY/Ai1NvCZWkPtGCUxVbscf/8QAGxAAAwEBAAMAAAAAAAAAAAAAAAERMZEQQVH/2gAIAQEAAT8hoOldHRin1F4pGsvYYC8Gy6XxklBB6Lx//9oADAMBAAIAAwAAABCjLgD/xAAXEQADAQAAAAAAAAAAAAAAAAAAAREQ/9oACAEDAQE/EGoilZ//xAAXEQADAQAAAAAAAAAAAAAAAAAAAREQ/9oACAECAQE/EE6yEZ//xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMRBRYcH/2gAIAQEAAT8QACibClEni0PjK0lmmU+RRCAXjHURzNaafsBQttvsy7BGhxiZQVoHcrw6Tnx//9k=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;usb hub&quot;
        title=&quot;&quot;
        src=&quot;/static/usbhub-9c418d5f49d801efa2185ae43032912e-177e4.jpg&quot;
        srcset=&quot;/static/usbhub-9c418d5f49d801efa2185ae43032912e-6c2d5.jpg 175w,
/static/usbhub-9c418d5f49d801efa2185ae43032912e-3d17c.jpg 350w,
/static/usbhub-9c418d5f49d801efa2185ae43032912e-177e4.jpg 700w,
/static/usbhub-9c418d5f49d801efa2185ae43032912e-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;A Mirror:&lt;/em&gt;&lt;/strong&gt; The mirror I’m working with is a dressing table mirror but you can use just about anything.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 133.29999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAMBAgUE/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAGyoQjzhDRGzjeeaRZ//8QAHRAAAgIBBQAAAAAAAAAAAAAAAQIAAxIQERMiI//aAAgBAQABBQJ0axQLFa1zkXPALSXtb0QdSssAy02n/8QAFhEAAwAAAAAAAAAAAAAAAAAAABAR/9oACAEDAQE/AXD/xAAWEQADAAAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8BdP/EABwQAAEEAwEAAAAAAAAAAAAAAAABAhEhECAyMf/aAAgBAQAGPwKox0o0akr6OIOStP/EABkQAQADAQEAAAAAAAAAAAAAAAEAETEhQf/aAAgBAQABPyEA2VcXdQUxgOcO9iWGrbahFY1bssqfZ4zr5CPSjs2F3BhKOhP/2gAMAwEAAgADAAAAEKz5Av/EABYRAAMAAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxCsf//EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAgEBPxBILYzf/8QAHhABAQADAAIDAQAAAAAAAAAAAREAIUFRkTFxgcH/2gAIAQEAAT8QVxH8tWM/mLMkCCenJQvARuvDF7rYZqvf3JPcqv0mMh5Z3ASJEJqFwwZIgAu/eBsAKqdriQLrxiV24qqnymf/2Q==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Mirror&quot;
        title=&quot;&quot;
        src=&quot;/static/mirrorframe-9ddec5cd67e7fb732f1b9e5c29bf329e-177e4.jpg&quot;
        srcset=&quot;/static/mirrorframe-9ddec5cd67e7fb732f1b9e5c29bf329e-6c2d5.jpg 175w,
/static/mirrorframe-9ddec5cd67e7fb732f1b9e5c29bf329e-3d17c.jpg 350w,
/static/mirrorframe-9ddec5cd67e7fb732f1b9e5c29bf329e-177e4.jpg 700w,
/static/mirrorframe-9ddec5cd67e7fb732f1b9e5c29bf329e-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Tools and Materials to put it together:&lt;/em&gt;&lt;/strong&gt; For this build I used wood to build a backing area where I could place everything nicely but you can do whatever is best for your setup. For the front I got a 2 way plastic mirror on Amazon that was close enough to the right size it just needed a bit of a trim on the edges&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 133.29999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDBAH/xAAWAQEBAQAAAAAAAAAAAAAAAAADAgH/2gAMAwEAAhADEAAAAacJTu4yCQkpOSXEEP8A/8QAGxAAAgIDAQAAAAAAAAAAAAAAAAECAxEhMSL/2gAIAQEAAQUCa814SyNieyctx6X8iNn/xAAVEQEBAAAAAAAAAAAAAAAAAAABIP/aAAgBAwEBPwFj/8QAFREBAQAAAAAAAAAAAAAAAAAAAiD/2gAIAQIBAT8BMf/EABkQAAMAAwAAAAAAAAAAAAAAAAABEBEhYf/aAAgBAQAGPwLpuuYqFP/EABsQAQACAwEBAAAAAAAAAAAAAAEAERAhQVEx/9oACAEBAAE/IbVqraYVPqAryMYy5RYRQX1Y37dxINE//9oADAMBAAIAAwAAABAc7X//xAAXEQADAQAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/EFwg9qIf/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERIf/aAAgBAgEBPxBtRRZGU//EAB0QAQEAAgIDAQAAAAAAAAAAAAERACExQVFhgXH/2gAIAQEAAT8Qk9TklD5iqTiT8x5a114xhtlwtkWkuDedvtx5BUtHrO5q/Zi2270ZQiEPD6x1Xa5YEAOs/9k=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Materials&quot;
        title=&quot;&quot;
        src=&quot;/static/materials-5bf4e665df45d713a6ffa7c7c4671e0f-177e4.jpg&quot;
        srcset=&quot;/static/materials-5bf4e665df45d713a6ffa7c7c4671e0f-6c2d5.jpg 175w,
/static/materials-5bf4e665df45d713a6ffa7c7c4671e0f-3d17c.jpg 350w,
/static/materials-5bf4e665df45d713a6ffa7c7c4671e0f-177e4.jpg 700w,
/static/materials-5bf4e665df45d713a6ffa7c7c4671e0f-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Cables to connect everything together:&lt;/em&gt;&lt;/strong&gt; HDMI Cable, USB to micro usb cables, male to male audio cable, power cable for the monitor all needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Setting up the Pie&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/pie.jpeg&quot; alt=&quot;Pie&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/MichMich/MagicMirror&quot;&gt;We will be using MagicMirror²&lt;/a&gt; for this build, make sure to read up on how to auto start the mirror too &lt;a href=&quot;https://github.com/MichMich/MagicMirror/wiki/Auto-Starting-MagicMirror&quot;&gt;here.&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Initially, I had planned on using Google assistant but as the build started Google deprecated the usage so instead to make sure we can have
&lt;a href=&quot;https://github.com/dtcooper/raspotify&quot;&gt;music we can use Raspotify&lt;/a&gt; for music, this had a bit of an issue with the HDMI so make sure you check out this if you have the same problem &lt;a href=&quot;https://github.com/dtcooper/raspotify/issues/174&quot;&gt;HDMI boost config&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Honestly once they are working it was smooth sailing!&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/installation.jpeg&quot; alt=&quot;Installation&quot;&gt;&lt;/p&gt;
&lt;p&gt;First I made sure the screen fit into the wood I had cut out. Made sure to use tape to seal any extra light from things like the pie showing up mirror side.

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 133.29999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEBQL/xAAWAQEBAQAAAAAAAAAAAAAAAAABAgD/2gAMAwEAAhADEAAAAV2Q5w7wwipOYXJUZxn/xAAbEAACAwEBAQAAAAAAAAAAAAABAgADERIxBP/aAAgBAQABBQKup0sbTMn0sRZ00RtRnQnoYGnkTx2Kn//EABcRAQEBAQAAAAAAAAAAAAAAAAARAQL/2gAIAQMBAT8BVztV/8QAGBEBAAMBAAAAAAAAAAAAAAAAAAECERL/2gAIAQIBAT8BcrRjJf/EAB8QAAIABQUAAAAAAAAAAAAAAAABERIhMWECEEFRcf/aAAgBAQAGPwLHYyxR8F2J4G5I+kZdKKbNEEz/xAAbEAACAwEBAQAAAAAAAAAAAAAAAREhMUFRkf/aAAgBAQABPyHp46nUM5lQqWIuxUxkjv7C13UIQ2fQsQFcxh+IeIou1ki9CUH/2gAMAwEAAgADAAAAEIAk/f/EABgRAAMBAQAAAAAAAAAAAAAAAAABESEQ/9oACAEDAQE/EMXCWkJWH//EABkRAQEAAwEAAAAAAAAAAAAAABEAATFBUf/aAAgBAgEBPxAWHtk6m1f/xAAeEAEBAAICAgMAAAAAAAAAAAABEQAhMVFBYXGRof/aAAgBAQABPxAxNgo11YakKod/LjhpXeTl6RDvLQg9vEkJ9ibw+Q2weCaxE4FsNfmUUKLNFzYLrpzzRqj7Mb01pzuvef/Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Screen front&quot;
        title=&quot;&quot;
        src=&quot;/static/screenfront-47cd9673c60b09f45e08f6bc441e9ed7-177e4.jpg&quot;
        srcset=&quot;/static/screenfront-47cd9673c60b09f45e08f6bc441e9ed7-6c2d5.jpg 175w,
/static/screenfront-47cd9673c60b09f45e08f6bc441e9ed7-3d17c.jpg 350w,
/static/screenfront-47cd9673c60b09f45e08f6bc441e9ed7-177e4.jpg 700w,
/static/screenfront-47cd9673c60b09f45e08f6bc441e9ed7-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;p&gt;Also useful for securing it in place

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 133.29999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAADAv/EABYBAQEBAAAAAAAAAAAAAAAAAAEAAv/aAAwDAQACEAMQAAAB4yQAX3FhGku1pNlF/8QAHRAAAgIBBQAAAAAAAAAAAAAAAQIAEhEDBBMjMf/aAAgBAQABBQJ24w3aiJVFCMptt9QC4QmZuBhR4ghn/8QAFxEBAQEBAAAAAAAAAAAAAAAAABEhQf/aAAgBAwEBPwHrUqP/xAAXEQEBAQEAAAAAAAAAAAAAAAAAEQFx/9oACAECAQE/AeJixX//xAAfEAEBAAECBwAAAAAAAAAAAAABAEERIQISIDFRYfD/2gAIAQEABj8C8wmMQKSPZvt7UvVycW4ZtDo//8QAHhABAAICAQUAAAAAAAAAAAAAAQARITEQQVFhcbH/2gAIAQEAAT8hI8WYWq90VBn3MqGW3ZDVdfELZjDXCTKxZ1HiVeqW2HFtz//aAAwDAQACAAMAAAAQMP2x/8QAGBEBAQEBAQAAAAAAAAAAAAAAAQARMUH/2gAIAQMBAT8QXht8tgGwL//EABgRAQEBAQEAAAAAAAAAAAAAAAERACEx/9oACAECAQE/EA5cv6akzKu//8QAHhABAAICAgMBAAAAAAAAAAAAAQARITFBUWGR8NH/2gAIAQEAAT8QeUOwLUZcKbnSyp2mrPN8RlSlay939UcnfhrsIULtalxu21Se5VDi3h8vsRUog3q43bSfsbjPTBb8T//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Screen back&quot;
        title=&quot;&quot;
        src=&quot;/static/screenback-46d340dcc77cc620d63cde38f3aaa39c-177e4.jpg&quot;
        srcset=&quot;/static/screenback-46d340dcc77cc620d63cde38f3aaa39c-6c2d5.jpg 175w,
/static/screenback-46d340dcc77cc620d63cde38f3aaa39c-3d17c.jpg 350w,
/static/screenback-46d340dcc77cc620d63cde38f3aaa39c-177e4.jpg 700w,
/static/screenback-46d340dcc77cc620d63cde38f3aaa39c-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;p&gt;Next I made sure everything was going to fit and cut holes for all the items to be accessible from the back

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 133.29999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAIEBQP/xAAWAQEBAQAAAAAAAAAAAAAAAAACAwH/2gAMAwEAAhADEAAAAYV7Oed1wHVhqckCaVP/xAAeEAACAQQDAQAAAAAAAAAAAAABAgADBBETEiExMv/aAAgBAQABBQK5ZliVzB2Lj59ceV+21E1sQqM8jsM//8QAFREBAQAAAAAAAAAAAAAAAAAAASD/2gAIAQMBAT8BWP/EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAgEBPwGRWq//xAAcEAACAgIDAAAAAAAAAAAAAAAAEQEhAhASgaH/2gAIAQEABj8CjjRc+FkCyIMcR0tNQzrX/8QAHBABAQEBAAIDAAAAAAAAAAAAAREAITFRQWFx/9oACAEBAAE/IRbPlZopV9uWLFfW6LtfGCnaKUZgBHrcOdmUACr9ZXPzSuZ4Zig5I+Mq7//aAAwDAQACAAMAAAAQ/wDRzv/EABgRAQEAAwAAAAAAAAAAAAAAAAEAESEx/9oACAEDAQE/EMDEA7Y5f//EABcRAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQIBAT8QohozGDVn/8QAHxABAAMAAQQDAAAAAAAAAAAAAQARITFBUWFxgZGh/9oACAEBAAE/EF62svCuZTFuSo50Wom4AvZywATqx7g60RsU/UwcDh8y99bX7XPxlcOEBQ8EMEOOiXymShQmv6FV7viNY9uhU//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;checking size&quot;
        title=&quot;&quot;
        src=&quot;/static/makingsureitfits-4624cfc8cbd781babcefea438cabf29f-177e4.jpg&quot;
        srcset=&quot;/static/makingsureitfits-4624cfc8cbd781babcefea438cabf29f-6c2d5.jpg 175w,
/static/makingsureitfits-4624cfc8cbd781babcefea438cabf29f-3d17c.jpg 350w,
/static/makingsureitfits-4624cfc8cbd781babcefea438cabf29f-177e4.jpg 700w,
/static/makingsureitfits-4624cfc8cbd781babcefea438cabf29f-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;p&gt;Finally I turned it all on without the new glass in to make sure it was working.

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 133.29999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAECAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABy8dMompYOToP/8QAHRAAAQQCAwAAAAAAAAAAAAAAAQACAxEQEyEiMf/aAAgBAQABBQLYZDYuXtJaPALqwU73P//EABgRAQEAAwAAAAAAAAAAAAAAAAARARIh/9oACAEDAQE/Ab1vlEf/xAAXEQEBAQEAAAAAAAAAAAAAAAAAERIh/9oACAECAQE/Ac8ZVX//xAAbEAACAQUAAAAAAAAAAAAAAAAAARECICExgf/aAAgBAQAGPwKahZG0cNkX/wD/xAAcEAEAAgIDAQAAAAAAAAAAAAABABEQITFBUYH/2gAIAQEAAT8hsKjwgb8BjLAntQa2NX0gUtP2aNoPEVncd6Ycf//aAAwDAQACAAMAAAAQNyP+/8QAGhEAAgIDAAAAAAAAAAAAAAAAAREAEDFBUf/aAAgBAwEBPxBQA7EahB4p/8QAGBEBAAMBAAAAAAAAAAAAAAAAAQAQURH/2gAIAQIBAT8QGtyA2C4g0//EABwQAQADAQADAQAAAAAAAAAAAAEAESExEFGBkf/aAAgBAQABPxBIL0AN0RHAVfoyXZuqDpmSRC8bGJBU6wyXQ4JRwho/lRUD5ajDS6I98Hs//9k=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;test run&quot;
        title=&quot;&quot;
        src=&quot;/static/testrunnomirror-1d38b45257ed68a15f53561db578c4c1-177e4.jpg&quot;
        srcset=&quot;/static/testrunnomirror-1d38b45257ed68a15f53561db578c4c1-6c2d5.jpg 175w,
/static/testrunnomirror-1d38b45257ed68a15f53561db578c4c1-3d17c.jpg 350w,
/static/testrunnomirror-1d38b45257ed68a15f53561db578c4c1-177e4.jpg 700w,
/static/testrunnomirror-1d38b45257ed68a15f53561db578c4c1-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;
&lt;h3&gt;All Done&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/working.jpeg&quot; alt=&quot;It is working&quot;&gt;&lt;/p&gt;
&lt;p&gt;
  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; margin: 15px -30px !important max-width: 700px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 133.29999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAQL/xAAWAQEBAQAAAAAAAAAAAAAAAAABAgD/2gAMAwEAAhADEAAAAVtl6NhODSxy4vQB/8QAGxAAAgIDAQAAAAAAAAAAAAAAAAECEQMSE0H/2gAIAQEAAQUC5qTSqO0jpSxOluKOMXFEHHWke0j/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwFUV//EABcRAAMBAAAAAAAAAAAAAAAAAAAQESH/2gAIAQIBAT8BmEKv/8QAHBAAAgICAwAAAAAAAAAAAAAAAAERMQIQISIy/9oACAEBAAY/AsriTq+CxpMctHmSMitVqj//xAAdEAEBAQEAAQUAAAAAAAAAAAABEQAhMRBBUXGR/9oACAEBAAE/IemYcfmIDI714QoUJfrOSb2LhNb66l3WviTFeLHI3nw6RAeinkb/2gAMAwEAAgADAAAAEG8SPv/EABcRAAMBAAAAAAAAAAAAAAAAAAABERD/2gAIAQMBAT8QrxBSP//EABkRAQACAwAAAAAAAAAAAAAAAAEAERAhUf/aAAgBAgEBPxADtjbson//xAAgEAEAAgICAQUAAAAAAAAAAAABABEhMUFRYYGRobHx/9oACAEBAAE/EKhURV8GKMppET6jNiNdR5NcvmkRuFIDHtFQUrRL6HVQy3ZAvNVH1S+X9g2XwvZ8zHAVwQ0PMCChraT/2Q==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;alive&quot;
        title=&quot;&quot;
        src=&quot;/static/mirrorworking-cb64dafa2f57f470f02f4200d794972e-177e4.jpg&quot;
        srcset=&quot;/static/mirrorworking-cb64dafa2f57f470f02f4200d794972e-6c2d5.jpg 175w,
/static/mirrorworking-cb64dafa2f57f470f02f4200d794972e-3d17c.jpg 350w,
/static/mirrorworking-cb64dafa2f57f470f02f4200d794972e-177e4.jpg 700w,
/static/mirrorworking-cb64dafa2f57f470f02f4200d794972e-3a454.jpg 1000w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  &lt;/p&gt;</content:encoded></item><item><title><![CDATA[Making a middleware API layer in GO]]></title><description><![CDATA[How to teach yourself a new language and the advantages of a middleware API (TurtleWare 2.0 pt1)
]]></description><link>https://github.com/seperot/sepe-devblog/middlewear-go/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/middlewear-go/</guid><pubDate>Sat, 21 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;How to teach yourself a new language and the advantages of a middleware API (TurtleWare 2.0 pt1)&lt;/p&gt;
&lt;!-- end --&gt;
&lt;h3&gt;History time&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/trtlwear.jpeg&quot; alt=&quot;Watch&quot;&gt;&lt;/p&gt;
&lt;p&gt;A couple of years ago, I knocked together a little watch app for the guys at &lt;a href=&quot;https://turtlecoin.lol&quot; target=&quot;_blank&quot;&gt;Turtlecoin&lt;/a&gt;. I wanted to understand both Wear OS and Kotlin better, so a simple watch face that pulls data from a few exchanges for a price seemed easy enough.&lt;/p&gt;
&lt;p&gt;An issue I had at the time was the watch face needed to be able to deal with all the API calls, parsing, sorting the value out, reacting to user settings and displaying. The version of Wear OS at the time didn’t even let you make API calls, which ended up forcing me down a companion app route on Android. This move, unfortunately, made all iOS or Wear only users unable to use the watch face. This was something that always bothered me, but I was working on other projects so I didn’t have time to really come back to it. &lt;/p&gt;
&lt;p&gt;Move ahead to the present, with WearOS now allowing Http requests, most of the exchanges I used for grabbing the price of the coin had vanished, and Google had started depreciating their weather API, the watch face needed a lot of work to be usable.&lt;/p&gt;
&lt;h3&gt;Middleware&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/mmim.jpeg&quot; alt=&quot;MiddleMan&quot;&gt;&lt;/p&gt;
&lt;p&gt;History lesson aside, why build a middleware API?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No need to redeploy the app&lt;/strong&gt; - In the time my app was out, almost every API it accessed was either gone or under threat of deprecation. This requires me to make a new version of the app, release it as an update to the app store, and the whole userbase to update or else their watch face will continue to not work. With a middleware, all I would need to do is update the API it with a new source, deploy, and all the watch faces instantly get back in action.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Improve readability of app&lt;/strong&gt; - A good 50% of the watch face app was JSON parsing and working out the price. When you move all that parsing and logic to the middleware, all the watch face needs to do is receive the API info and display it. With all that code removed, what’s left is far easier to read.&lt;/p&gt;
&lt;h3&gt;Get Go(lang)ing&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/gopher.jpeg&quot; alt=&quot;Gopher&quot;&gt;&lt;/p&gt;
&lt;p&gt;The best way I find for learning new languages is just trying things out based on how I think it will work, getting it wrong, looking up how to make it work, and finally fixing it. It’s a great feeling as you go through your project and you notice you are getting more and more right first time.&lt;/p&gt;
&lt;p&gt;So for example when I first built this handler for listening and serving Http. I had set it up like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;serveCoinValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseWriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Usd&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;$&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; priceCalculator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PriceCalc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;USD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
        priceCalculator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ExchangeOgre&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
		coinval &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; coinValue&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;$&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; priceCalculator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PriceCalc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;USD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
		priceCalculator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ExchangeOgre&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        Btc&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;₿&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; priceCalculator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PriceCalc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;BTC&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
        priceCalculator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ExchangeOgre&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;₿&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; 
        priceCalculator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PriceCalc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;BTC&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; priceCalculator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ExchangeOgre&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	js&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;coinval&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusInternalServerError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bk&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;	
	w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusOK&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;js&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It worked, as in it passed JSON over, but there were some issues. Firstly, if I want a handler for another call like getting the weather, I will have to make a second handler. Secondly, this call reacts to any Http request with this response, no thought about malformed requests or proper error handling. So with some googling and refactored my second pass looked like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;HandlerFunc &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseWriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
	r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			js &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;byte&lt;/span&gt;
			err &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;URL&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/coin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
			js&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TradeOgre&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
			price&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BtcFiatPrice&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
			getjson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Map&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/weather&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
			lat &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Header&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;lat&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			lon &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Header&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;lon&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
				&lt;span class=&quot;token string&quot;&gt;&quot;Missing Lat Lon Headers&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
				http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusInternalServerError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				js&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;weather&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Getter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lat&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lon&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
				weather&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OpenWeather&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; getjson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Map&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
			w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusNotFound&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`{&quot;error&quot;: &quot;nothing found&quot;}`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Method &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
			w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusOK&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;js&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
			w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusNotFound&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`{&quot;error&quot;: &quot;nothing found&quot;}`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusInternalServerError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This allows to have a single error handler that handles different calls and gives the correct response and is easy to call or mock out for tests.&lt;/p&gt;
&lt;p&gt;Another important thing when learning a language is to avoid 3rd party libraries where possible. While Gorilla Mux makes complicated handlers and routers easier and setting everything to work as a AWS lambda function is really cool, you won’t really understand the problem they fix if you use them from the very beginning.&lt;/p&gt;
&lt;h3&gt;Test all the things&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/testing.jpeg&quot; alt=&quot;Testing&quot;&gt;&lt;/p&gt;
&lt;p&gt;Writing and covering your project in tests is another great way to refine your knowledge and improve your code. While TDD/BDD is great for languages you are comfortable in, when learning a new language, building your unit tests after you’ve built your project works better. Writing tests for something you can’t even conceptualize is really hard and could triple the time before you get that first “it works!” moment. If you write the tests after you have that moment then you can refactor that code down to work with the tests and learn mistakes you made earlier on. As an added bonus, I think doing it this way when learning really gives you an appreciation of how unit tests can help you write better code and make sure it’s working before it goes to production.&lt;/p&gt;
&lt;p&gt;Writing tests in go is also super simple, by passing functions in your code you can create mock versions easily in your tests, like below:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TestOpenWeather&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;testing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; result2 &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;OpenWeather&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;12&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;12&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; testJsonRetriever&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; result2 &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;47&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;OH NO&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;testJsonRetriever&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fullUrl &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
client &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Client&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	stubbedResponse &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`{&quot;weather&quot;: [{&quot;main&quot;: &quot;test&quot;}, 
	{&quot;main&quot;: &quot;fail&quot;}],
	&quot;main&quot;: {&quot;temp&quot;: 47}}`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; result &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	jsonErr &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Unmarshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stubbedResponse&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; jsonErr &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jsonErr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Finishing up&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/flowmap.jpeg&quot; alt=&quot;Middleware flow&quot;&gt;&lt;/p&gt;
&lt;p&gt;The end result for me was a nice little 350ish line Golang project. Which &lt;a href=&quot;https://github.com/seperot/turtle-wear-api&quot;&gt;you can check out the code for here&lt;/a&gt; or if your interested in seeing it working &lt;a href=&quot;https://app.swaggerhub.com/apis-docs/ijhdev/turtle-wear-api/1.0.0&quot;&gt;you can use the swagger doc here&lt;/a&gt;. I would love to get feedback on ways to improve what’s here or why I’m wrong.&lt;/p&gt;
&lt;p&gt;This is part 1 of a 3 part blog talking about making version 2 of the watch app. In part 2, I will be going into the WearOS and Kotlin work of the watch app. Part 3 will be about containerizing, automating and deploying everything.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Making a simple crypto trade bot]]></title><description><![CDATA[A guide on making a simple bot that can trade while you focus on the hard stuff
]]></description><link>https://github.com/seperot/sepe-devblog/simple-trade-bot/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/simple-trade-bot/</guid><pubDate>Sun, 26 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A guide on making a simple bot that can trade while you focus on the hard stuff&lt;/p&gt;
&lt;!-- end --&gt;
&lt;h3&gt;The Bot&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/bot.jpeg&quot; alt=&quot;Bot&quot;&gt;&lt;/p&gt;
&lt;p&gt;The bot we are going to be setting up today is called &lt;a href=&quot;https://github.com/freqtrade/freqtrade&quot; target=&quot;_blank&quot;&gt;Freqtrade&lt;/a&gt;.  It’s a very easy to set up bot that can be controlled via &lt;a href=&quot;https://telegram.org/&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;
. As we are not going to be using the codebase on our machine, the only file you need from their GitHub is &lt;a href=&quot;https://github.com/freqtrade/freqtrade/blob/develop/config.json.example&quot; target=&quot;_blank&quot;&gt;this json file&lt;/a&gt;.  If you copy the lines of this file and paste them into a text editor (I recommend &lt;a href=&quot;https://www.sublimetext.com/&quot; target=&quot;_blank&quot;&gt;Sublime text&lt;/a&gt;)  and save the file as config.json.&lt;/p&gt;
&lt;p&gt;Now lets have a look at the config file, the first important part is these two lines&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“max_open_trades”: 3, &lt;/li&gt;
&lt;li&gt;“stake_amount”: 0.05,&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These lines are where you setup how much the bot is going to be using to make it’s trades, so in this example it’s making 3 trades of 0.05 BTC so in total 0.15 BTC needs to be in the account for it to trade effectively. You can change both numbers up or down as much as you like, just make sure you have at least the amount of funds inside for it to make the trades. Remember the more you put in the bigger the reward but the bigger the risk.&lt;/p&gt;
&lt;p&gt;The next section is about setup strategy. The  best thing to do here is leave it alone for your first couple of weeks, the bot learns as it trades (real transactions only) so let it go for a bit before changing something. If you do want to tweak rules later &lt;a href=&quot;https://freqtrade.io/en/latest/&quot; target=&quot;_blank&quot;&gt;Freqtrades documentation is here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;The Exchange&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/exchange.jpeg&quot; alt=&quot;Exchange&quot;&gt;&lt;/p&gt;
&lt;p&gt;The next important bit is the exchange, for this we are going to be using &lt;a href=&quot;https://www.binance.com/?ref=27977821&quot; target=&quot;_blank&quot;&gt;Binance&lt;/a&gt; so sign up or in here &lt;a href=&quot;https://www.binance.com/?ref=27977821&quot; target=&quot;_blank&quot;&gt;https://www.binance.com/?ref=27977821&lt;/a&gt; to continue. Once signed in, go to your account and press the API settings button. Name your new API ‘my trading bot’ or something to that effect so you know what the keys relate to. &lt;a href=&quot;https://www.binance.com/?ref=27977821&quot; target=&quot;_blank&quot;&gt;Binance&lt;/a&gt; will then take you through a series of steps on setting this up, the important thing to remember is that both &lt;strong&gt;Read Info&lt;/strong&gt; and &lt;strong&gt;Enable Trading&lt;/strong&gt; are checked and &lt;strong&gt;Enable Withdrawals&lt;/strong&gt; is unchecked. Once it give you the keys, copy them both and keep them somewhere safe! You will never see the secret key again and will have to redo this part of the process again if you lose it. Now go back to your config.json file and change the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;name needs changing from “bittrex” to “binance”&lt;/li&gt;
&lt;li&gt;replace “your_exchange_key” with your API Key from Binance&lt;/li&gt;
&lt;li&gt;replace “your_exchange_secret” with your Secret Key from Binance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before we leave this section, now is a good time to put your BTC your trading with into &lt;a href=&quot;https://www.binance.com/?ref=27977821&quot; target=&quot;_blank&quot;&gt;Binance&lt;/a&gt; we talked about further up the guide, it is recommended to put a little more than the planned amount in to allow for the bot to make a couple early mistakes while it learns and improves. &lt;/p&gt;
&lt;p&gt;The next section is the pair_whitelist and pair_blacklist
The whitelist is all the currency’s you want the bot to be watching, I would recommend going for 10 to begin with. As for the blacklist, we only need to have BNB/BTC in there as the bot gets confused with &lt;a href=&quot;https://www.binance.com/?ref=27977821&quot; target=&quot;_blank&quot;&gt;Binance&lt;/a&gt; using BNB to discount fee’s&lt;/p&gt;
&lt;h3&gt;The Communication&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/communication.jpeg&quot; alt=&quot;Communication&quot;&gt;&lt;/p&gt;
&lt;p&gt;The next important section of the config file for now is the &lt;a href=&quot;https://telegram.org/&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt; section. This will allow you to get updates from your bot and send it commands like stop/start etc. Firstly on &lt;a href=&quot;https://telegram.org/&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;, message &lt;strong&gt;@BotFather&lt;/strong&gt; with /start followed by /newbot and follow it’s instructions to get your new bot. Copy the HTTP API token it gives you and paste it to replace “your_telegram_token”. Next message &lt;strong&gt;@get_id_bot&lt;/strong&gt; with /start and it will give you your chat ID copy it and paste it to replace “your_telegram_chat_id”. This means that your bot will only reply to your ID and ignore any other.&lt;/p&gt;
&lt;h3&gt;The Server&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/server.jpeg&quot; alt=&quot;Server&quot;&gt;&lt;/p&gt;
&lt;p&gt;With that all done, the final step is to set this all up, &lt;a href=&quot;https://www.vultr.com/?ref=8109640-4F&quot; target=&quot;_blank&quot;&gt;Vultr&lt;/a&gt; is a server hosting which this referral code &lt;a href=&quot;https://www.vultr.com/?ref=8109640-4F&quot; target=&quot;_blank&quot;&gt;https://www.vultr.com/?ref=8109640-4F&lt;/a&gt; it gives you $50 free credit, the server we well be setting up is only $5 a month so that’s 10 months of free trading! Once you sign up and follow the instructions &lt;a href=&quot;https://www.vultr.com/?ref=8109640-4F&quot; target=&quot;_blank&quot;&gt;Vultr&lt;/a&gt; gives, it’s time to set up the server. Press Deploy new server and select &lt;a href=&quot;https://www.vultr.com/?ref=8109640-4F&quot; target=&quot;_blank&quot;&gt;Vultr&lt;/a&gt; Cloud Computing, choose the region you would prefer, select Ubuntu as the OS and 16.04 x64 as the version, selected the $5 25GB SSD as your server size. Once that’s all selected click deploy now and wait for &lt;a href=&quot;https://www.vultr.com/?ref=8109640-4F&quot; target=&quot;_blank&quot;&gt;Vultr&lt;/a&gt; so set it all up for you, this will take a few mins. Once it’s finished click on the server name and click on the view console icon at the top left and log in. There is going to be a series of commands here to get things setup so just type them in one at a time.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; update
&lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; docker-ce
systemctl &lt;span class=&quot;token function&quot;&gt;enable&lt;/span&gt; docker
docker pull freqtradeorg/freqtrade:develop
docker tag freqtradeorg/freqtrade:develop freqtrade
docker build -t freqtrade &lt;span class=&quot;token keyword&quot;&gt;.&lt;/span&gt;
docker build -f ./Dockerfile.develop -t freqtrade-dev &lt;span class=&quot;token keyword&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; ~/.freqtrade
&lt;span class=&quot;token function&quot;&gt;cd&lt;/span&gt; ~/.freqtrade
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; user_data&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once that step is done we need to transfer the config file to the new folder. There is a few different ways to do this, the simplest for most users is to ssh move the file. To do this, open up a command line terminal, navigate to the folder you saved the config.json file and get the ip address of your Vultr server ready and enter the flooring&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;scp&lt;/span&gt; config.json root@&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;enter\_your\_ip\_here&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:/root/.freqtrade
&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; tradesv3.sqlite
&lt;span class=&quot;token function&quot;&gt;scp&lt;/span&gt; tradesv3.sqlite root@&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;enter\_your\_ip\_here&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:/root/.freqtrade&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;The Big Finale&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./images/Finale.jpeg&quot; alt=&quot;Finale&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally go back to your Vultr terminal, it’s time for everything to come together. Run the following command&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;docker run -d --name freqtrade -v /etc/localtime:/etc/localtime:ro 
-v ~/.freqtrade/config.json:/freqtrade/config.json 
-v ~/.freqtrade/user_data/:/freqtrade/user_data 
-v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite 
freqtrade --db-url sqlite:///tradesv3.sqlite&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If everything was done right, you should get a message on Telegram that your bot has started. Now it will start scanning the selected currencies for movements and buy and sell as appropriate.&lt;/p&gt;
&lt;p&gt;Congrats on your new bot!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[CI/CD for Mobile Development]]></title><description><![CDATA[A guide on minimizing faff and getting a scalable team moving faster
]]></description><link>https://github.com/seperot/sepe-devblog/mobile-ci-cd/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/mobile-ci-cd/</guid><pubDate>Sat, 13 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A guide on minimizing faff and getting a scalable team moving faster&lt;/p&gt;
&lt;!-- end --&gt;
&lt;h3&gt;Caveats and Preamble&lt;/h3&gt;
&lt;p&gt;Before you start on this path, a few assumptions have been made, mostly for the sake of not explaining everything. It is assumed you are comfortable with Git, Unit and UI testing, and have admin rights to any systems to want to integrating into. Also don’t take this as by the letter solution, use it as a way to try things out and explore options not mentioned.&lt;/p&gt;
&lt;h2&gt;What is CI/CD?&lt;/h2&gt;
&lt;p&gt;CI/CD stands for continuous integration / continuous delivery. The CI part is the goal of having code automatically built into an application and ran against automated tests to allow engineers to commit code more frequently and react faster to any issues. The CD part is built applications being available to different environments, exploratory testing and eventually deployment to the app stores. Both parts work together when you work with a pipeline, have good code coverage and a easy to follow process for everyone that isn’t time consuming.&lt;/p&gt;
&lt;p&gt;CI/CD works best when you have a team of engineers working on a product together, but don’t need to be living in each others pockets knowing what they are working on. The &lt;a href=&quot;https://medium.com/productmanagement101/spotify-squad-framework-part-i-8f74bcfcd761&quot; target=&quot;_blank&quot;&gt;squad&lt;/a&gt; model is a good example of what I mean here. It also works well for a single developer trying to save precious time normally wasted on repetitive tasks outside of improving their app. Utilizing small, fast, incremental changes will mean no one is getting stuck behind a massive pull request or finding out their code is conflicting with someone elses 2 weeks later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/pipeline.jpeg&quot; alt=&quot;Pipelines&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Building a pipeline with Bitrise&lt;/h2&gt;
&lt;p&gt;The step towards a CI/CD workflow is getting a automated pipeline. This will listen out for certain triggers and fire a series of automated tasks out from it. There are a number of good pipeline CI/CD tools out there today. &lt;a href=&quot;https://bitbucket.org/product/features/pipelines&quot; target=&quot;_blank&quot;&gt;Bitbucket&lt;/a&gt; and &lt;a href=&quot;https://azure.microsoft.com/en-gb/services/devops/pipelines/&quot; target=&quot;_blank&quot;&gt;Azure devops&lt;/a&gt; have built in solutions, &lt;a href=&quot;https://jenkins.io/&quot; target=&quot;_blank&quot;&gt;Jenkins&lt;/a&gt;, &lt;a href=&quot;https://www.jetbrains.com/teamcity/&quot; target=&quot;_blank&quot;&gt;Team City&lt;/a&gt; and &lt;a href=&quot;https://circleci.com/&quot; target=&quot;_blank&quot;&gt;Circle CI&lt;/a&gt; are great stand alone options with good integrations with most languages and systems. As this is focusing on mobile development, We will be using &lt;a href=&quot;https://www.bitrise.io&quot; target=&quot;_blank&quot;&gt;Bitrise.&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Bitrise offers a Hobby tier which is free to use. Their limits on build times in this band are a quite lean but work if you are investigating options or just starting out a project and don’t have a lot of code down yet, other than that you have the exact same tools available to you as a larger company using the service. Once signed up you can hook your git repository of choice in with it’s step by step starting guide, at the end of that it will spit you out an app dashboard with a test build running and you on the builds tab. &lt;/p&gt;
&lt;p&gt;From here, click on the settings tab first, review all the options in here including that your build number, default branch, app name &amp;#x26; SSH is all correct. I would also recommend turning on ‘enable rolling builds’ here, it will help if your firing a lot of builds off quickly so you don’t end up with a giant queue of builds that don’t need to be built anymore. Next, check over the code tab and configure a web hook to your git repository, this is important as this the link bitrise needs to listen for branch changes. Once that’s sorted head into the workflow tab.&lt;/p&gt;
&lt;p&gt;Here you will have several tabs based on your app type, the important ones to cover here are Workflows, Env Vars, and Triggers the rest are situational and are mostly not needed right now. Starting with Workflows, this is the main area of Bitrise and where you will be spending most of your time when building and improving your pipeline. Using the mindset of having reusable components, I found the best way to structure a Bitrise workflow is the following:&lt;/p&gt;
&lt;h4&gt;Step&lt;/h4&gt;
&lt;p&gt;This is the inner part of every workflow, some ones pre setup that are useful are Activate SSH key, Git Clone Repository and Bitrise.io Cache:Pull. If you click the + symbols in between the steps you can grab from a library of pre-made steps that you just need to configure and use.&lt;/p&gt;
&lt;h4&gt;Workflow step&lt;/h4&gt;
&lt;p&gt;This is your a workflow that does a single purpose. So let’s say you wanted to have a step that runs your Unit tests, you would make a workflow called STEP-UNIT-TEST, and inside you would grab all the steps you need to complete that task and configure them.&lt;/p&gt;
&lt;h4&gt;Workflow build&lt;/h4&gt;
&lt;p&gt;This is what you call with your triggers. For example, if you wanted to make a build that runs when a PR is triggered, you would make a workflow called BUILD-PR and use the ‘add workflows before/after’ to add Workflow steps for all reusable parts you’ve made and add only a couple of steps for the specific requirements of the PR&lt;/p&gt;
&lt;p&gt;For this, lets just build a basic workflow build that has two workflow steps. First workflow step we will make is STEP-PRE-BUILD, this should have the Activate SSH key, Git Clone and Pull cache regardless of platform. For iOS add your certificate and profile installer to the end, for Android add install missing Android SDK components and a gradle runner step where in the config you just have clean in the tasks to run. &lt;strong&gt;Note:&lt;/strong&gt; We’re not going into specific config settings here, but make sure you check all the steps have the right paths or names on them as we go. Next make a workflow step called STEP-POST-BUILD, with this we can add Deploy to Bitrise.io, and Bitrise.io Cache Push.&lt;/p&gt;
&lt;p&gt;With the pieces ready, let’s build a final workflow called BUILD-DEVELOP. In there, click on Add workflow before and select our STEP-PRE-BUILD, next click Add workflow after and select the STEP-POST-BUILD. With that done all you need to do is add steps to the BUILD-DEVELOP area, in Android this would be another gradle runner with the task “assemble” and in iOS you would most likely be using fastlane, so add that step in there. With all that configured, go back up a level by clicking on the project name at the top of the page, select the button Stat/Schedule a Build, scroll down to the Workflow dropdown and select your BUILD-DEVELOP step and start a build. If you have configured it correctly, it should create a build ready for you to use.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/clouds.jpeg&quot; alt=&quot;StaticAnalysis&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Static code analysis&lt;/h2&gt;
&lt;p&gt;One important thing to add to your pipeline is static code analysis. This doesn’t replace the need for other engineers reviewing code, rather it acts as an extra reviewer who is obsessed with code rules. There are a few different tools for the job, &lt;a href=&quot;https://www.sonarqube.org/&quot; target=&quot;_blank&quot;&gt;SonarQube&lt;/a&gt; is a good option if you are looking to host something locally and &lt;a href=&quot;https://sonarcloud.io/about&quot; target=&quot;_blank&quot;&gt;SonarCloud&lt;/a&gt; is very handy if you don’t want to host. Other options like &lt;a href=&quot;https://www.codacy.com/&quot; target=&quot;_blank&quot;&gt;Codacy&lt;/a&gt; and &lt;a href=&quot;https://www.cqse.eu/en/products/teamscale/landing/&quot; target=&quot;_blank&quot;&gt;Teamscale&lt;/a&gt; offer a similar service, so find the one that supports your codebase best. Connecting it to your git repo and your pipeline, you can set the code analyser to fire every time you trigger a build and will comment on the pull request. They cover a range of rules from code duplication, vulnerabilities including &lt;a href=&quot;https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project&quot; target=&quot;_blank&quot;&gt;OWASP Top 10&lt;/a&gt;, bugs, and Unit test coverage.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/automation.jpeg&quot; alt=&quot;Automation&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Unit &amp;#x26; UI automated tests&lt;/h2&gt;
&lt;p&gt;I’m not going to go into how to write unit or UI tests here, that’s for a different post. This is more a short bit on what tests your pipeline should be looking for and what you should be doing. Unit tests should be on everything that can be tested in isolation, so don’t try and unit test a button click that uses platform logic, but do test that code that arranges strings alphabetically. Unit tests are generally cheap to run so regardless of the pipeline (Dev, Release, Test) it’s a good idea to have them run. UI tests on the other hand are far more brittle as any UI change on the app can break them as well as being very resource heavy. As such you should stick to having only a couple of them that run a happy path scenario and only run on a code merge build.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/breakingbarrier.jpeg&quot; alt=&quot;Breaking Barrier&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Getting past a git flow mentality&lt;/h2&gt;
&lt;p&gt;One thing that is important with when moving to a CI/CD way of working is handling git correctly. Most people are aware of git flow, and much like that “truly agile” company you always get told about, they only do it half right. However, to get the best out of CI/CD it’s better to throw git flow out completely and adopt &lt;a href=&quot;https://trunkbaseddevelopment.com/&quot; target=&quot;_blank&quot;&gt;trunk based development.&lt;/a&gt; Trunk based development is essentially a Master or Trunk branch that engineers will create short lived branches which contain a small amounts of work. Once the work has been done and tested by the developer, it’s put in a PR where the pipeline will run all your tests and static code checks along with a code review from others. This will give some certainty that what is going into Trunk is safe. Then to release a build live, you tag the Trunk at that point with the version and fire up a release build. You can make a release branch at this point, but I would suggest keeping it to tagging unless issues arise and Trunk has moved on already. &lt;/p&gt;
&lt;p&gt;This, is almost guaranteed to have someone worried:&lt;/p&gt;
&lt;h4&gt;But what if someone pushes broken code up?&lt;/h4&gt;
&lt;p&gt;Well, if this is something you think your engineers will be doing often, you need to evaluate your hiring policies and interview criteria. &lt;/p&gt;
&lt;p&gt;Seriously though, making pull requests mandatory on your Trunk branch is an easy way to help this process. Later on you will be hooking in a pipeline that will automatically build and run tests on the code that can pass or fail the PR on it’s own as well as static code analysis to really lock down any issue of a mistaken crash getting in.&lt;/p&gt;
&lt;h4&gt;How will I know what’s going into the codebase?&lt;/h4&gt;
&lt;p&gt;Another reason for Pull Requests but with the added need for concise commit messages and squashed commits. This allows a nice clean timeline on your trunk branch for any dev to read.&lt;/p&gt;
&lt;h4&gt;But how will we know when it’s time to release?&lt;/h4&gt;
&lt;p&gt;Always be releasing, if you have a Trunk with code constantly going into it and there isn’t stuff to release then you need to review the size of your tasks.&lt;/p&gt;
&lt;h4&gt;Won’t we get held up if we have to wait for code to be released with only one branch?&lt;/h4&gt;
&lt;p&gt;That’s the good part of the tagging system, you can focus the build on that release. If the build needs a fix, make a branch of that tagged version, once that branch has been released merge the release branch back to Trunk and continue on as normal.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/projectmanager.jpeg&quot; alt=&quot;Project Manager&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Integrating with project management tools&lt;/h2&gt;
&lt;p&gt;A common disruption in Engineering is asking for progress updates, the usual issue is you have a varying level of interaction from dev to dev about how much they want to deal with this. One of the nicer things about automating a pipeline is you can automate some of this process. Jira ticket updating with status and build numbers, Slack messages and emails can all be sent out from the various build types to keep everyone who wants to know what the status of that important ticket in the loop.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/flow.jpeg&quot; alt=&quot;CI/CD Flow&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Hopefully this has given you a good overview on setting up a basic pipeline and what you can do to make improve your process. There is a lot of tweaking and improving you can do from here. Automating version and build numbers, outputting unit test results to a webpage, even something fun like triggering a sound to play in the office every time a build goes live. Over time you should find you have to release more frequently as work ready for release it appearing faster and faster. &lt;/p&gt;</content:encoded></item><item><title><![CDATA[Sprint Planning Buckaroo]]></title><description><![CDATA[So, this sounds insane. But hear me out, it will make sense shortly
]]></description><link>https://github.com/seperot/sepe-devblog/sprint-buckaroo/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/sprint-buckaroo/</guid><pubDate>Sat, 02 Mar 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So, this sounds insane. But hear me out, it will make sense shortly&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;I, like many developers theses days work in a pseudo agile way. We write tickets, stick them on a board of some kind, then smash all the tickets into the QA ready column until the two weeks have passed, then release it. Couple of problems with this, one being the estimation system for what “enough work” is and the other being the fighting balance of getting the work product want, whilst also sorting tech debt.&lt;/p&gt;
&lt;h2&gt;It’s just a 5 min job right?&lt;/h2&gt;
&lt;p&gt;Time estimation is something almost all developers have to deal with, trying to convey to people who don’t exactly understand what your doing how long something they want will take. The simplest example of this is just estimating in hours or days. Other systems like sprint poker and points systems give a better idea of effort, while not giving the dev team a deadline, but they need your product team to understand what a single point is worth to really be relevant.&lt;/p&gt;
&lt;h2&gt;Don’t change that code now, it looks fine and we have all this to do!&lt;/h2&gt;
&lt;p&gt;The business must always be making new things, especially in competitive markets. But if the code has problems and is ignored by the company it will have to eventually face the dreaded re-write and we all know a horror story or two about that! Balancing this is hard, with normally one side winning out completely from the other, not exactly collaborative.&lt;/p&gt;
&lt;h2&gt;So… you said something about Buckaroo?&lt;/h2&gt;
&lt;p&gt;Yes, Sprint Planning Buckaroo.&lt;/p&gt;
&lt;div&gt;
          &lt;div
            class=&quot;gatsby-resp-iframe-wrapper&quot;
            style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden;margin-bottom: 1.0725rem&quot;
          &gt;
            &lt;iframe src=&quot;https://www.youtube.com/embed/UIBBnNk216k&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen style=&quot;
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
          &quot;&gt;&lt;/iframe&gt;
          &lt;/div&gt;
          &lt;/div&gt;
&lt;p&gt;Now you may have put most of the dots together at this point. Assign tickets to items in the game, throw them on the Bucking Donkey (Horse?) until it kicks, once he kicks that’s a full sprint. But let’s go into slightly more detail.&lt;/p&gt;
&lt;p&gt;Firstly the important bit, all the bits of buckaroo weight different amounts. So by using that, you can pick your big wins to be the hat or the chest and your easy fixes to be the holster or the rope. There’s a list near the bottom of the weights of each item. Sometimes however it’s better to not share the exact weight out and let people add put their own notions of weight, whatever works best for your team.&lt;/p&gt;
&lt;p&gt;Next you need your tickets. You want to make sure you have tasks broken down to their smallest parts that are safe to release to the wild. This should be pretty standard practice for most development teams but for anyone wanting to try who doesn’t normally sprint plan, this may help.&lt;/p&gt;
&lt;h2&gt;How to run a session&lt;/h2&gt;
&lt;p&gt;So now you have all your pieces, your work and more than likely your debt ready to go, how does it work? Well first you split the items by their colour, Red will be product, Blue will be debt, if you have a version with more colours then split the rest equally between the two main colours. If you have multiple disciplines of engineering (API, Web, Mobile, etc) make sure they all do their own Buckaroo’s worth of work. You don’t want three Web tasks, one API task and Mobile doing nothing all week.&lt;/p&gt;
&lt;h4&gt;Step one&lt;/h4&gt;
&lt;p&gt;Pick a ticket, go for high priority ones first and smaller ones near the tipping point. Red always goes first with the saddle so this can be the core bit or work that needs to be done, careful to make sure it’s a need not a want.&lt;/p&gt;
&lt;h4&gt;Step two&lt;/h4&gt;
&lt;p&gt;Agree upon an item to represent the ticket, the bigger task can be a hat while that smaller one shouldn’t be more than the rope for example.&lt;/p&gt;
&lt;h4&gt;Step three&lt;/h4&gt;
&lt;p&gt;Put that item on the buckaroo, carefully. There is a mulligan option if someone has an issue like sneezing or slipping while placing.&lt;/p&gt;
&lt;h4&gt;Step four&lt;/h4&gt;
&lt;p&gt;Repeat steps one - three again until it bucks, and there you have a sprint ready to go!&lt;/p&gt;
&lt;h2&gt;Cool, but why is this such a great idea then?&lt;/h2&gt;
&lt;p&gt;On the simplest level it makes it easy for everyone to understand workloads. Having something physical that will literally kick out when it’s had too much is much easier to understand than a ticket having an effort level of 5. It also makes it a system where no one is holding back work or adding too much, everyone will blame the Donkey (Horse?) for the workload being the workload. Finally, it’s fun, you literally get to play high stakes Buckaroo every time you need to plan a sprint.&lt;/p&gt;
&lt;p&gt;So buy a copy of Buckaroo and have a go, leave me comments below on how it went over with your squad and any alterations you made, such as adding blu tak to certain items if you end up with a mega piece of work!&lt;/p&gt;
&lt;h2&gt;The Weights&lt;/h2&gt;
&lt;h3&gt;Reds:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Hat - 11g&lt;/li&gt;
&lt;li&gt;Blanket - 9g&lt;/li&gt;
&lt;li&gt;Saddle- 7g&lt;/li&gt;
&lt;li&gt;Dynamite- 5g&lt;/li&gt;
&lt;li&gt;Shovel- 4g&lt;/li&gt;
&lt;li&gt;Holster- 2g&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Blues:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Chest- 11g&lt;/li&gt;
&lt;li&gt;Lamp- 7g&lt;/li&gt;
&lt;li&gt;Guitar- 6g&lt;/li&gt;
&lt;li&gt;Watch- 5g&lt;/li&gt;
&lt;li&gt;Pan- 3g&lt;/li&gt;
&lt;li&gt;Rope- 3g&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note: I originally wrote this article on Medium in 2018, you can check it out (and give it a clap) &lt;a href=&quot;https://medium.com/@ian.hayward_26242/sprint-planning-buckaroo-b1bb192177ae&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Welcome to the Blog!]]></title><description><![CDATA[^An image of me making this blog
]]></description><link>https://github.com/seperot/sepe-devblog/welcome-to-the-blog/</link><guid isPermaLink="false">https://github.com/seperot/sepe-devblog/welcome-to-the-blog/</guid><pubDate>Fri, 15 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;^An image of me making this blog&lt;/p&gt;
&lt;!-- end --&gt;
&lt;p&gt;Seriously, thank you for taking the time to check out this blog. If your looking at this page then you are either someone I’ve forced into checking out the site, a robot, or someone looking for easter eggs.&lt;/p&gt;
&lt;p&gt;All you will find here is me thanking you for checking out my site. If you are looking to help me out at all, click the links in the bar that I reference in my description blurb.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/partyp-db57bf970375d87479945b009041874c.gif&quot; alt=&quot;Party&quot;&gt;&lt;/p&gt;</content:encoded></item></channel></rss>